@@ -5,6 +5,7 @@ import type {
55 Event ,
66 MessageItem ,
77 StatusBarItem ,
8+ Uri ,
89} from 'vscode' ;
910import {
1011 authentication ,
@@ -106,6 +107,7 @@ export class SubscriptionService implements Disposable {
106107 }
107108 } ) ,
108109 container . uri . onDidReceiveSubscriptionUpdatedUri ( this . onSubscriptionUpdatedUri , this ) ,
110+ container . uri . onDidReceiveLoginUri ( this . onLoginUri , this ) ,
109111 ) ;
110112
111113 const subscription = this . getStoredSubscription ( ) ;
@@ -359,14 +361,39 @@ export class SubscriptionService implements Disposable {
359361 ) ;
360362 }
361363
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 > {
362386 // Abort any waiting authentication to ensure we can start a new flow
363387 await this . container . accountAuthentication . abort ( ) ;
364388 void this . showAccountView ( ) ;
365389
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+ } ) ;
367394 const loggedIn = Boolean ( session ) ;
368395 if ( loggedIn ) {
369- void this . showPlanMessage ( source ) ;
396+ void this . showPlanMessage ( options ?. source ) ;
370397 }
371398 return loggedIn ;
372399 }
@@ -914,7 +941,7 @@ export class SubscriptionService implements Disposable {
914941 @debug ( )
915942 private async ensureSession (
916943 createIfNeeded : boolean ,
917- options ?: { force ?: boolean ; signUp ?: boolean } ,
944+ options ?: { force ?: boolean ; signUp ?: boolean ; signIn ?: { code : string ; state ?: string } } ,
918945 ) : Promise < AuthenticationSession | undefined > {
919946 if ( this . _sessionPromise != null ) {
920947 void ( await this . _sessionPromise ) ;
@@ -924,7 +951,10 @@ export class SubscriptionService implements Disposable {
924951 if ( this . _session === null && ! createIfNeeded ) return undefined ;
925952
926953 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 (
928958 s => {
929959 this . _session = s ;
930960 this . _sessionPromise = undefined ;
@@ -945,23 +975,24 @@ export class SubscriptionService implements Disposable {
945975 @debug ( )
946976 private async getOrCreateSession (
947977 createIfNeeded : boolean ,
948- signUp : boolean = false ,
978+ options ?: { signUp ? : boolean ; signIn ?: { code : string ; state ?: string } } ,
949979 ) : Promise < AuthenticationSession | null > {
950980 const scope = getLogScope ( ) ;
951981
952982 let session : AuthenticationSession | null | undefined ;
953-
954983 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+ } ) ;
963991 } catch ( ex ) {
964992 session = null ;
993+ if ( options != null && createIfNeeded ) {
994+ this . container . accountAuthentication . clearOptionsForScopes ( authenticationProviderScopes ) ;
995+ }
965996
966997 if ( ex instanceof Error && ex . message . includes ( 'User did not consent' ) ) {
967998 setLogScopeExit ( scope , ' \u2022 User declined authentication' ) ;
@@ -1349,6 +1380,31 @@ export class SubscriptionService implements Disposable {
13491380 ) ;
13501381 }
13511382
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+
13521408 async onSubscriptionUpdatedUri ( ) {
13531409 if ( this . _session == null ) return ;
13541410 const oldSubscriptionState = this . _subscription . state ;
0 commit comments