@@ -5,6 +5,7 @@ import type {
5
5
Event ,
6
6
MessageItem ,
7
7
StatusBarItem ,
8
+ Uri ,
8
9
} from 'vscode' ;
9
10
import {
10
11
authentication ,
@@ -106,6 +107,7 @@ export class SubscriptionService implements Disposable {
106
107
}
107
108
} ) ,
108
109
container . uri . onDidReceiveSubscriptionUpdatedUri ( this . onSubscriptionUpdatedUri , this ) ,
110
+ container . uri . onDidReceiveLoginUri ( this . onLoginUri , this ) ,
109
111
) ;
110
112
111
113
const subscription = this . getStoredSubscription ( ) ;
@@ -359,14 +361,39 @@ export class SubscriptionService implements Disposable {
359
361
) ;
360
362
}
361
363
364
+ return this . loginCore ( { signUp : signUp , source : source } ) ;
365
+ }
366
+
367
+ async loginWithCode ( authentication : { code : string ; state ?: string } , source ?: Source ) : Promise < boolean > {
368
+ if ( ! ( await ensurePlusFeaturesEnabled ( ) ) ) return false ;
369
+ if ( this . container . telemetry . enabled ) {
370
+ this . container . telemetry . sendEvent ( 'subscription/action' , { action : 'sign-in' } , source ) ;
371
+ }
372
+
373
+ const session = await this . ensureSession ( false ) ;
374
+ if ( session != null ) {
375
+ await this . logout ( undefined , source ) ;
376
+ }
377
+
378
+ return this . loginCore ( { signIn : authentication , source : source } ) ;
379
+ }
380
+
381
+ private async loginCore ( options ?: {
382
+ signUp ?: boolean ;
383
+ source ?: Source ;
384
+ signIn ?: { code : string ; state ?: string } ;
385
+ } ) : Promise < boolean > {
362
386
// Abort any waiting authentication to ensure we can start a new flow
363
387
await this . container . accountAuthentication . abort ( ) ;
364
388
void this . showAccountView ( ) ;
365
389
366
- const session = await this . ensureSession ( true , { signUp : signUp } ) ;
390
+ const session = await this . ensureSession ( true , {
391
+ signIn : options ?. signIn ,
392
+ signUp : options ?. signUp ,
393
+ } ) ;
367
394
const loggedIn = Boolean ( session ) ;
368
395
if ( loggedIn ) {
369
- void this . showPlanMessage ( source ) ;
396
+ void this . showPlanMessage ( options ?. source ) ;
370
397
}
371
398
return loggedIn ;
372
399
}
@@ -914,7 +941,7 @@ export class SubscriptionService implements Disposable {
914
941
@debug ( )
915
942
private async ensureSession (
916
943
createIfNeeded : boolean ,
917
- options ?: { force ?: boolean ; signUp ?: boolean } ,
944
+ options ?: { force ?: boolean ; signUp ?: boolean ; signIn ?: { code : string ; state ?: string } } ,
918
945
) : Promise < AuthenticationSession | undefined > {
919
946
if ( this . _sessionPromise != null ) {
920
947
void ( await this . _sessionPromise ) ;
@@ -924,7 +951,10 @@ export class SubscriptionService implements Disposable {
924
951
if ( this . _session === null && ! createIfNeeded ) return undefined ;
925
952
926
953
if ( this . _sessionPromise === undefined ) {
927
- this . _sessionPromise = this . getOrCreateSession ( createIfNeeded , options ?. signUp ) . then (
954
+ this . _sessionPromise = this . getOrCreateSession ( createIfNeeded , {
955
+ signUp : options ?. signUp ,
956
+ signIn : options ?. signIn ,
957
+ } ) . then (
928
958
s => {
929
959
this . _session = s ;
930
960
this . _sessionPromise = undefined ;
@@ -945,23 +975,24 @@ export class SubscriptionService implements Disposable {
945
975
@debug ( )
946
976
private async getOrCreateSession (
947
977
createIfNeeded : boolean ,
948
- signUp : boolean = false ,
978
+ options ?: { signUp ? : boolean ; signIn ?: { code : string ; state ?: string } } ,
949
979
) : Promise < AuthenticationSession | null > {
950
980
const scope = getLogScope ( ) ;
951
981
952
982
let session : AuthenticationSession | null | undefined ;
953
-
954
983
try {
955
- session = await authentication . getSession (
956
- authenticationProviderId ,
957
- signUp ? [ ...authenticationProviderScopes , 'signUp' ] : authenticationProviderScopes ,
958
- {
959
- createIfNone : createIfNeeded ,
960
- silent : ! createIfNeeded ,
961
- } ,
962
- ) ;
984
+ if ( options != null && createIfNeeded ) {
985
+ this . container . accountAuthentication . setOptionsForScopes ( authenticationProviderScopes , options ) ;
986
+ }
987
+ session = await authentication . getSession ( authenticationProviderId , authenticationProviderScopes , {
988
+ createIfNone : createIfNeeded ,
989
+ silent : ! createIfNeeded ,
990
+ } ) ;
963
991
} catch ( ex ) {
964
992
session = null ;
993
+ if ( options != null && createIfNeeded ) {
994
+ this . container . accountAuthentication . clearOptionsForScopes ( authenticationProviderScopes ) ;
995
+ }
965
996
966
997
if ( ex instanceof Error && ex . message . includes ( 'User did not consent' ) ) {
967
998
setLogScopeExit ( scope , ' \u2022 User declined authentication' ) ;
@@ -1349,6 +1380,31 @@ export class SubscriptionService implements Disposable {
1349
1380
) ;
1350
1381
}
1351
1382
1383
+ onLoginUri ( uri : Uri ) {
1384
+ const scope = getLogScope ( ) ;
1385
+ const queryParams : URLSearchParams = new URLSearchParams ( uri . query ) ;
1386
+ const code = queryParams . get ( 'code' ) ;
1387
+ const state = queryParams . get ( 'state' ) ;
1388
+ const context = queryParams . get ( 'context' ) ;
1389
+ let contextMessage = 'sign in to GitKraken' ;
1390
+
1391
+ switch ( context ) {
1392
+ case 'start_trial' :
1393
+ contextMessage = 'start a Pro trial' ;
1394
+ break ;
1395
+ }
1396
+
1397
+ if ( code == null ) {
1398
+ Logger . error ( `No code provided. Link: ${ uri . toString ( true ) } ` , scope ) ;
1399
+ void window . showErrorMessage (
1400
+ `Unable to ${ contextMessage } with that link. Please try clicking the link again. If this issue persists, please contact support.` ,
1401
+ ) ;
1402
+ return ;
1403
+ }
1404
+
1405
+ void this . loginWithCode ( { code : code , state : state ?? undefined } , { source : 'deeplink' } ) ;
1406
+ }
1407
+
1352
1408
async onSubscriptionUpdatedUri ( ) {
1353
1409
if ( this . _session == null ) return ;
1354
1410
const oldSubscriptionState = this . _subscription . state ;
0 commit comments