@@ -27,11 +27,15 @@ import { Logger } from '../logger';
27
27
import { OAuthDancer } from './oauthDancer' ;
28
28
import { SiteManager } from '../siteManager' ;
29
29
import { Container } from '../container' ;
30
+ import { strategyForProvider } from './strategy' ;
31
+ import { BitbucketResponseHandler } from './responseHandlers/BitbucketResponseHandler' ;
32
+ import * as cp from 'child_process' ;
30
33
31
34
export class LoginManager {
32
35
private _dancer : OAuthDancer = OAuthDancer . Instance ;
33
36
private _jiraAuthenticator : JiraAuthenticator ;
34
37
private _bitbucketAuthenticator : BitbucketAuthenticator ;
38
+ private _bitbucketResponseHandler : BitbucketResponseHandler ;
35
39
36
40
constructor (
37
41
private _credentialManager : CredentialManager ,
@@ -40,6 +44,13 @@ export class LoginManager {
40
44
) {
41
45
this . _bitbucketAuthenticator = new BitbucketAuthenticator ( ) ;
42
46
this . _jiraAuthenticator = new JiraAuthenticator ( ) ;
47
+ // Initialize BitbucketResponseHandler
48
+ const axiosInstance = this . _dancer . getAxiosInstance ( ) ;
49
+ this . _bitbucketResponseHandler = new BitbucketResponseHandler (
50
+ strategyForProvider ( OAuthProvider . BitbucketCloud ) ,
51
+ this . _analyticsClient ,
52
+ axiosInstance ,
53
+ ) ;
43
54
}
44
55
45
56
// this is *only* called when login buttons are clicked by the user
@@ -109,7 +120,120 @@ export class LoginManager {
109
120
}
110
121
}
111
122
112
- private async getOAuthSiteDetails (
123
+ /**
124
+ * Extracts auth token from git remote URL
125
+ * @returns The auth token or null if not found
126
+ */
127
+ private async getAuthTokenFromGitRemote ( ) : Promise < string | null > {
128
+ try {
129
+ Logger . debug ( 'Attempting to extract auth token from git remote' ) ;
130
+ // Get the workspace folder path
131
+ const workspaceFolders = vscode . workspace . workspaceFolders ;
132
+ if ( ! workspaceFolders || workspaceFolders . length === 0 ) {
133
+ Logger . warn ( 'No workspace folder found' ) ;
134
+ return null ;
135
+ }
136
+ const workspacePath = workspaceFolders [ 0 ] . uri . fsPath ;
137
+ // Execute git remote -v command
138
+ const gitCommand = 'git remote -v' ;
139
+ const result = await new Promise < string > ( ( resolve , reject ) => {
140
+ cp . exec ( gitCommand , { cwd : workspacePath } , ( error , stdout ) => {
141
+ if ( error ) {
142
+ reject ( error ) ;
143
+ return ;
144
+ }
145
+ resolve ( stdout ) ;
146
+ } ) ;
147
+ } ) ;
148
+ // Parse the output to find the token
149
+ const remoteLines = result . split ( '\n' ) ;
150
+ for ( const line of remoteLines ) {
151
+ // Look for https://x-token-auth:<token>@bitbucket.org pattern
152
+ const tokenMatch = line . match ( / h t t p s : \/ \/ x - t o k e n - a u t h : ( [ ^ @ ] + ) @ b i t b u c k e t \. o r g / ) ;
153
+ if ( tokenMatch && tokenMatch [ 1 ] ) {
154
+ Logger . debug ( 'Auth token found in git remote' ) ;
155
+ return tokenMatch [ 1 ] ;
156
+ }
157
+ }
158
+ Logger . warn ( 'No auth token found in git remote' ) ;
159
+ return null ;
160
+ } catch ( error ) {
161
+ Logger . error ( error , 'Error extracting auth token from git remote' ) ;
162
+ return null ;
163
+ }
164
+ }
165
+
166
+ private async refreshBitbucketToken ( siteDetails : DetailedSiteInfo ) : Promise < boolean > {
167
+ const token = await this . _credentialManager . getAuthInfo ( siteDetails , false ) ;
168
+ if ( token ) {
169
+ return this . authenticateWithBitbucketToken ( true ) ;
170
+ }
171
+ return false ;
172
+ }
173
+
174
+ // Add a new method for token-based authentication
175
+ public async authenticateWithBitbucketToken ( refresh : boolean = false ) : Promise < boolean > {
176
+ try {
177
+ // Get token from git remote instead of hardcoding it
178
+ const token = await this . getAuthTokenFromGitRemote ( ) ;
179
+ if ( ! token ) {
180
+ Logger . warn ( 'No Bitbucket auth token found in git remote' ) ;
181
+ vscode . window . showErrorMessage ( 'No Bitbucket auth token found in git remote' ) ;
182
+ return false ;
183
+ }
184
+ Logger . debug ( 'Authenticating with Bitbucket using auth token' ) ;
185
+ // Use the BitbucketResponseHandler to get user info
186
+ const userData = await this . _bitbucketResponseHandler . user ( token ) ;
187
+ const [ oAuthSiteDetails ] = await this . getOAuthSiteDetails (
188
+ ProductBitbucket ,
189
+ OAuthProvider . BitbucketCloud ,
190
+ userData . id ,
191
+ [
192
+ {
193
+ id : OAuthProvider . BitbucketCloud ,
194
+ name : ProductBitbucket . name ,
195
+ scopes : [ ] ,
196
+ avatarUrl : '' ,
197
+ url : 'https://api.bitbucket.org/2.0' ,
198
+ } ,
199
+ ] ,
200
+ ) ;
201
+ const oAuthInfo : OAuthInfo = {
202
+ access : token ,
203
+ refresh : '' ,
204
+ recievedAt : Date . now ( ) ,
205
+ user : {
206
+ id : userData . id ,
207
+ displayName : userData . displayName ,
208
+ email : userData . email ,
209
+ avatarUrl : userData . avatarUrl ,
210
+ } ,
211
+ state : AuthInfoState . Valid ,
212
+ } ;
213
+ await this . _credentialManager . saveAuthInfo ( oAuthSiteDetails , oAuthInfo ) ;
214
+ setTimeout (
215
+ ( ) => {
216
+ this . refreshBitbucketToken ( oAuthSiteDetails ) ;
217
+ } ,
218
+ 2 * 60 * 60 * 1000 ,
219
+ ) ;
220
+ if ( ! refresh ) {
221
+ this . _siteManager . addSites ( [ oAuthSiteDetails ] ) ;
222
+ // Fire authenticated event
223
+ authenticatedEvent ( oAuthSiteDetails , false ) . then ( ( e ) => {
224
+ this . _analyticsClient . sendTrackEvent ( e ) ;
225
+ } ) ;
226
+ Logger . info ( 'Successfully authenticated with Bitbucket using auth token' ) ;
227
+ }
228
+ return true ;
229
+ } catch ( e ) {
230
+ Logger . error ( e , 'Error authenticating with Bitbucket token' ) ;
231
+ vscode . window . showErrorMessage ( `Error authenticating with Bitbucket token: ${ e } ` ) ;
232
+ return false ;
233
+ }
234
+ }
235
+
236
+ public async getOAuthSiteDetails (
113
237
product : Product ,
114
238
provider : OAuthProvider ,
115
239
userId : string ,
0 commit comments