@supabase/gotrue-js 2.108.2 → 2.112.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/dist/main/GoTrueAdminApi.d.ts +1 -4
- package/dist/main/GoTrueAdminApi.d.ts.map +1 -1
- package/dist/main/GoTrueAdminApi.js +1 -4
- package/dist/main/GoTrueAdminApi.js.map +1 -1
- package/dist/main/GoTrueClient.d.ts +67 -21
- package/dist/main/GoTrueClient.d.ts.map +1 -1
- package/dist/main/GoTrueClient.js +217 -68
- package/dist/main/GoTrueClient.js.map +1 -1
- package/dist/main/lib/constants.d.ts +13 -0
- package/dist/main/lib/constants.d.ts.map +1 -1
- package/dist/main/lib/constants.js +14 -1
- package/dist/main/lib/constants.js.map +1 -1
- package/dist/main/lib/errors.d.ts +1 -1
- package/dist/main/lib/fetch.d.ts.map +1 -1
- package/dist/main/lib/fetch.js +12 -6
- package/dist/main/lib/fetch.js.map +1 -1
- package/dist/main/lib/helpers.d.ts +50 -1
- package/dist/main/lib/helpers.d.ts.map +1 -1
- package/dist/main/lib/helpers.js +161 -5
- package/dist/main/lib/helpers.js.map +1 -1
- package/dist/main/lib/locks.d.ts +1 -1
- package/dist/main/lib/locks.js +1 -1
- package/dist/main/lib/types.d.ts +71 -9
- package/dist/main/lib/types.d.ts.map +1 -1
- package/dist/main/lib/types.js.map +1 -1
- package/dist/main/lib/version.d.ts +1 -1
- package/dist/main/lib/version.js +1 -1
- package/dist/module/GoTrueAdminApi.d.ts +1 -4
- package/dist/module/GoTrueAdminApi.d.ts.map +1 -1
- package/dist/module/GoTrueAdminApi.js +1 -4
- package/dist/module/GoTrueAdminApi.js.map +1 -1
- package/dist/module/GoTrueClient.d.ts +67 -21
- package/dist/module/GoTrueClient.d.ts.map +1 -1
- package/dist/module/GoTrueClient.js +219 -70
- package/dist/module/GoTrueClient.js.map +1 -1
- package/dist/module/lib/constants.d.ts +13 -0
- package/dist/module/lib/constants.d.ts.map +1 -1
- package/dist/module/lib/constants.js +13 -0
- package/dist/module/lib/constants.js.map +1 -1
- package/dist/module/lib/errors.d.ts +1 -1
- package/dist/module/lib/fetch.d.ts.map +1 -1
- package/dist/module/lib/fetch.js +12 -6
- package/dist/module/lib/fetch.js.map +1 -1
- package/dist/module/lib/helpers.d.ts +50 -1
- package/dist/module/lib/helpers.d.ts.map +1 -1
- package/dist/module/lib/helpers.js +153 -5
- package/dist/module/lib/helpers.js.map +1 -1
- package/dist/module/lib/locks.d.ts +1 -1
- package/dist/module/lib/locks.js +1 -1
- package/dist/module/lib/types.d.ts +71 -9
- package/dist/module/lib/types.d.ts.map +1 -1
- package/dist/module/lib/types.js.map +1 -1
- package/dist/module/lib/version.d.ts +1 -1
- package/dist/module/lib/version.js +1 -1
- package/dist/tsconfig.module.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/GoTrueAdminApi.ts +1 -4
- package/src/GoTrueClient.ts +268 -91
- package/src/lib/constants.ts +15 -0
- package/src/lib/errors.ts +1 -1
- package/src/lib/fetch.ts +13 -8
- package/src/lib/helpers.ts +198 -6
- package/src/lib/locks.ts +1 -1
- package/src/lib/types.ts +71 -9
- package/src/lib/version.ts +1 -1
package/src/GoTrueClient.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
EXPIRY_MARGIN_MS,
|
|
7
7
|
GOTRUE_URL,
|
|
8
8
|
JWKS_TTL,
|
|
9
|
+
PKCE_FLOW_ID_PARAM,
|
|
9
10
|
REFRESH_FAILURE_COOLDOWN_MS,
|
|
10
11
|
STORAGE_KEY,
|
|
11
12
|
} from './lib/constants'
|
|
@@ -36,6 +37,7 @@ import {
|
|
|
36
37
|
_userResponse,
|
|
37
38
|
} from './lib/fetch'
|
|
38
39
|
import {
|
|
40
|
+
appendFlowIdToRedirectTo,
|
|
39
41
|
assertPasskeyExperimentalEnabled,
|
|
40
42
|
decodeJWT,
|
|
41
43
|
deepClone,
|
|
@@ -47,14 +49,19 @@ import {
|
|
|
47
49
|
insecureUserWarningProxy,
|
|
48
50
|
isBrowser,
|
|
49
51
|
parseParametersFromURL,
|
|
52
|
+
pkceVerifierSlotKey,
|
|
53
|
+
removeAllPKCEVerifiers,
|
|
50
54
|
removeItemAsync,
|
|
55
|
+
removePKCEVerifier,
|
|
51
56
|
resolveFetch,
|
|
57
|
+
retrievePKCEVerifier,
|
|
52
58
|
retryable,
|
|
53
59
|
setItemAsync,
|
|
54
60
|
sleep,
|
|
55
61
|
supportsLocalStorage,
|
|
56
62
|
userNotAvailableProxy,
|
|
57
63
|
validateExp,
|
|
64
|
+
validatePKCEFlowId,
|
|
58
65
|
} from './lib/helpers'
|
|
59
66
|
import { memoryLocalStorageAdapter } from './lib/local-storage'
|
|
60
67
|
import { LockAcquireTimeoutError, navigatorLock } from './lib/locks'
|
|
@@ -323,6 +330,23 @@ export default class GoTrueClient {
|
|
|
323
330
|
* Keep extra care to never reject or throw uncaught errors
|
|
324
331
|
*/
|
|
325
332
|
protected initializePromise: Promise<InitializeResult> | null = null
|
|
333
|
+
/**
|
|
334
|
+
* Non-null only while `initialize()` is running. While open,
|
|
335
|
+
* `_notifyAllSubscribers` enqueues init-chain notifications (those fired with
|
|
336
|
+
* `broadcast = true`, i.e. from `_recoverAndRefresh`) into this array instead
|
|
337
|
+
* of firing directly, so that `initializePromise` is guaranteed to be
|
|
338
|
+
* resolved before any subscriber callback runs. Callbacks that call
|
|
339
|
+
* `getSession()` / `getUser()` etc. would otherwise deadlock because those
|
|
340
|
+
* methods await `initializePromise`. Notifications from the incoming
|
|
341
|
+
* BroadcastChannel handler (`broadcast = false`) are not enqueued — they fire
|
|
342
|
+
* immediately. Flushed (in order) by `initialize()` after `initializePromise`
|
|
343
|
+
* settles.
|
|
344
|
+
*/
|
|
345
|
+
private _pendingInitNotifications: Array<{
|
|
346
|
+
event: AuthChangeEvent
|
|
347
|
+
session: Session | null
|
|
348
|
+
broadcast: boolean
|
|
349
|
+
}> | null = null
|
|
326
350
|
protected detectSessionInUrl:
|
|
327
351
|
| boolean
|
|
328
352
|
| ((url: URL, params: { [parameter: string]: string }) => boolean) = true
|
|
@@ -584,6 +608,16 @@ export default class GoTrueClient {
|
|
|
584
608
|
return await this.initializePromise
|
|
585
609
|
}
|
|
586
610
|
|
|
611
|
+
// Open the notification queue before _initialize() runs so that every
|
|
612
|
+
// _notifyAllSubscribers call inside the init chain enqueues instead of
|
|
613
|
+
// firing. Without this, a callback receiving SIGNED_IN (or TOKEN_REFRESHED
|
|
614
|
+
// / SIGNED_OUT) during _recoverAndRefresh would deadlock if it called
|
|
615
|
+
// getSession() / getUser() — those methods await initializePromise, which
|
|
616
|
+
// can only resolve after the callback returns, which can only return after
|
|
617
|
+
// getSession() resolves. The queue is flushed below after initializePromise
|
|
618
|
+
// has settled, so callbacks run with a fully resolved initializePromise.
|
|
619
|
+
this._pendingInitNotifications = []
|
|
620
|
+
|
|
587
621
|
this.initializePromise = (async () => {
|
|
588
622
|
if (this.lock != null) {
|
|
589
623
|
// TODO(v3): remove legacy lock path
|
|
@@ -594,7 +628,17 @@ export default class GoTrueClient {
|
|
|
594
628
|
return await this._initialize()
|
|
595
629
|
})()
|
|
596
630
|
|
|
597
|
-
|
|
631
|
+
const result = await this.initializePromise
|
|
632
|
+
|
|
633
|
+
// initializePromise is now resolved — flush queued notifications in order.
|
|
634
|
+
// Callbacks can safely call getSession() / getUser() / signOut() etc.
|
|
635
|
+
const queue = this._pendingInitNotifications ?? []
|
|
636
|
+
this._pendingInitNotifications = null
|
|
637
|
+
for (const n of queue) {
|
|
638
|
+
await this._notifyAllSubscribers(n.event, n.session, n.broadcast)
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return result
|
|
598
642
|
}
|
|
599
643
|
|
|
600
644
|
/**
|
|
@@ -968,6 +1012,7 @@ export default class GoTrueClient {
|
|
|
968
1012
|
* ```
|
|
969
1013
|
*/
|
|
970
1014
|
async signUp(credentials: SignUpWithPasswordCredentials): Promise<AuthResponse> {
|
|
1015
|
+
let flowId: string | null = null
|
|
971
1016
|
try {
|
|
972
1017
|
let res: AuthResponse
|
|
973
1018
|
if ('email' in credentials) {
|
|
@@ -975,14 +1020,11 @@ export default class GoTrueClient {
|
|
|
975
1020
|
let codeChallenge: string | null = null
|
|
976
1021
|
let codeChallengeMethod: string | null = null
|
|
977
1022
|
if (this.flowType === 'pkce') {
|
|
978
|
-
;[codeChallenge, codeChallengeMethod] = await
|
|
979
|
-
this.storage,
|
|
980
|
-
this.storageKey
|
|
981
|
-
)
|
|
1023
|
+
;[codeChallenge, codeChallengeMethod, flowId] = await this._getCodeChallengeAndMethod()
|
|
982
1024
|
}
|
|
983
1025
|
res = await _request(this.fetch, 'POST', `${this.url}/signup`, {
|
|
984
1026
|
headers: this.headers,
|
|
985
|
-
redirectTo: options?.emailRedirectTo,
|
|
1027
|
+
redirectTo: this._maybeAppendFlowIdToRedirect(options?.emailRedirectTo, flowId),
|
|
986
1028
|
body: {
|
|
987
1029
|
email,
|
|
988
1030
|
password,
|
|
@@ -1015,7 +1057,7 @@ export default class GoTrueClient {
|
|
|
1015
1057
|
const { data, error } = res
|
|
1016
1058
|
|
|
1017
1059
|
if (error || !data) {
|
|
1018
|
-
await
|
|
1060
|
+
await removePKCEVerifier(this.storage, this.storageKey, flowId)
|
|
1019
1061
|
return this._returnResult({ data: { user: null, session: null }, error: error })
|
|
1020
1062
|
}
|
|
1021
1063
|
|
|
@@ -1029,7 +1071,7 @@ export default class GoTrueClient {
|
|
|
1029
1071
|
|
|
1030
1072
|
return this._returnResult({ data: { user, session }, error: null })
|
|
1031
1073
|
} catch (error) {
|
|
1032
|
-
await
|
|
1074
|
+
await removePKCEVerifier(this.storage, this.storageKey, flowId)
|
|
1033
1075
|
if (isAuthError(error)) {
|
|
1034
1076
|
return this._returnResult({ data: { user: null, session: null }, error })
|
|
1035
1077
|
}
|
|
@@ -1252,7 +1294,8 @@ export default class GoTrueClient {
|
|
|
1252
1294
|
* {
|
|
1253
1295
|
* data: {
|
|
1254
1296
|
* provider: 'github',
|
|
1255
|
-
* url: <PROVIDER_URL_TO_REDIRECT_TO
|
|
1297
|
+
* url: <PROVIDER_URL_TO_REDIRECT_TO>,
|
|
1298
|
+
* flowId: <PKCE_FLOW_ID_OR_NULL>
|
|
1256
1299
|
* },
|
|
1257
1300
|
* error: null
|
|
1258
1301
|
* }
|
|
@@ -1325,12 +1368,29 @@ export default class GoTrueClient {
|
|
|
1325
1368
|
*
|
|
1326
1369
|
* @remarks
|
|
1327
1370
|
* - Used when `flowType` is set to `pkce` in client options.
|
|
1371
|
+
* - When several PKCE flows are in flight at once, pass `options.flowId` so
|
|
1372
|
+
* the code is exchanged with the verifier created by that specific flow.
|
|
1373
|
+
* The flow id is returned by `signInWithOAuth`, and with
|
|
1374
|
+
* `experimental.appendPkceFlowIdToRedirects` enabled it also arrives on
|
|
1375
|
+
* your callback URL as the reserved `sb_flow_id` query parameter (read
|
|
1376
|
+
* automatically in a browser).
|
|
1377
|
+
* - When a flow id is present but its stored verifier is gone (evicted,
|
|
1378
|
+
* already used, or from another device), the call fails with a verifier
|
|
1379
|
+
* missing error instead of trying another flow's verifier — a mismatched
|
|
1380
|
+
* verifier would consume the single-use code. Without any flow id the
|
|
1381
|
+
* most recently stored verifier is used, as before.
|
|
1328
1382
|
*
|
|
1329
1383
|
* @example Exchange Auth Code
|
|
1330
1384
|
* ```js
|
|
1331
1385
|
* supabase.auth.exchangeCodeForSession('34e770dd-9ff9-416c-87fa-43b31d7ef225')
|
|
1332
1386
|
* ```
|
|
1333
1387
|
*
|
|
1388
|
+
* @example Exchange Auth Code for a specific flow (e.g. in a server-side callback handler)
|
|
1389
|
+
* ```js
|
|
1390
|
+
* const flowId = requestUrl.searchParams.get('sb_flow_id')
|
|
1391
|
+
* supabase.auth.exchangeCodeForSession(code, flowId ? { flowId } : undefined)
|
|
1392
|
+
* ```
|
|
1393
|
+
*
|
|
1334
1394
|
* @exampleResponse Exchange Auth Code
|
|
1335
1395
|
* ```json
|
|
1336
1396
|
* {
|
|
@@ -1488,17 +1548,20 @@ export default class GoTrueClient {
|
|
|
1488
1548
|
* }
|
|
1489
1549
|
* ```
|
|
1490
1550
|
*/
|
|
1491
|
-
async exchangeCodeForSession(
|
|
1551
|
+
async exchangeCodeForSession(
|
|
1552
|
+
authCode: string,
|
|
1553
|
+
options?: { flowId?: string }
|
|
1554
|
+
): Promise<AuthTokenResponse> {
|
|
1492
1555
|
await this.initializePromise
|
|
1493
1556
|
|
|
1494
1557
|
if (this.lock != null) {
|
|
1495
1558
|
// TODO(v3): remove legacy lock path
|
|
1496
1559
|
return this._acquireLock(this.lockAcquireTimeout, async () => {
|
|
1497
|
-
return this._exchangeCodeForSession(authCode)
|
|
1560
|
+
return this._exchangeCodeForSession(authCode, options)
|
|
1498
1561
|
})
|
|
1499
1562
|
}
|
|
1500
1563
|
|
|
1501
|
-
return this._exchangeCodeForSession(authCode)
|
|
1564
|
+
return this._exchangeCodeForSession(authCode, options)
|
|
1502
1565
|
}
|
|
1503
1566
|
|
|
1504
1567
|
/**
|
|
@@ -1931,15 +1994,40 @@ export default class GoTrueClient {
|
|
|
1931
1994
|
}
|
|
1932
1995
|
}
|
|
1933
1996
|
|
|
1934
|
-
private async _exchangeCodeForSession(
|
|
1997
|
+
private async _exchangeCodeForSession(
|
|
1998
|
+
authCode: string,
|
|
1999
|
+
options?: { flowId?: string }
|
|
2000
|
+
): Promise<
|
|
1935
2001
|
| {
|
|
1936
2002
|
data: { session: Session; user: User; redirectType: string | null }
|
|
1937
2003
|
error: null
|
|
1938
2004
|
}
|
|
1939
2005
|
| { data: { session: null; user: null; redirectType: null }; error: AuthError }
|
|
1940
2006
|
> {
|
|
1941
|
-
const
|
|
1942
|
-
const
|
|
2007
|
+
const hasExplicitFlowId = options?.flowId != null
|
|
2008
|
+
const requestedFlowId = hasExplicitFlowId
|
|
2009
|
+
? validatePKCEFlowId(options?.flowId)
|
|
2010
|
+
: isBrowser()
|
|
2011
|
+
? validatePKCEFlowId(parseParametersFromURL(window.location.href)[PKCE_FLOW_ID_PARAM])
|
|
2012
|
+
: null
|
|
2013
|
+
|
|
2014
|
+
if (hasExplicitFlowId && !requestedFlowId) {
|
|
2015
|
+
this._debug(
|
|
2016
|
+
'#_exchangeCodeForSession()',
|
|
2017
|
+
'provided flowId is not a valid flow id',
|
|
2018
|
+
options?.flowId
|
|
2019
|
+
)
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
// With a flow id (explicit or from the callback URL) the lookup is
|
|
2023
|
+
// slot-only and a miss fails fast — see retrievePKCEVerifier. An invalid
|
|
2024
|
+
// explicit flow id also fails fast rather than borrowing another flow's
|
|
2025
|
+
// verifier.
|
|
2026
|
+
const { verifier: storageItem, flowId } =
|
|
2027
|
+
hasExplicitFlowId && !requestedFlowId
|
|
2028
|
+
? { verifier: null, flowId: null }
|
|
2029
|
+
: await retrievePKCEVerifier(this.storage, this.storageKey, requestedFlowId)
|
|
2030
|
+
const [codeVerifier, redirectType] = (storageItem ?? '').split('/')
|
|
1943
2031
|
|
|
1944
2032
|
try {
|
|
1945
2033
|
if (!codeVerifier && this.flowType === 'pkce') {
|
|
@@ -1959,7 +2047,7 @@ export default class GoTrueClient {
|
|
|
1959
2047
|
xform: _sessionResponse,
|
|
1960
2048
|
}
|
|
1961
2049
|
)
|
|
1962
|
-
await
|
|
2050
|
+
await removePKCEVerifier(this.storage, this.storageKey, flowId)
|
|
1963
2051
|
if (error) {
|
|
1964
2052
|
throw error
|
|
1965
2053
|
}
|
|
@@ -1979,7 +2067,7 @@ export default class GoTrueClient {
|
|
|
1979
2067
|
}
|
|
1980
2068
|
return this._returnResult({ data: { ...data, redirectType: redirectType ?? null }, error })
|
|
1981
2069
|
} catch (error) {
|
|
1982
|
-
await
|
|
2070
|
+
await removePKCEVerifier(this.storage, this.storageKey, flowId)
|
|
1983
2071
|
if (isAuthError(error)) {
|
|
1984
2072
|
return this._returnResult({
|
|
1985
2073
|
data: { user: null, session: null, redirectType: null },
|
|
@@ -2179,16 +2267,14 @@ export default class GoTrueClient {
|
|
|
2179
2267
|
* ```
|
|
2180
2268
|
*/
|
|
2181
2269
|
async signInWithOtp(credentials: SignInWithPasswordlessCredentials): Promise<AuthOtpResponse> {
|
|
2270
|
+
let flowId: string | null = null
|
|
2182
2271
|
try {
|
|
2183
2272
|
if ('email' in credentials) {
|
|
2184
2273
|
const { email, options } = credentials
|
|
2185
2274
|
let codeChallenge: string | null = null
|
|
2186
2275
|
let codeChallengeMethod: string | null = null
|
|
2187
2276
|
if (this.flowType === 'pkce') {
|
|
2188
|
-
;[codeChallenge, codeChallengeMethod] = await
|
|
2189
|
-
this.storage,
|
|
2190
|
-
this.storageKey
|
|
2191
|
-
)
|
|
2277
|
+
;[codeChallenge, codeChallengeMethod, flowId] = await this._getCodeChallengeAndMethod()
|
|
2192
2278
|
}
|
|
2193
2279
|
const { error } = await _request(this.fetch, 'POST', `${this.url}/otp`, {
|
|
2194
2280
|
headers: this.headers,
|
|
@@ -2200,7 +2286,7 @@ export default class GoTrueClient {
|
|
|
2200
2286
|
code_challenge: codeChallenge,
|
|
2201
2287
|
code_challenge_method: codeChallengeMethod,
|
|
2202
2288
|
},
|
|
2203
|
-
redirectTo: options?.emailRedirectTo,
|
|
2289
|
+
redirectTo: this._maybeAppendFlowIdToRedirect(options?.emailRedirectTo, flowId),
|
|
2204
2290
|
})
|
|
2205
2291
|
return this._returnResult({ data: { user: null, session: null }, error })
|
|
2206
2292
|
}
|
|
@@ -2223,7 +2309,7 @@ export default class GoTrueClient {
|
|
|
2223
2309
|
}
|
|
2224
2310
|
throw new AuthInvalidCredentialsError('You must provide either an email or phone number.')
|
|
2225
2311
|
} catch (error) {
|
|
2226
|
-
await
|
|
2312
|
+
await removePKCEVerifier(this.storage, this.storageKey, flowId)
|
|
2227
2313
|
if (isAuthError(error)) {
|
|
2228
2314
|
return this._returnResult({ data: { user: null, session: null }, error })
|
|
2229
2315
|
}
|
|
@@ -2470,21 +2556,19 @@ export default class GoTrueClient {
|
|
|
2470
2556
|
* ```
|
|
2471
2557
|
*/
|
|
2472
2558
|
async signInWithSSO(params: SignInWithSSO): Promise<SSOResponse> {
|
|
2559
|
+
let flowId: string | null = null
|
|
2473
2560
|
try {
|
|
2474
2561
|
let codeChallenge: string | null = null
|
|
2475
2562
|
let codeChallengeMethod: string | null = null
|
|
2476
2563
|
if (this.flowType === 'pkce') {
|
|
2477
|
-
;[codeChallenge, codeChallengeMethod] = await
|
|
2478
|
-
this.storage,
|
|
2479
|
-
this.storageKey
|
|
2480
|
-
)
|
|
2564
|
+
;[codeChallenge, codeChallengeMethod, flowId] = await this._getCodeChallengeAndMethod()
|
|
2481
2565
|
}
|
|
2482
2566
|
|
|
2483
2567
|
const result = await _request(this.fetch, 'POST', `${this.url}/sso`, {
|
|
2484
2568
|
body: {
|
|
2485
2569
|
...('providerId' in params ? { provider_id: params.providerId } : null),
|
|
2486
2570
|
...('domain' in params ? { domain: params.domain } : null),
|
|
2487
|
-
redirect_to: params.options?.redirectTo
|
|
2571
|
+
redirect_to: this._maybeAppendFlowIdToRedirect(params.options?.redirectTo, flowId),
|
|
2488
2572
|
...(params?.options?.captchaToken
|
|
2489
2573
|
? { gotrue_meta_security: { captcha_token: params.options.captchaToken } }
|
|
2490
2574
|
: null),
|
|
@@ -2503,7 +2587,7 @@ export default class GoTrueClient {
|
|
|
2503
2587
|
|
|
2504
2588
|
return this._returnResult(result)
|
|
2505
2589
|
} catch (error) {
|
|
2506
|
-
await
|
|
2590
|
+
await removePKCEVerifier(this.storage, this.storageKey, flowId)
|
|
2507
2591
|
if (isAuthError(error)) {
|
|
2508
2592
|
return this._returnResult({ data: null, error })
|
|
2509
2593
|
}
|
|
@@ -2629,6 +2713,7 @@ export default class GoTrueClient {
|
|
|
2629
2713
|
* ```
|
|
2630
2714
|
*/
|
|
2631
2715
|
async resend(credentials: ResendParams): Promise<AuthOtpResponse> {
|
|
2716
|
+
let flowId: string | null = null
|
|
2632
2717
|
try {
|
|
2633
2718
|
const endpoint = `${this.url}/resend`
|
|
2634
2719
|
if ('email' in credentials) {
|
|
@@ -2636,10 +2721,7 @@ export default class GoTrueClient {
|
|
|
2636
2721
|
let codeChallenge: string | null = null
|
|
2637
2722
|
let codeChallengeMethod: string | null = null
|
|
2638
2723
|
if (this.flowType === 'pkce') {
|
|
2639
|
-
;[codeChallenge, codeChallengeMethod] = await
|
|
2640
|
-
this.storage,
|
|
2641
|
-
this.storageKey
|
|
2642
|
-
)
|
|
2724
|
+
;[codeChallenge, codeChallengeMethod, flowId] = await this._getCodeChallengeAndMethod()
|
|
2643
2725
|
}
|
|
2644
2726
|
const { error } = await _request(this.fetch, 'POST', endpoint, {
|
|
2645
2727
|
headers: this.headers,
|
|
@@ -2650,10 +2732,10 @@ export default class GoTrueClient {
|
|
|
2650
2732
|
code_challenge: codeChallenge,
|
|
2651
2733
|
code_challenge_method: codeChallengeMethod,
|
|
2652
2734
|
},
|
|
2653
|
-
redirectTo: options?.emailRedirectTo,
|
|
2735
|
+
redirectTo: this._maybeAppendFlowIdToRedirect(options?.emailRedirectTo, flowId),
|
|
2654
2736
|
})
|
|
2655
2737
|
if (error) {
|
|
2656
|
-
await
|
|
2738
|
+
await removePKCEVerifier(this.storage, this.storageKey, flowId)
|
|
2657
2739
|
}
|
|
2658
2740
|
return this._returnResult({ data: { user: null, session: null }, error })
|
|
2659
2741
|
} else if ('phone' in credentials) {
|
|
@@ -2675,7 +2757,7 @@ export default class GoTrueClient {
|
|
|
2675
2757
|
'You must provide either an email or phone number and a type'
|
|
2676
2758
|
)
|
|
2677
2759
|
} catch (error) {
|
|
2678
|
-
await
|
|
2760
|
+
await removePKCEVerifier(this.storage, this.storageKey, flowId)
|
|
2679
2761
|
if (isAuthError(error)) {
|
|
2680
2762
|
return this._returnResult({ data: { user: null, session: null }, error })
|
|
2681
2763
|
}
|
|
@@ -2692,7 +2774,7 @@ export default class GoTrueClient {
|
|
|
2692
2774
|
* to the client. If that storage is based on request cookies for example,
|
|
2693
2775
|
* the values in it may not be authentic and therefore it's strongly advised
|
|
2694
2776
|
* against using this method and its results in such circumstances. A warning
|
|
2695
|
-
* will be emitted if this is detected. Use {@link
|
|
2777
|
+
* will be emitted if this is detected. Use {@link GoTrueClient.getUser} instead.
|
|
2696
2778
|
*
|
|
2697
2779
|
* @category Auth
|
|
2698
2780
|
*
|
|
@@ -2861,7 +2943,7 @@ export default class GoTrueClient {
|
|
|
2861
2943
|
}
|
|
2862
2944
|
|
|
2863
2945
|
/**
|
|
2864
|
-
* Use instead of {@link
|
|
2946
|
+
* Use instead of {@link GoTrueClient.getSession} inside the library. Loads the session
|
|
2865
2947
|
* via `__loadSession` (which may trigger a refresh if the access token is
|
|
2866
2948
|
* within the expiry margin) and runs `fn` with the result.
|
|
2867
2949
|
*/
|
|
@@ -2906,7 +2988,7 @@ export default class GoTrueClient {
|
|
|
2906
2988
|
/**
|
|
2907
2989
|
* NEVER USE DIRECTLY!
|
|
2908
2990
|
*
|
|
2909
|
-
* Always use
|
|
2991
|
+
* Always use `_useSession`.
|
|
2910
2992
|
*/
|
|
2911
2993
|
private async __loadSession(): Promise<
|
|
2912
2994
|
| {
|
|
@@ -3170,7 +3252,6 @@ export default class GoTrueClient {
|
|
|
3170
3252
|
// session in the database, indicating the user is signed out.
|
|
3171
3253
|
|
|
3172
3254
|
await this._removeSession()
|
|
3173
|
-
await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`)
|
|
3174
3255
|
}
|
|
3175
3256
|
|
|
3176
3257
|
return this._returnResult({ data: { user: null }, error })
|
|
@@ -3318,6 +3399,7 @@ export default class GoTrueClient {
|
|
|
3318
3399
|
emailRedirectTo?: string | undefined
|
|
3319
3400
|
} = {}
|
|
3320
3401
|
): Promise<UserResponse> {
|
|
3402
|
+
let flowId: string | null = null
|
|
3321
3403
|
try {
|
|
3322
3404
|
return await this._useSession(async (result) => {
|
|
3323
3405
|
const { data: sessionData, error: sessionError } = result
|
|
@@ -3331,15 +3413,12 @@ export default class GoTrueClient {
|
|
|
3331
3413
|
let codeChallenge: string | null = null
|
|
3332
3414
|
let codeChallengeMethod: string | null = null
|
|
3333
3415
|
if (this.flowType === 'pkce' && attributes.email != null) {
|
|
3334
|
-
;[codeChallenge, codeChallengeMethod] = await
|
|
3335
|
-
this.storage,
|
|
3336
|
-
this.storageKey
|
|
3337
|
-
)
|
|
3416
|
+
;[codeChallenge, codeChallengeMethod, flowId] = await this._getCodeChallengeAndMethod()
|
|
3338
3417
|
}
|
|
3339
3418
|
|
|
3340
3419
|
const { data, error: userError } = await _request(this.fetch, 'PUT', `${this.url}/user`, {
|
|
3341
3420
|
headers: this.headers,
|
|
3342
|
-
redirectTo: options?.emailRedirectTo,
|
|
3421
|
+
redirectTo: this._maybeAppendFlowIdToRedirect(options?.emailRedirectTo, flowId),
|
|
3343
3422
|
body: {
|
|
3344
3423
|
...attributes,
|
|
3345
3424
|
code_challenge: codeChallenge,
|
|
@@ -3357,7 +3436,7 @@ export default class GoTrueClient {
|
|
|
3357
3436
|
return this._returnResult({ data: { user: session.user }, error: null })
|
|
3358
3437
|
})
|
|
3359
3438
|
} catch (error) {
|
|
3360
|
-
await
|
|
3439
|
+
await removePKCEVerifier(this.storage, this.storageKey, flowId)
|
|
3361
3440
|
if (isAuthError(error)) {
|
|
3362
3441
|
return this._returnResult({ data: { user: null }, error })
|
|
3363
3442
|
}
|
|
@@ -3789,11 +3868,14 @@ export default class GoTrueClient {
|
|
|
3789
3868
|
if (callbackUrlType === 'pkce') {
|
|
3790
3869
|
this._debug('#_initialize()', 'begin', 'is PKCE flow', true)
|
|
3791
3870
|
if (!params.code) throw new AuthPKCEGrantCodeExchangeError('No code detected.')
|
|
3792
|
-
const { data, error } = await this._exchangeCodeForSession(params.code
|
|
3871
|
+
const { data, error } = await this._exchangeCodeForSession(params.code, {
|
|
3872
|
+
flowId: params[PKCE_FLOW_ID_PARAM],
|
|
3873
|
+
})
|
|
3793
3874
|
if (error) throw error
|
|
3794
3875
|
|
|
3795
3876
|
const url = new URL(window.location.href)
|
|
3796
3877
|
url.searchParams.delete('code')
|
|
3878
|
+
url.searchParams.delete(PKCE_FLOW_ID_PARAM)
|
|
3797
3879
|
|
|
3798
3880
|
window.history.replaceState(window.history.state, '', url.toString())
|
|
3799
3881
|
|
|
@@ -3897,12 +3979,24 @@ export default class GoTrueClient {
|
|
|
3897
3979
|
* Checks if the current URL and backing storage contain parameters given by a PKCE flow
|
|
3898
3980
|
*/
|
|
3899
3981
|
private async _isPKCECallback(params: { [parameter: string]: string }): Promise<boolean> {
|
|
3982
|
+
if (!params.code) {
|
|
3983
|
+
return false
|
|
3984
|
+
}
|
|
3985
|
+
|
|
3986
|
+
const flowId = validatePKCEFlowId(params[PKCE_FLOW_ID_PARAM])
|
|
3987
|
+
if (
|
|
3988
|
+
flowId &&
|
|
3989
|
+
(await getItemAsync(this.storage, pkceVerifierSlotKey(this.storageKey, flowId)))
|
|
3990
|
+
) {
|
|
3991
|
+
return true
|
|
3992
|
+
}
|
|
3993
|
+
|
|
3900
3994
|
const currentStorageContent = await getItemAsync(
|
|
3901
3995
|
this.storage,
|
|
3902
3996
|
`${this.storageKey}-code-verifier`
|
|
3903
3997
|
)
|
|
3904
3998
|
|
|
3905
|
-
return !!
|
|
3999
|
+
return !!currentStorageContent
|
|
3906
4000
|
}
|
|
3907
4001
|
|
|
3908
4002
|
/**
|
|
@@ -3963,6 +4057,9 @@ export default class GoTrueClient {
|
|
|
3963
4057
|
{ scope }: SignOut = { scope: 'global' }
|
|
3964
4058
|
): Promise<{ error: AuthError | null }> {
|
|
3965
4059
|
return await this._useSession(async (result) => {
|
|
4060
|
+
const removeCurrentSession = async () => {
|
|
4061
|
+
await this._removeSession()
|
|
4062
|
+
}
|
|
3966
4063
|
const { data, error: sessionError } = result
|
|
3967
4064
|
if (sessionError && !isAuthSessionMissingError(sessionError)) {
|
|
3968
4065
|
return this._returnResult({ error: sessionError })
|
|
@@ -3980,13 +4077,15 @@ export default class GoTrueClient {
|
|
|
3980
4077
|
isAuthSessionMissingError(error)
|
|
3981
4078
|
)
|
|
3982
4079
|
) {
|
|
4080
|
+
if (scope !== 'others') {
|
|
4081
|
+
await removeCurrentSession()
|
|
4082
|
+
}
|
|
3983
4083
|
return this._returnResult({ error })
|
|
3984
4084
|
}
|
|
3985
4085
|
}
|
|
3986
4086
|
}
|
|
3987
4087
|
if (scope !== 'others') {
|
|
3988
|
-
await
|
|
3989
|
-
await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`)
|
|
4088
|
+
await removeCurrentSession()
|
|
3990
4089
|
}
|
|
3991
4090
|
return this._returnResult({ error: null })
|
|
3992
4091
|
})
|
|
@@ -4247,7 +4346,18 @@ export default class GoTrueClient {
|
|
|
4247
4346
|
} catch (err) {
|
|
4248
4347
|
await this.stateChangeEmitters.get(id)?.callback('INITIAL_SESSION', null)
|
|
4249
4348
|
this._debug('INITIAL_SESSION', 'callback id', id, 'error', err)
|
|
4250
|
-
if (
|
|
4349
|
+
if (
|
|
4350
|
+
isAuthSessionMissingError(err) ||
|
|
4351
|
+
isAuthRetryableFetchError(err) ||
|
|
4352
|
+
(isAuthApiError(err) &&
|
|
4353
|
+
(err.code === 'refresh_token_not_found' ||
|
|
4354
|
+
err.code === 'refresh_token_already_used' ||
|
|
4355
|
+
err.code === 'session_expired'))
|
|
4356
|
+
) {
|
|
4357
|
+
// A missing session, a transient/aborted network failure (e.g. a
|
|
4358
|
+
// superseded page navigation cancelling the in-flight request), or
|
|
4359
|
+
// a dead refresh token (e.g. stale SSR cookies) is not an
|
|
4360
|
+
// application error — warn rather than surface it raw.
|
|
4251
4361
|
console.warn(err)
|
|
4252
4362
|
} else {
|
|
4253
4363
|
console.error(err)
|
|
@@ -4338,11 +4448,10 @@ export default class GoTrueClient {
|
|
|
4338
4448
|
> {
|
|
4339
4449
|
let codeChallenge: string | null = null
|
|
4340
4450
|
let codeChallengeMethod: string | null = null
|
|
4451
|
+
let flowId: string | null = null
|
|
4341
4452
|
|
|
4342
4453
|
if (this.flowType === 'pkce') {
|
|
4343
|
-
;[codeChallenge, codeChallengeMethod] = await
|
|
4344
|
-
this.storage,
|
|
4345
|
-
this.storageKey,
|
|
4454
|
+
;[codeChallenge, codeChallengeMethod, flowId] = await this._getCodeChallengeAndMethod(
|
|
4346
4455
|
true // isPasswordRecovery
|
|
4347
4456
|
)
|
|
4348
4457
|
}
|
|
@@ -4355,10 +4464,10 @@ export default class GoTrueClient {
|
|
|
4355
4464
|
gotrue_meta_security: { captcha_token: options.captchaToken },
|
|
4356
4465
|
},
|
|
4357
4466
|
headers: this.headers,
|
|
4358
|
-
redirectTo: options.redirectTo,
|
|
4467
|
+
redirectTo: this._maybeAppendFlowIdToRedirect(options.redirectTo, flowId),
|
|
4359
4468
|
})
|
|
4360
4469
|
} catch (error) {
|
|
4361
|
-
await
|
|
4470
|
+
await removePKCEVerifier(this.storage, this.storageKey, flowId)
|
|
4362
4471
|
if (isAuthError(error)) {
|
|
4363
4472
|
return this._returnResult({ data: null, error })
|
|
4364
4473
|
}
|
|
@@ -4460,7 +4569,8 @@ export default class GoTrueClient {
|
|
|
4460
4569
|
* {
|
|
4461
4570
|
* data: {
|
|
4462
4571
|
* provider: 'github',
|
|
4463
|
-
* url: <PROVIDER_URL_TO_REDIRECT_TO
|
|
4572
|
+
* url: <PROVIDER_URL_TO_REDIRECT_TO>,
|
|
4573
|
+
* flowId: <PKCE_FLOW_ID_OR_NULL>
|
|
4464
4574
|
* },
|
|
4465
4575
|
* error: null
|
|
4466
4576
|
* }
|
|
@@ -4475,11 +4585,12 @@ export default class GoTrueClient {
|
|
|
4475
4585
|
}
|
|
4476
4586
|
|
|
4477
4587
|
private async linkIdentityOAuth(credentials: SignInWithOAuthCredentials): Promise<OAuthResponse> {
|
|
4588
|
+
let flowId: string | null = null
|
|
4478
4589
|
try {
|
|
4479
4590
|
const { data, error } = await this._useSession(async (result) => {
|
|
4480
4591
|
const { data, error } = result
|
|
4481
4592
|
if (error) throw error
|
|
4482
|
-
const url:
|
|
4593
|
+
const { url, flowId: urlFlowId } = await this._getUrlForProvider(
|
|
4483
4594
|
`${this.url}/user/identities/authorize`,
|
|
4484
4595
|
credentials.provider,
|
|
4485
4596
|
{
|
|
@@ -4489,6 +4600,7 @@ export default class GoTrueClient {
|
|
|
4489
4600
|
skipBrowserRedirect: true,
|
|
4490
4601
|
}
|
|
4491
4602
|
)
|
|
4603
|
+
flowId = urlFlowId
|
|
4492
4604
|
return await _request(this.fetch, 'GET', url, {
|
|
4493
4605
|
headers: this.headers,
|
|
4494
4606
|
jwt: data.session?.access_token ?? undefined,
|
|
@@ -4499,12 +4611,15 @@ export default class GoTrueClient {
|
|
|
4499
4611
|
window.location.assign(data?.url)
|
|
4500
4612
|
}
|
|
4501
4613
|
return this._returnResult({
|
|
4502
|
-
data: { provider: credentials.provider, url: data?.url },
|
|
4614
|
+
data: { provider: credentials.provider, url: data?.url, flowId },
|
|
4503
4615
|
error: null,
|
|
4504
4616
|
})
|
|
4505
4617
|
} catch (error) {
|
|
4506
4618
|
if (isAuthError(error)) {
|
|
4507
|
-
return this._returnResult({
|
|
4619
|
+
return this._returnResult({
|
|
4620
|
+
data: { provider: credentials.provider, url: null, flowId },
|
|
4621
|
+
error,
|
|
4622
|
+
})
|
|
4508
4623
|
}
|
|
4509
4624
|
throw error
|
|
4510
4625
|
}
|
|
@@ -4552,7 +4667,7 @@ export default class GoTrueClient {
|
|
|
4552
4667
|
}
|
|
4553
4668
|
return this._returnResult({ data, error })
|
|
4554
4669
|
} catch (error) {
|
|
4555
|
-
await
|
|
4670
|
+
await removePKCEVerifier(this.storage, this.storageKey, null)
|
|
4556
4671
|
if (isAuthError(error)) {
|
|
4557
4672
|
return this._returnResult({ data: { user: null, session: null }, error })
|
|
4558
4673
|
}
|
|
@@ -4688,7 +4803,7 @@ export default class GoTrueClient {
|
|
|
4688
4803
|
skipBrowserRedirect?: boolean
|
|
4689
4804
|
}
|
|
4690
4805
|
) {
|
|
4691
|
-
const url
|
|
4806
|
+
const { url, flowId } = await this._getUrlForProvider(`${this.url}/authorize`, provider, {
|
|
4692
4807
|
redirectTo: options.redirectTo,
|
|
4693
4808
|
scopes: options.scopes,
|
|
4694
4809
|
queryParams: options.queryParams,
|
|
@@ -4701,7 +4816,7 @@ export default class GoTrueClient {
|
|
|
4701
4816
|
window.location.assign(url)
|
|
4702
4817
|
}
|
|
4703
4818
|
|
|
4704
|
-
return { data: { provider, url }, error: null }
|
|
4819
|
+
return { data: { provider, url, flowId }, error: null }
|
|
4705
4820
|
}
|
|
4706
4821
|
|
|
4707
4822
|
/**
|
|
@@ -4823,7 +4938,15 @@ export default class GoTrueClient {
|
|
|
4823
4938
|
} catch (err) {
|
|
4824
4939
|
this._debug(debugName, 'error', err)
|
|
4825
4940
|
|
|
4826
|
-
|
|
4941
|
+
if (isAuthRetryableFetchError(err)) {
|
|
4942
|
+
// Transient/aborted network failure during session recovery (e.g. a
|
|
4943
|
+
// superseded page navigation cancelling the in-flight request). Warn
|
|
4944
|
+
// rather than surface it raw; the session is untouched and recovery
|
|
4945
|
+
// will run again on the next load.
|
|
4946
|
+
console.warn(err)
|
|
4947
|
+
} else {
|
|
4948
|
+
console.error(err)
|
|
4949
|
+
}
|
|
4827
4950
|
return
|
|
4828
4951
|
} finally {
|
|
4829
4952
|
this._debug(debugName, 'end')
|
|
@@ -5011,6 +5134,20 @@ export default class GoTrueClient {
|
|
|
5011
5134
|
session: Session | null,
|
|
5012
5135
|
broadcast = true
|
|
5013
5136
|
) {
|
|
5137
|
+
if (this._pendingInitNotifications !== null && broadcast) {
|
|
5138
|
+
// We're inside initialize() before initializePromise has resolved, and
|
|
5139
|
+
// this notification originates from the init chain (_recoverAndRefresh),
|
|
5140
|
+
// which always fires with broadcast = true. Enqueue instead of firing so
|
|
5141
|
+
// that callbacks can safely call getSession() / getUser() without
|
|
5142
|
+
// deadlocking on initializePromise.
|
|
5143
|
+
//
|
|
5144
|
+
// Notifications with broadcast = false come from the incoming
|
|
5145
|
+
// BroadcastChannel handler (a cross-tab event), not the init chain, so
|
|
5146
|
+
// they are fired immediately to preserve multi-tab ordering.
|
|
5147
|
+
this._pendingInitNotifications.push({ event, session, broadcast })
|
|
5148
|
+
return
|
|
5149
|
+
}
|
|
5150
|
+
|
|
5014
5151
|
const debugName = `#_notifyAllSubscribers(${event})`
|
|
5015
5152
|
this._debug(debugName, 'begin', session, `broadcast = ${broadcast}`)
|
|
5016
5153
|
|
|
@@ -5051,7 +5188,6 @@ export default class GoTrueClient {
|
|
|
5051
5188
|
// _saveSession is always called whenever a new session has been acquired
|
|
5052
5189
|
// so we can safely suppress the warning returned by future getSession calls
|
|
5053
5190
|
this.suppressGetSessionWarning = true
|
|
5054
|
-
await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`)
|
|
5055
5191
|
// Create a shallow copy to work with, to avoid mutating the original session object if it's used elsewhere
|
|
5056
5192
|
const sessionToProcess = { ...session }
|
|
5057
5193
|
|
|
@@ -5101,7 +5237,7 @@ export default class GoTrueClient {
|
|
|
5101
5237
|
this.suppressGetSessionWarning = false
|
|
5102
5238
|
|
|
5103
5239
|
await removeItemAsync(this.storage, this.storageKey)
|
|
5104
|
-
await
|
|
5240
|
+
await removeAllPKCEVerifiers(this.storage, this.storageKey)
|
|
5105
5241
|
await removeItemAsync(this.storage, this.storageKey + '-user')
|
|
5106
5242
|
|
|
5107
5243
|
if (this.userStorage) {
|
|
@@ -5114,8 +5250,8 @@ export default class GoTrueClient {
|
|
|
5114
5250
|
/**
|
|
5115
5251
|
* Removes any registered visibilitychange callback.
|
|
5116
5252
|
*
|
|
5117
|
-
* {@
|
|
5118
|
-
* {@
|
|
5253
|
+
* {@link GoTrueClient.startAutoRefresh}
|
|
5254
|
+
* {@link GoTrueClient.stopAutoRefresh}
|
|
5119
5255
|
*/
|
|
5120
5256
|
private _removeVisibilityChangedCallback() {
|
|
5121
5257
|
this._debug('#_removeVisibilityChangedCallback()')
|
|
@@ -5133,7 +5269,7 @@ export default class GoTrueClient {
|
|
|
5133
5269
|
}
|
|
5134
5270
|
|
|
5135
5271
|
/**
|
|
5136
|
-
* This is the private implementation of {@link
|
|
5272
|
+
* This is the private implementation of {@link GoTrueClient.startAutoRefresh}. Use this
|
|
5137
5273
|
* within the library.
|
|
5138
5274
|
*/
|
|
5139
5275
|
private async _startAutoRefresh() {
|
|
@@ -5179,7 +5315,7 @@ export default class GoTrueClient {
|
|
|
5179
5315
|
}
|
|
5180
5316
|
|
|
5181
5317
|
/**
|
|
5182
|
-
* This is the private implementation of {@link
|
|
5318
|
+
* This is the private implementation of {@link GoTrueClient.stopAutoRefresh}. Use this
|
|
5183
5319
|
* within the library.
|
|
5184
5320
|
*/
|
|
5185
5321
|
private async _stopAutoRefresh() {
|
|
@@ -5220,7 +5356,7 @@ export default class GoTrueClient {
|
|
|
5220
5356
|
* platform's foreground indication mechanism and call these methods
|
|
5221
5357
|
* appropriately to conserve resources.
|
|
5222
5358
|
*
|
|
5223
|
-
* {@
|
|
5359
|
+
* {@link GoTrueClient.stopAutoRefresh}
|
|
5224
5360
|
*
|
|
5225
5361
|
* @category Auth
|
|
5226
5362
|
*
|
|
@@ -5255,7 +5391,7 @@ export default class GoTrueClient {
|
|
|
5255
5391
|
* If you call this method any managed visibility change callback will be
|
|
5256
5392
|
* removed and you must manage visibility changes on your own.
|
|
5257
5393
|
*
|
|
5258
|
-
* See {@link
|
|
5394
|
+
* See {@link GoTrueClient.startAutoRefresh} for more details.
|
|
5259
5395
|
*
|
|
5260
5396
|
* @category Auth
|
|
5261
5397
|
*
|
|
@@ -5525,19 +5661,23 @@ export default class GoTrueClient {
|
|
|
5525
5661
|
skipBrowserRedirect?: boolean
|
|
5526
5662
|
}
|
|
5527
5663
|
) {
|
|
5664
|
+
let redirectTo = options?.redirectTo
|
|
5665
|
+
let codeChallenge: string | null = null
|
|
5666
|
+
let codeChallengeMethod: string | null = null
|
|
5667
|
+
let flowId: string | null = null
|
|
5668
|
+
if (this.flowType === 'pkce') {
|
|
5669
|
+
;[codeChallenge, codeChallengeMethod, flowId] = await this._getCodeChallengeAndMethod()
|
|
5670
|
+
redirectTo = this._maybeAppendFlowIdToRedirect(redirectTo, flowId)
|
|
5671
|
+
}
|
|
5672
|
+
|
|
5528
5673
|
const urlParams: string[] = [`provider=${encodeURIComponent(provider)}`]
|
|
5529
|
-
if (
|
|
5530
|
-
urlParams.push(`redirect_to=${encodeURIComponent(
|
|
5674
|
+
if (redirectTo) {
|
|
5675
|
+
urlParams.push(`redirect_to=${encodeURIComponent(redirectTo)}`)
|
|
5531
5676
|
}
|
|
5532
5677
|
if (options?.scopes) {
|
|
5533
5678
|
urlParams.push(`scopes=${encodeURIComponent(options.scopes)}`)
|
|
5534
5679
|
}
|
|
5535
|
-
if (
|
|
5536
|
-
const [codeChallenge, codeChallengeMethod] = await getCodeChallengeAndMethod(
|
|
5537
|
-
this.storage,
|
|
5538
|
-
this.storageKey
|
|
5539
|
-
)
|
|
5540
|
-
|
|
5680
|
+
if (codeChallenge != null && codeChallengeMethod != null) {
|
|
5541
5681
|
const flowParams = new URLSearchParams({
|
|
5542
5682
|
code_challenge: `${encodeURIComponent(codeChallenge)}`,
|
|
5543
5683
|
code_challenge_method: `${encodeURIComponent(codeChallengeMethod)}`,
|
|
@@ -5552,7 +5692,44 @@ export default class GoTrueClient {
|
|
|
5552
5692
|
urlParams.push(`skip_http_redirect=${options.skipBrowserRedirect}`)
|
|
5553
5693
|
}
|
|
5554
5694
|
|
|
5555
|
-
return `${url}?${urlParams.join('&')}
|
|
5695
|
+
return { url: `${url}?${urlParams.join('&')}`, flowId }
|
|
5696
|
+
}
|
|
5697
|
+
|
|
5698
|
+
/**
|
|
5699
|
+
* Appends the reserved flow id parameter to a redirect URL so the callback
|
|
5700
|
+
* can be matched to the verifier stored for its flow. Opt-in via
|
|
5701
|
+
* `experimental.appendPkceFlowIdToRedirects`: redirect URLs are validated
|
|
5702
|
+
* against the project's allow list including the query string, so an extra
|
|
5703
|
+
* parameter can stop exact (non-wildcard) entries from matching.
|
|
5704
|
+
*/
|
|
5705
|
+
private _maybeAppendFlowIdToRedirect(
|
|
5706
|
+
redirectTo: string | undefined,
|
|
5707
|
+
flowId: string | null
|
|
5708
|
+
): string | undefined {
|
|
5709
|
+
if (!redirectTo || !flowId || !this.experimental.appendPkceFlowIdToRedirects) {
|
|
5710
|
+
return redirectTo ?? undefined
|
|
5711
|
+
}
|
|
5712
|
+
return appendFlowIdToRedirectTo(redirectTo, flowId)
|
|
5713
|
+
}
|
|
5714
|
+
|
|
5715
|
+
/**
|
|
5716
|
+
* Generates and stores a PKCE challenge/verifier pair for a new flow,
|
|
5717
|
+
* logging any pending verifier the bounded slot ring evicts.
|
|
5718
|
+
*/
|
|
5719
|
+
private async _getCodeChallengeAndMethod(
|
|
5720
|
+
isPasswordRecovery = false
|
|
5721
|
+
): Promise<[string, string, string]> {
|
|
5722
|
+
return getCodeChallengeAndMethod(
|
|
5723
|
+
this.storage,
|
|
5724
|
+
this.storageKey,
|
|
5725
|
+
isPasswordRecovery,
|
|
5726
|
+
(evictedFlowId) =>
|
|
5727
|
+
this._debug(
|
|
5728
|
+
'#_getCodeChallengeAndMethod()',
|
|
5729
|
+
'evicted oldest pending PKCE verifier slot',
|
|
5730
|
+
evictedFlowId
|
|
5731
|
+
)
|
|
5732
|
+
)
|
|
5556
5733
|
}
|
|
5557
5734
|
|
|
5558
5735
|
private async _unenroll(params: MFAUnenrollParams): Promise<AuthMFAUnenrollResponse> {
|
|
@@ -5577,7 +5754,7 @@ export default class GoTrueClient {
|
|
|
5577
5754
|
}
|
|
5578
5755
|
|
|
5579
5756
|
/**
|
|
5580
|
-
* {@
|
|
5757
|
+
* {@link GoTrueMFAApi#enroll}
|
|
5581
5758
|
*/
|
|
5582
5759
|
private async _enroll(params: MFAEnrollTOTPParams): Promise<AuthMFAEnrollTOTPResponse>
|
|
5583
5760
|
private async _enroll(params: MFAEnrollPhoneParams): Promise<AuthMFAEnrollPhoneResponse>
|
|
@@ -5624,7 +5801,7 @@ export default class GoTrueClient {
|
|
|
5624
5801
|
}
|
|
5625
5802
|
|
|
5626
5803
|
/**
|
|
5627
|
-
* {@
|
|
5804
|
+
* {@link GoTrueMFAApi#verify}
|
|
5628
5805
|
*/
|
|
5629
5806
|
private async _verify(params: MFAVerifyTOTPParams): Promise<AuthMFAVerifyResponse>
|
|
5630
5807
|
private async _verify(params: MFAVerifyPhoneParams): Promise<AuthMFAVerifyResponse>
|
|
@@ -5713,7 +5890,7 @@ export default class GoTrueClient {
|
|
|
5713
5890
|
}
|
|
5714
5891
|
|
|
5715
5892
|
/**
|
|
5716
|
-
* {@
|
|
5893
|
+
* {@link GoTrueMFAApi#challenge}
|
|
5717
5894
|
*/
|
|
5718
5895
|
private async _challenge(
|
|
5719
5896
|
params: MFAChallengeTOTPParams
|
|
@@ -5808,7 +5985,7 @@ export default class GoTrueClient {
|
|
|
5808
5985
|
}
|
|
5809
5986
|
|
|
5810
5987
|
/**
|
|
5811
|
-
* {@
|
|
5988
|
+
* {@link GoTrueMFAApi#challengeAndVerify}
|
|
5812
5989
|
*/
|
|
5813
5990
|
private async _challengeAndVerify(
|
|
5814
5991
|
params: MFAChallengeAndVerifyParams
|
|
@@ -5828,7 +6005,7 @@ export default class GoTrueClient {
|
|
|
5828
6005
|
}
|
|
5829
6006
|
|
|
5830
6007
|
/**
|
|
5831
|
-
* {@
|
|
6008
|
+
* {@link GoTrueMFAApi#listFactors}
|
|
5832
6009
|
*/
|
|
5833
6010
|
private async _listFactors(): Promise<AuthMFAListFactorsResponse> {
|
|
5834
6011
|
const {
|
|
@@ -5861,7 +6038,7 @@ export default class GoTrueClient {
|
|
|
5861
6038
|
}
|
|
5862
6039
|
|
|
5863
6040
|
/**
|
|
5864
|
-
* {@
|
|
6041
|
+
* {@link GoTrueMFAApi#getAuthenticatorAssuranceLevel}
|
|
5865
6042
|
*/
|
|
5866
6043
|
private async _getAuthenticatorAssuranceLevel(
|
|
5867
6044
|
jwt?: string
|
|
@@ -6209,15 +6386,15 @@ export default class GoTrueClient {
|
|
|
6209
6386
|
* Extracts the JWT claims present in the access token by first verifying the
|
|
6210
6387
|
* JWT against the server's JSON Web Key Set endpoint
|
|
6211
6388
|
* `/.well-known/jwks.json` which is often cached, resulting in significantly
|
|
6212
|
-
* faster responses. Prefer this method over {@link
|
|
6389
|
+
* faster responses. Prefer this method over {@link GoTrueClient.getUser} which always
|
|
6213
6390
|
* sends a request to the Auth server for each JWT.
|
|
6214
6391
|
*
|
|
6215
6392
|
* If the project is not using an asymmetric JWT signing key (like ECC or
|
|
6216
|
-
* RSA) it always sends a request to the Auth server (similar to
|
|
6217
|
-
*
|
|
6393
|
+
* RSA) it always sends a request to the Auth server (similar to
|
|
6394
|
+
* {@link GoTrueClient.getUser}) to verify the JWT.
|
|
6218
6395
|
*
|
|
6219
6396
|
* @param jwt An optional specific JWT you wish to verify, not the one you
|
|
6220
|
-
* can obtain from {@link
|
|
6397
|
+
* can obtain from {@link GoTrueClient.getSession}.
|
|
6221
6398
|
* @param options Various additional options that allow you to customize the
|
|
6222
6399
|
* behavior of this method.
|
|
6223
6400
|
*
|