@@ -12,6 +12,7 @@ import {
12
12
isAuthError ,
13
13
isAuthRetryableFetchError ,
14
14
isAuthSessionMissingError ,
15
+ isAuthImplicitGrantRedirectError ,
15
16
} from './lib/errors'
16
17
import {
17
18
Fetch ,
@@ -88,13 +89,11 @@ import type {
88
89
LockFunc ,
89
90
UserIdentity ,
90
91
SignInAnonymouslyCredentials ,
91
- } from './lib/types'
92
- import {
93
92
MFAEnrollTOTPParams ,
94
93
MFAEnrollPhoneParams ,
95
94
AuthMFAEnrollTOTPResponse ,
96
95
AuthMFAEnrollPhoneResponse ,
97
- } from './lib/internal- types'
96
+ } from './lib/types'
98
97
99
98
polyfillGlobalThis ( ) // Make "globalThis" available
100
99
@@ -306,16 +305,34 @@ export default class GoTrueClient {
306
305
*/
307
306
private async _initialize ( ) : Promise < InitializeResult > {
308
307
try {
309
- const isPKCEFlow = isBrowser ( ) ? await this . _isPKCEFlow ( ) : false
310
- this . _debug ( '#_initialize()' , 'begin' , 'is PKCE flow' , isPKCEFlow )
311
-
312
- if ( isPKCEFlow || ( this . detectSessionInUrl && this . _isImplicitGrantFlow ( ) ) ) {
313
- const { data, error } = await this . _getSessionFromURL ( isPKCEFlow )
308
+ const params = parseParametersFromURL ( window . location . href )
309
+ let callbackUrlType = 'none'
310
+ if ( this . _isImplicitGrantCallback ( params ) ) {
311
+ callbackUrlType = 'implicit'
312
+ } else if ( await this . _isPKCECallback ( params ) ) {
313
+ callbackUrlType = 'pkce'
314
+ }
315
+
316
+ /**
317
+ * Attempt to get the session from the URL only if these conditions are fulfilled
318
+ *
319
+ * Note: If the URL isn't one of the callback url types (implicit or pkce),
320
+ * then there could be an existing session so we don't want to prematurely remove it
321
+ */
322
+ if ( isBrowser ( ) && this . detectSessionInUrl && callbackUrlType !== 'none' ) {
323
+ const { data, error } = await this . _getSessionFromURL ( params , callbackUrlType )
314
324
if ( error ) {
315
325
this . _debug ( '#_initialize()' , 'error detecting session from URL' , error )
316
326
317
- if ( error ?. code === 'identity_already_exists' ) {
318
- return { error }
327
+ if ( isAuthImplicitGrantRedirectError ( error ) ) {
328
+ const errorCode = error . details ?. code
329
+ if (
330
+ errorCode === 'identity_already_exists' ||
331
+ errorCode === 'identity_not_found' ||
332
+ errorCode === 'single_identity_not_deletable'
333
+ ) {
334
+ return { error }
335
+ }
319
336
}
320
337
321
338
// failed login attempt via url,
@@ -1419,7 +1436,10 @@ export default class GoTrueClient {
1419
1436
/**
1420
1437
* Gets the session data from a URL string
1421
1438
*/
1422
- private async _getSessionFromURL ( isPKCEFlow : boolean ) : Promise <
1439
+ private async _getSessionFromURL (
1440
+ params : { [ parameter : string ] : string } ,
1441
+ callbackUrlType : string
1442
+ ) : Promise <
1423
1443
| {
1424
1444
data : { session : Session ; redirectType : string | null }
1425
1445
error : null
@@ -1428,15 +1448,39 @@ export default class GoTrueClient {
1428
1448
> {
1429
1449
try {
1430
1450
if ( ! isBrowser ( ) ) throw new AuthImplicitGrantRedirectError ( 'No browser detected.' )
1431
- if ( this . flowType === 'implicit' && ! this . _isImplicitGrantFlow ( ) ) {
1432
- throw new AuthImplicitGrantRedirectError ( 'Not a valid implicit grant flow url.' )
1433
- } else if ( this . flowType == 'pkce' && ! isPKCEFlow ) {
1434
- throw new AuthPKCEGrantCodeExchangeError ( 'Not a valid PKCE flow url.' )
1451
+
1452
+ // If there's an error in the URL, it doesn't matter what flow it is, we just return the error.
1453
+ if ( params . error || params . error_description || params . error_code ) {
1454
+ // The error class returned implies that the redirect is from an implicit grant flow
1455
+ // but it could also be from a redirect error from a PKCE flow.
1456
+ throw new AuthImplicitGrantRedirectError (
1457
+ params . error_description || 'Error in URL with unspecified error_description' ,
1458
+ {
1459
+ error : params . error || 'unspecified_error' ,
1460
+ code : params . error_code || 'unspecified_code' ,
1461
+ }
1462
+ )
1435
1463
}
1436
1464
1437
- const params = parseParametersFromURL ( window . location . href )
1465
+ // Checks for mismatches between the flowType initialised in the client and the URL parameters
1466
+ switch ( callbackUrlType ) {
1467
+ case 'implicit' :
1468
+ if ( this . flowType === 'pkce' ) {
1469
+ throw new AuthPKCEGrantCodeExchangeError ( 'Not a valid PKCE flow url.' )
1470
+ }
1471
+ break
1472
+ case 'pkce' :
1473
+ if ( this . flowType === 'implicit' ) {
1474
+ throw new AuthImplicitGrantRedirectError ( 'Not a valid implicit grant flow url.' )
1475
+ }
1476
+ break
1477
+ default :
1478
+ // there's no mismatch so we continue
1479
+ }
1438
1480
1439
- if ( isPKCEFlow ) {
1481
+ // Since this is a redirect for PKCE, we attempt to retrieve the code from the URL for the code exchange
1482
+ if ( callbackUrlType === 'pkce' ) {
1483
+ this . _debug ( '#_initialize()' , 'begin' , 'is PKCE flow' , true )
1440
1484
if ( ! params . code ) throw new AuthPKCEGrantCodeExchangeError ( 'No code detected.' )
1441
1485
const { data, error } = await this . _exchangeCodeForSession ( params . code )
1442
1486
if ( error ) throw error
@@ -1449,16 +1493,6 @@ export default class GoTrueClient {
1449
1493
return { data : { session : data . session , redirectType : null } , error : null }
1450
1494
}
1451
1495
1452
- if ( params . error || params . error_description || params . error_code ) {
1453
- throw new AuthImplicitGrantRedirectError (
1454
- params . error_description || 'Error in URL with unspecified error_description' ,
1455
- {
1456
- error : params . error || 'unspecified_error' ,
1457
- code : params . error_code || 'unspecified_code' ,
1458
- }
1459
- )
1460
- }
1461
-
1462
1496
const {
1463
1497
provider_token,
1464
1498
provider_refresh_token,
@@ -1536,18 +1570,14 @@ export default class GoTrueClient {
1536
1570
/**
1537
1571
* Checks if the current URL contains parameters given by an implicit oauth grant flow (https://www.rfc-editor.org/rfc/rfc6749.html#section-4.2)
1538
1572
*/
1539
- private _isImplicitGrantFlow ( ) : boolean {
1540
- const params = parseParametersFromURL ( window . location . href )
1541
-
1542
- return ! ! ( isBrowser ( ) && ( params . access_token || params . error_description ) )
1573
+ private _isImplicitGrantCallback ( params : { [ parameter : string ] : string } ) : boolean {
1574
+ return Boolean ( params . access_token || params . error_description )
1543
1575
}
1544
1576
1545
1577
/**
1546
1578
* Checks if the current URL and backing storage contain parameters given by a PKCE flow
1547
1579
*/
1548
- private async _isPKCEFlow ( ) : Promise < boolean > {
1549
- const params = parseParametersFromURL ( window . location . href )
1550
-
1580
+ private async _isPKCECallback ( params : { [ parameter : string ] : string } ) : Promise < boolean > {
1551
1581
const currentStorageContent = await getItemAsync (
1552
1582
this . storage ,
1553
1583
`${ this . storageKey } -code-verifier`
0 commit comments