@atproto/oauth-client 0.1.0 → 0.1.2-rc.0
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/CHANGELOG.md +23 -0
- package/README.md +165 -31
- package/dist/errors/token-invalid-error.d.ts +7 -0
- package/dist/errors/token-invalid-error.d.ts.map +1 -0
- package/dist/errors/token-invalid-error.js +16 -0
- package/dist/errors/token-invalid-error.js.map +1 -0
- package/dist/errors/token-refresh-error.d.ts +7 -0
- package/dist/errors/token-refresh-error.d.ts.map +1 -0
- package/dist/errors/token-refresh-error.js +16 -0
- package/dist/errors/token-refresh-error.js.map +1 -0
- package/dist/errors/token-revoked-error.d.ts +7 -0
- package/dist/errors/token-revoked-error.d.ts.map +1 -0
- package/dist/errors/token-revoked-error.js +16 -0
- package/dist/errors/token-revoked-error.js.map +1 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/lock.d.ts +2 -1
- package/dist/lock.d.ts.map +1 -1
- package/dist/lock.js +2 -2
- package/dist/lock.js.map +1 -1
- package/dist/oauth-agent.d.ts.map +1 -1
- package/dist/oauth-agent.js +14 -9
- package/dist/oauth-agent.js.map +1 -1
- package/dist/oauth-atp-agent.d.ts +11 -0
- package/dist/oauth-atp-agent.d.ts.map +1 -0
- package/dist/oauth-atp-agent.js +52 -0
- package/dist/oauth-atp-agent.js.map +1 -0
- package/dist/oauth-client.d.ts +254 -24
- package/dist/oauth-client.d.ts.map +1 -1
- package/dist/oauth-client.js +68 -9
- package/dist/oauth-client.js.map +1 -1
- package/dist/oauth-resolver.d.ts +5 -4
- package/dist/oauth-resolver.d.ts.map +1 -1
- package/dist/oauth-resolver.js.map +1 -1
- package/dist/oauth-server-agent.d.ts.map +1 -1
- package/dist/oauth-server-agent.js +85 -29
- package/dist/oauth-server-agent.js.map +1 -1
- package/dist/runtime-implementation.d.ts +10 -5
- package/dist/runtime-implementation.d.ts.map +1 -1
- package/dist/runtime.d.ts +3 -3
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +18 -12
- package/dist/runtime.js.map +1 -1
- package/dist/session-getter.d.ts +19 -0
- package/dist/session-getter.d.ts.map +1 -1
- package/dist/session-getter.js +134 -42
- package/dist/session-getter.js.map +1 -1
- package/dist/state-store.d.ts +11 -0
- package/dist/state-store.d.ts.map +1 -0
- package/dist/state-store.js +3 -0
- package/dist/state-store.js.map +1 -0
- package/dist/types.d.ts +3 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/util.d.ts +10 -3
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +43 -23
- package/dist/util.js.map +1 -1
- package/dist/validate-client-metadata.d.ts.map +1 -1
- package/dist/validate-client-metadata.js +17 -0
- package/dist/validate-client-metadata.js.map +1 -1
- package/package.json +10 -8
- package/src/errors/token-invalid-error.ts +9 -0
- package/src/{refresh-error.ts → errors/token-refresh-error.ts} +1 -1
- package/src/errors/token-revoked-error.ts +9 -0
- package/src/index.ts +12 -1
- package/src/lock.ts +3 -4
- package/src/oauth-agent.ts +20 -9
- package/src/oauth-atp-agent.ts +49 -0
- package/src/oauth-client.ts +117 -34
- package/src/oauth-resolver.ts +4 -4
- package/src/oauth-server-agent.ts +9 -9
- package/src/runtime-implementation.ts +19 -11
- package/src/runtime.ts +13 -17
- package/src/session-getter.ts +135 -71
- package/src/state-store.ts +12 -0
- package/src/types.ts +5 -2
- package/src/util.ts +63 -32
- package/src/validate-client-metadata.ts +18 -0
package/src/oauth-client.ts
CHANGED
@@ -11,17 +11,20 @@ import {
|
|
11
11
|
HandleResolver,
|
12
12
|
} from '@atproto-labs/handle-resolver'
|
13
13
|
import { IdentityResolver } from '@atproto-labs/identity-resolver'
|
14
|
-
import { SimpleStore } from '@atproto-labs/simple-store'
|
15
14
|
import { SimpleStoreMemory } from '@atproto-labs/simple-store-memory'
|
16
15
|
import { Key, Keyset } from '@atproto/jwk'
|
17
16
|
import {
|
17
|
+
OAuthClientIdDiscoverable,
|
18
18
|
OAuthClientMetadata,
|
19
19
|
OAuthClientMetadataInput,
|
20
|
+
oauthClientMetadataSchema,
|
20
21
|
OAuthResponseMode,
|
21
22
|
} from '@atproto/oauth-types'
|
22
23
|
|
23
24
|
import { FALLBACK_ALG } from './constants.js'
|
25
|
+
import { TokenRevokedError } from './errors/token-revoked-error.js'
|
24
26
|
import { OAuthAgent } from './oauth-agent.js'
|
27
|
+
import { OAuthAtpAgent } from './oauth-atp-agent.js'
|
25
28
|
import {
|
26
29
|
AuthorizationServerMetadataCache,
|
27
30
|
OAuthAuthorizationServerMetadataResolver,
|
@@ -36,30 +39,26 @@ import { DpopNonceCache, OAuthServerAgent } from './oauth-server-agent.js'
|
|
36
39
|
import { OAuthServerFactory } from './oauth-server-factory.js'
|
37
40
|
import { RuntimeImplementation } from './runtime-implementation.js'
|
38
41
|
import { Runtime } from './runtime.js'
|
39
|
-
import {
|
42
|
+
import {
|
43
|
+
SessionEventMap,
|
44
|
+
SessionGetter,
|
45
|
+
SessionStore,
|
46
|
+
} from './session-getter.js'
|
47
|
+
import { InternalStateData, StateStore } from './state-store.js'
|
40
48
|
import { AuthorizeOptions, ClientMetadata } from './types.js'
|
49
|
+
import { CustomEventTarget } from './util.js'
|
41
50
|
import { validateClientMetadata } from './validate-client-metadata.js'
|
42
51
|
|
43
|
-
export type InternalStateData = {
|
44
|
-
iss: string
|
45
|
-
nonce: string
|
46
|
-
dpopKey: Key
|
47
|
-
verifier?: string
|
48
|
-
|
49
|
-
/**
|
50
|
-
* @note This could be parametrized to be of any type. This wasn't done for
|
51
|
-
* the sake of simplicity but could be added in a later development.
|
52
|
-
*/
|
53
|
-
appState?: string
|
54
|
-
}
|
55
|
-
|
56
|
-
export type StateStore = SimpleStore<string, InternalStateData>
|
57
|
-
|
58
52
|
// Export all types needed to construct OAuthClientOptions
|
59
53
|
export type {
|
60
54
|
AuthorizationServerMetadataCache,
|
55
|
+
DidCache,
|
61
56
|
DpopNonceCache,
|
62
57
|
Fetch,
|
58
|
+
HandleCache,
|
59
|
+
HandleResolver,
|
60
|
+
InternalStateData,
|
61
|
+
Key,
|
63
62
|
Keyset,
|
64
63
|
OAuthClientMetadata,
|
65
64
|
OAuthClientMetadataInput,
|
@@ -67,6 +66,7 @@ export type {
|
|
67
66
|
ProtectedResourceMetadataCache,
|
68
67
|
RuntimeImplementation,
|
69
68
|
SessionStore,
|
69
|
+
StateStore,
|
70
70
|
}
|
71
71
|
|
72
72
|
export type OAuthClientOptions = {
|
@@ -91,7 +91,47 @@ export type OAuthClientOptions = {
|
|
91
91
|
fetch?: Fetch
|
92
92
|
}
|
93
93
|
|
94
|
-
export
|
94
|
+
export type OAuthClientEventMap = SessionEventMap
|
95
|
+
|
96
|
+
export type OAuthClientFetchMetadataOptions = {
|
97
|
+
clientId: OAuthClientIdDiscoverable
|
98
|
+
fetch?: Fetch
|
99
|
+
signal?: AbortSignal
|
100
|
+
}
|
101
|
+
|
102
|
+
export class OAuthClient extends CustomEventTarget<OAuthClientEventMap> {
|
103
|
+
static async fetchMetadata({
|
104
|
+
clientId,
|
105
|
+
fetch = globalThis.fetch,
|
106
|
+
signal,
|
107
|
+
}: OAuthClientFetchMetadataOptions) {
|
108
|
+
signal?.throwIfAborted()
|
109
|
+
|
110
|
+
const request = new Request(clientId, {
|
111
|
+
redirect: 'error',
|
112
|
+
signal: signal,
|
113
|
+
})
|
114
|
+
const response = await fetch(request)
|
115
|
+
|
116
|
+
if (response.status !== 200) {
|
117
|
+
response.body?.cancel?.()
|
118
|
+
throw new TypeError(`Failed to fetch client metadata: ${response.status}`)
|
119
|
+
}
|
120
|
+
|
121
|
+
// https://drafts.aaronpk.com/draft-parecki-oauth-client-id-metadata-document/draft-parecki-oauth-client-id-metadata-document.html#section-4.1
|
122
|
+
const mime = response.headers.get('content-type')?.split(';')[0].trim()
|
123
|
+
if (mime !== 'application/json') {
|
124
|
+
response.body?.cancel?.()
|
125
|
+
throw new TypeError(`Invalid client metadata content type: ${mime}`)
|
126
|
+
}
|
127
|
+
|
128
|
+
const json: unknown = await response.json()
|
129
|
+
|
130
|
+
signal?.throwIfAborted()
|
131
|
+
|
132
|
+
return oauthClientMetadataSchema.parse(json)
|
133
|
+
}
|
134
|
+
|
95
135
|
// Config
|
96
136
|
readonly clientMetadata: ClientMetadata
|
97
137
|
readonly responseMode: OAuthResponseMode
|
@@ -132,6 +172,8 @@ export class OAuthClient {
|
|
132
172
|
runtimeImplementation,
|
133
173
|
keyset,
|
134
174
|
}: OAuthClientOptions) {
|
175
|
+
super()
|
176
|
+
|
135
177
|
this.keyset = keyset
|
136
178
|
? keyset instanceof Keyset
|
137
179
|
? keyset
|
@@ -177,6 +219,15 @@ export class OAuthClient {
|
|
177
219
|
this.runtime,
|
178
220
|
)
|
179
221
|
this.stateStore = stateStore
|
222
|
+
|
223
|
+
// Proxy sessionGetter events
|
224
|
+
for (const type of ['deleted', 'updated'] as const) {
|
225
|
+
this.sessionGetter.addEventListener(type, (event) => {
|
226
|
+
if (!this.dispatchCustomEvent(type, event.detail)) {
|
227
|
+
event.preventDefault()
|
228
|
+
}
|
229
|
+
})
|
230
|
+
}
|
180
231
|
}
|
181
232
|
|
182
233
|
// Exposed as public API for convenience
|
@@ -194,10 +245,11 @@ export class OAuthClient {
|
|
194
245
|
return this.identityResolver.handleResolver
|
195
246
|
}
|
196
247
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
248
|
+
get jwks() {
|
249
|
+
return this.keyset?.publicJwks ?? ({ keys: [] as const } as const)
|
250
|
+
}
|
251
|
+
|
252
|
+
async authorize(input: string, options?: AuthorizeOptions): Promise<URL> {
|
201
253
|
const redirectUri =
|
202
254
|
options?.redirect_uri ?? this.clientMetadata.redirect_uris[0]
|
203
255
|
if (!this.clientMetadata.redirect_uris.includes(redirectUri)) {
|
@@ -239,7 +291,9 @@ export class OAuthClient {
|
|
239
291
|
code_challenge_method: pkce?.method,
|
240
292
|
nonce,
|
241
293
|
state,
|
242
|
-
login_hint: identity
|
294
|
+
login_hint: identity
|
295
|
+
? input // If input is a handle or a DID, use it as a login_hint
|
296
|
+
: undefined,
|
243
297
|
response_mode: this.responseMode,
|
244
298
|
response_type:
|
245
299
|
// Negotiate by using the order in the client metadata
|
@@ -297,8 +351,24 @@ export class OAuthClient {
|
|
297
351
|
)
|
298
352
|
}
|
299
353
|
|
354
|
+
/**
|
355
|
+
* This method allows the client to proactively revoke the request_uri it
|
356
|
+
* created through PAR.
|
357
|
+
*/
|
358
|
+
async abortRequest(authorizeUrl: URL) {
|
359
|
+
const requestUri = authorizeUrl.searchParams.get('request_uri')
|
360
|
+
if (!requestUri) return
|
361
|
+
|
362
|
+
// @NOTE This is not implemented here because, 1) the request server should
|
363
|
+
// invalidate the request_uri after some delay anyways, and 2) I am not sure
|
364
|
+
// that the revocation endpoint is even supposed to support this (and I
|
365
|
+
// don't want to spend the time checking now).
|
366
|
+
|
367
|
+
// @TODO investigate actual necessity & feasibility of this feature
|
368
|
+
}
|
369
|
+
|
300
370
|
async callback(params: URLSearchParams): Promise<{
|
301
|
-
agent:
|
371
|
+
agent: OAuthAtpAgent
|
302
372
|
state: string | null
|
303
373
|
}> {
|
304
374
|
const responseJwt = params.get('response')
|
@@ -409,7 +479,7 @@ export class OAuthClient {
|
|
409
479
|
*
|
410
480
|
* @param refresh See {@link SessionGetter.getSession}
|
411
481
|
*/
|
412
|
-
async restore(sub: string, refresh?: boolean): Promise<
|
482
|
+
async restore(sub: string, refresh?: boolean): Promise<OAuthAtpAgent> {
|
413
483
|
const { dpopKey, tokenSet } = await this.sessionGetter.getSession(
|
414
484
|
sub,
|
415
485
|
refresh,
|
@@ -424,17 +494,30 @@ export class OAuthClient {
|
|
424
494
|
}
|
425
495
|
|
426
496
|
async revoke(sub: string) {
|
427
|
-
const { dpopKey, tokenSet } = await this.sessionGetter.
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
const server = await this.serverFactory.fromIssuer(tokenSet.iss, dpopKey)
|
497
|
+
const { dpopKey, tokenSet } = await this.sessionGetter.getSession(
|
498
|
+
sub,
|
499
|
+
false,
|
500
|
+
)
|
432
501
|
|
433
|
-
await
|
434
|
-
|
502
|
+
// NOT using `;(await this.restore(sub, false)).signOut()` because we want
|
503
|
+
// the tokens to be deleted even if it was not possible to fetch the issuer
|
504
|
+
// data.
|
505
|
+
try {
|
506
|
+
const server = await this.serverFactory.fromIssuer(tokenSet.iss, dpopKey)
|
507
|
+
await server.revoke(tokenSet.access_token)
|
508
|
+
} finally {
|
509
|
+
await this.sessionGetter.delStored(sub, new TokenRevokedError(sub))
|
510
|
+
}
|
435
511
|
}
|
436
512
|
|
437
|
-
createAgent(server: OAuthServerAgent, sub: string):
|
438
|
-
|
513
|
+
createAgent(server: OAuthServerAgent, sub: string): OAuthAtpAgent {
|
514
|
+
const oauthAgent = new OAuthAgent(
|
515
|
+
server,
|
516
|
+
sub,
|
517
|
+
this.sessionGetter,
|
518
|
+
this.fetch,
|
519
|
+
)
|
520
|
+
|
521
|
+
return new OAuthAtpAgent(oauthAgent)
|
439
522
|
}
|
440
523
|
}
|
package/src/oauth-resolver.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import {
|
2
|
-
|
2
|
+
ResolveIdentityOptions,
|
3
3
|
IdentityResolver,
|
4
4
|
ResolvedIdentity,
|
5
5
|
} from '@atproto-labs/identity-resolver'
|
@@ -13,7 +13,7 @@ import {
|
|
13
13
|
import { OAuthProtectedResourceMetadataResolver } from './oauth-protected-resource-metadata-resolver.js'
|
14
14
|
|
15
15
|
export type { GetCachedOptions }
|
16
|
-
export type
|
16
|
+
export type ResolveOAuthOptions = GetCachedOptions & ResolveIdentityOptions
|
17
17
|
|
18
18
|
export class OAuthResolver {
|
19
19
|
constructor(
|
@@ -24,7 +24,7 @@ export class OAuthResolver {
|
|
24
24
|
|
25
25
|
public async resolveIdentity(
|
26
26
|
input: string,
|
27
|
-
options?:
|
27
|
+
options?: ResolveIdentityOptions,
|
28
28
|
): Promise<ResolvedIdentity> {
|
29
29
|
try {
|
30
30
|
return await this.identityResolver.resolve(input, options)
|
@@ -93,7 +93,7 @@ export class OAuthResolver {
|
|
93
93
|
|
94
94
|
public async resolve(
|
95
95
|
input: string,
|
96
|
-
options?:
|
96
|
+
options?: ResolveOAuthOptions,
|
97
97
|
): Promise<{
|
98
98
|
identity: ResolvedIdentity
|
99
99
|
metadata: OAuthAuthorizationServerMetadata
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Fetch, Json,
|
1
|
+
import { Fetch, Json, bindFetch, fetchJsonProcessor } from '@atproto-labs/fetch'
|
2
2
|
import { SimpleStore } from '@atproto-labs/simple-store'
|
3
3
|
import { Key, Keyset, SignedJwt } from '@atproto/jwk'
|
4
4
|
import {
|
@@ -14,13 +14,13 @@ import {
|
|
14
14
|
} from '@atproto/oauth-types'
|
15
15
|
|
16
16
|
import { FALLBACK_ALG } from './constants.js'
|
17
|
+
import { TokenRefreshError } from './errors/token-refresh-error.js'
|
17
18
|
import { dpopFetchWrapper } from './fetch-dpop.js'
|
18
19
|
import { OAuthResolver } from './oauth-resolver.js'
|
19
20
|
import { OAuthResponseError } from './oauth-response-error.js'
|
20
|
-
import { RefreshError } from './refresh-error.js'
|
21
21
|
import { Runtime } from './runtime.js'
|
22
22
|
import { ClientMetadata } from './types.js'
|
23
|
-
import {
|
23
|
+
import { timeoutSignal } from './util.js'
|
24
24
|
|
25
25
|
export type TokenSet = {
|
26
26
|
iss: string
|
@@ -89,7 +89,7 @@ export class OAuthServerAgent {
|
|
89
89
|
|
90
90
|
async refresh(tokenSet: TokenSet): Promise<TokenSet> {
|
91
91
|
if (!tokenSet.refresh_token) {
|
92
|
-
throw new
|
92
|
+
throw new TokenRefreshError(tokenSet.sub, 'No refresh token available')
|
93
93
|
}
|
94
94
|
|
95
95
|
const tokenResponse = await this.request('token', {
|
@@ -99,13 +99,13 @@ export class OAuthServerAgent {
|
|
99
99
|
|
100
100
|
try {
|
101
101
|
if (tokenSet.sub !== tokenResponse.sub) {
|
102
|
-
throw new
|
102
|
+
throw new TokenRefreshError(
|
103
103
|
tokenSet.sub,
|
104
104
|
`Unexpected "sub" in token response (${tokenResponse.sub})`,
|
105
105
|
)
|
106
106
|
}
|
107
107
|
if (tokenSet.iss !== this.serverMetadata.issuer) {
|
108
|
-
throw new
|
108
|
+
throw new TokenRefreshError(tokenSet.sub, 'Issuer mismatch')
|
109
109
|
}
|
110
110
|
|
111
111
|
return this.processTokenResponse(tokenResponse)
|
@@ -132,9 +132,9 @@ export class OAuthServerAgent {
|
|
132
132
|
if (!sub) throw new TypeError(`Missing "sub" in token response`)
|
133
133
|
|
134
134
|
// @TODO (?) make timeout configurable
|
135
|
-
|
136
|
-
|
137
|
-
)
|
135
|
+
using signal = timeoutSignal(10e3)
|
136
|
+
|
137
|
+
const resolved = await this.oauthResolver.resolve(sub, { signal })
|
138
138
|
|
139
139
|
if (resolved.metadata.issuer !== this.serverMetadata.issuer) {
|
140
140
|
// Best case scenario; the user switched PDS. Worst case scenario; a bad
|
@@ -1,17 +1,25 @@
|
|
1
1
|
import { Key } from '@atproto/jwk'
|
2
|
-
|
3
|
-
export type DigestAlgorithm = {
|
4
|
-
name: 'sha256' | 'sha384' | 'sha512'
|
5
|
-
}
|
2
|
+
import { Awaitable } from './util.js'
|
6
3
|
|
7
4
|
export type { Key }
|
5
|
+
export type RuntimeKeyFactory = (algs: string[]) => Key | PromiseLike<Key>
|
6
|
+
|
7
|
+
export type RuntimeRandomValues = (length: number) => Awaitable<Uint8Array>
|
8
|
+
|
9
|
+
export type DigestAlgorithm = { name: 'sha256' | 'sha384' | 'sha512' }
|
10
|
+
export type RuntimeDigest = (
|
11
|
+
data: Uint8Array,
|
12
|
+
alg: DigestAlgorithm,
|
13
|
+
) => Awaitable<Uint8Array>
|
14
|
+
|
15
|
+
export type RuntimeLock = <T>(
|
16
|
+
name: string,
|
17
|
+
fn: () => Awaitable<T>,
|
18
|
+
) => Awaitable<T>
|
8
19
|
|
9
20
|
export interface RuntimeImplementation {
|
10
|
-
createKey
|
11
|
-
getRandomValues:
|
12
|
-
digest:
|
13
|
-
|
14
|
-
algorithm: DigestAlgorithm,
|
15
|
-
) => Uint8Array | PromiseLike<Uint8Array>
|
16
|
-
requestLock?: <T>(name: string, fn: () => T | PromiseLike<T>) => Promise<T>
|
21
|
+
createKey: RuntimeKeyFactory
|
22
|
+
getRandomValues: RuntimeRandomValues
|
23
|
+
digest: RuntimeDigest
|
24
|
+
requestLock?: RuntimeLock
|
17
25
|
}
|
package/src/runtime.ts
CHANGED
@@ -5,10 +5,22 @@ import { requestLocalLock } from './lock.js'
|
|
5
5
|
import {
|
6
6
|
DigestAlgorithm,
|
7
7
|
RuntimeImplementation,
|
8
|
+
RuntimeLock,
|
8
9
|
} from './runtime-implementation.js'
|
9
10
|
|
10
11
|
export class Runtime {
|
11
|
-
|
12
|
+
readonly hasImplementationLock: boolean
|
13
|
+
readonly usingLock: RuntimeLock
|
14
|
+
|
15
|
+
constructor(protected implementation: RuntimeImplementation) {
|
16
|
+
const { requestLock } = implementation
|
17
|
+
|
18
|
+
this.hasImplementationLock = requestLock != null
|
19
|
+
this.usingLock =
|
20
|
+
requestLock?.bind(implementation) ||
|
21
|
+
// Falling back to a local lock
|
22
|
+
requestLocalLock
|
23
|
+
}
|
12
24
|
|
13
25
|
public async generateKey(algs: string[]): Promise<Key> {
|
14
26
|
const algsSorted = Array.from(algs).sort(compareAlgos)
|
@@ -26,22 +38,6 @@ export class Runtime {
|
|
26
38
|
return base64url.baseEncode(bytes)
|
27
39
|
}
|
28
40
|
|
29
|
-
get hasLock() {
|
30
|
-
return !!this.implementation.requestLock
|
31
|
-
}
|
32
|
-
|
33
|
-
public async withLock<T>(
|
34
|
-
name: string,
|
35
|
-
fn: () => T | PromiseLike<T>,
|
36
|
-
): Promise<T> {
|
37
|
-
if (this.implementation.requestLock) {
|
38
|
-
return this.implementation.requestLock(name, fn)
|
39
|
-
} else {
|
40
|
-
// Falling back to a local lock
|
41
|
-
return requestLocalLock(name, fn)
|
42
|
-
}
|
43
|
-
}
|
44
|
-
|
45
41
|
public async validateIdTokenClaims(
|
46
42
|
token: string,
|
47
43
|
state: string,
|