@atproto/oauth-client 0.5.14 → 0.6.1
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 +20 -0
- package/dist/oauth-authorization-server-metadata-resolver.d.ts +1 -1
- package/dist/oauth-authorization-server-metadata-resolver.d.ts.map +1 -1
- package/dist/oauth-authorization-server-metadata-resolver.js +1 -1
- package/dist/oauth-authorization-server-metadata-resolver.js.map +1 -1
- package/dist/oauth-client.d.ts +7 -8
- package/dist/oauth-client.d.ts.map +1 -1
- package/dist/oauth-client.js +27 -26
- package/dist/oauth-client.js.map +1 -1
- package/dist/oauth-protected-resource-metadata-resolver.d.ts +3 -3
- package/dist/oauth-protected-resource-metadata-resolver.d.ts.map +1 -1
- package/dist/oauth-protected-resource-metadata-resolver.js +4 -0
- package/dist/oauth-protected-resource-metadata-resolver.js.map +1 -1
- package/dist/oauth-resolver.d.ts +1 -1
- package/dist/oauth-resolver.d.ts.map +1 -1
- package/dist/oauth-resolver.js +3 -0
- package/dist/oauth-resolver.js.map +1 -1
- package/dist/oauth-server-factory.d.ts +1 -1
- package/dist/oauth-server-factory.d.ts.map +1 -1
- package/dist/oauth-server-factory.js +0 -7
- package/dist/oauth-server-factory.js.map +1 -1
- package/dist/oauth-session.d.ts.map +1 -1
- package/dist/oauth-session.js +1 -4
- package/dist/oauth-session.js.map +1 -1
- package/dist/session-getter.d.ts +16 -21
- package/dist/session-getter.d.ts.map +1 -1
- package/dist/session-getter.js +65 -60
- package/dist/session-getter.js.map +1 -1
- package/dist/state-store.d.ts +13 -3
- package/dist/state-store.d.ts.map +1 -1
- package/dist/state-store.js.map +1 -1
- package/dist/util.d.ts +0 -10
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +1 -64
- package/dist/util.js.map +1 -1
- package/package.json +11 -11
- package/src/oauth-authorization-server-metadata-resolver.ts +2 -2
- package/src/oauth-client.ts +47 -50
- package/src/oauth-protected-resource-metadata-resolver.ts +9 -4
- package/src/oauth-resolver.ts +5 -1
- package/src/oauth-server-factory.ts +2 -16
- package/src/oauth-session.ts +1 -4
- package/src/session-getter.ts +85 -102
- package/src/state-store.ts +13 -3
- package/src/util.ts +0 -67
package/dist/util.js
CHANGED
|
@@ -1,17 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
6
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
|
-
};
|
|
8
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
-
};
|
|
13
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.
|
|
3
|
+
exports.ifString = void 0;
|
|
15
4
|
exports.contentMime = contentMime;
|
|
16
5
|
exports.combineSignals = combineSignals;
|
|
17
6
|
const ifString = (v) => (typeof v === 'string' ? v : undefined);
|
|
@@ -19,58 +8,6 @@ exports.ifString = ifString;
|
|
|
19
8
|
function contentMime(headers) {
|
|
20
9
|
return headers.get('content-type')?.split(';')[0].trim();
|
|
21
10
|
}
|
|
22
|
-
/**
|
|
23
|
-
* Ponyfill for `CustomEvent` constructor.
|
|
24
|
-
*/
|
|
25
|
-
exports.CustomEvent = globalThis.CustomEvent ??
|
|
26
|
-
(() => {
|
|
27
|
-
var _CustomEvent_detail;
|
|
28
|
-
class CustomEvent extends Event {
|
|
29
|
-
constructor(type, options) {
|
|
30
|
-
if (!arguments.length)
|
|
31
|
-
throw new TypeError('type argument is required');
|
|
32
|
-
super(type, options);
|
|
33
|
-
_CustomEvent_detail.set(this, void 0);
|
|
34
|
-
__classPrivateFieldSet(this, _CustomEvent_detail, options?.detail ?? null, "f");
|
|
35
|
-
}
|
|
36
|
-
get detail() {
|
|
37
|
-
return __classPrivateFieldGet(this, _CustomEvent_detail, "f");
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
_CustomEvent_detail = new WeakMap();
|
|
41
|
-
Object.defineProperties(CustomEvent.prototype, {
|
|
42
|
-
[Symbol.toStringTag]: {
|
|
43
|
-
writable: false,
|
|
44
|
-
enumerable: false,
|
|
45
|
-
configurable: true,
|
|
46
|
-
value: 'CustomEvent',
|
|
47
|
-
},
|
|
48
|
-
detail: {
|
|
49
|
-
enumerable: true,
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
return CustomEvent;
|
|
53
|
-
})();
|
|
54
|
-
class CustomEventTarget {
|
|
55
|
-
constructor() {
|
|
56
|
-
Object.defineProperty(this, "eventTarget", {
|
|
57
|
-
enumerable: true,
|
|
58
|
-
configurable: true,
|
|
59
|
-
writable: true,
|
|
60
|
-
value: new EventTarget()
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
addEventListener(type, callback, options) {
|
|
64
|
-
this.eventTarget.addEventListener(type, callback, options);
|
|
65
|
-
}
|
|
66
|
-
removeEventListener(type, callback, options) {
|
|
67
|
-
this.eventTarget.removeEventListener(type, callback, options);
|
|
68
|
-
}
|
|
69
|
-
dispatchCustomEvent(type, detail, init) {
|
|
70
|
-
return this.eventTarget.dispatchEvent(new exports.CustomEvent(type, { ...init, detail }));
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
exports.CustomEventTarget = CustomEventTarget;
|
|
74
11
|
function combineSignals(signals) {
|
|
75
12
|
const controller = new DisposableAbortController();
|
|
76
13
|
const onAbort = function (_event) {
|
package/dist/util.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;;AAKA,kCAEC;AAED,wCA0BC;AAhCM,MAAM,QAAQ,GAAG,CAAI,CAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;AAA/D,QAAA,QAAQ,YAAuD;AAE5E,SAAgB,WAAW,CAAC,OAAgB;IAC1C,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAA;AAC3D,CAAC;AAED,SAAgB,cAAc,CAC5B,OAA6C;IAE7C,MAAM,UAAU,GAAG,IAAI,yBAAyB,EAAE,CAAA;IAElD,MAAM,OAAO,GAAG,UAA6B,MAAa;QACxD,MAAM,MAAM,GAAG,IAAI,KAAK,CAAC,4BAA4B,EAAE;YACrD,KAAK,EAAE,IAAI,CAAC,MAAM;SACnB,CAAC,CAAA;QAEF,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAC1B,CAAC,CAAA;IAED,IAAI,CAAC;QACH,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,IAAI,GAAG,EAAE,CAAC;gBACR,GAAG,CAAC,cAAc,EAAE,CAAA;gBACpB,GAAG,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAA;YACvE,CAAC;QACH,CAAC;QAED,OAAO,UAAU,CAAA;IACnB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACrB,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,yBAA0B,SAAQ,eAAe;IACrD,CAAC,MAAM,CAAC,OAAO,CAAC;QACd,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC,CAAA;IACvD,CAAC;CACF","sourcesContent":["export type Awaitable<T> = T | PromiseLike<T>\nexport type Simplify<T> = { [K in keyof T]: T[K] } & NonNullable<unknown>\n\nexport const ifString = <V>(v: V) => (typeof v === 'string' ? v : undefined)\n\nexport function contentMime(headers: Headers): string | undefined {\n return headers.get('content-type')?.split(';')[0]!.trim()\n}\n\nexport function combineSignals(\n signals: readonly (AbortSignal | undefined)[],\n): AbortController & Disposable {\n const controller = new DisposableAbortController()\n\n const onAbort = function (this: AbortSignal, _event: Event) {\n const reason = new Error('This operation was aborted', {\n cause: this.reason,\n })\n\n controller.abort(reason)\n }\n\n try {\n for (const sig of signals) {\n if (sig) {\n sig.throwIfAborted()\n sig.addEventListener('abort', onAbort, { signal: controller.signal })\n }\n }\n\n return controller\n } catch (err) {\n controller.abort(err)\n throw err\n }\n}\n\n/**\n * Allows using {@link AbortController} with the `using` keyword, in order to\n * automatically abort them once the execution block ends.\n */\nclass DisposableAbortController extends AbortController implements Disposable {\n [Symbol.dispose]() {\n this.abort(new Error('AbortController was disposed'))\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/oauth-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "OAuth client for ATPROTO PDS. This package serves as common base for environment-specific implementations (NodeJS, Browser, React-Native).",
|
|
6
6
|
"keywords": [
|
|
@@ -28,16 +28,16 @@
|
|
|
28
28
|
"core-js": "^3",
|
|
29
29
|
"multiformats": "^9.9.0",
|
|
30
30
|
"zod": "^3.23.8",
|
|
31
|
-
"@atproto-labs/did-resolver": "0.2.6",
|
|
32
|
-
"@atproto-labs/fetch": "0.2.3",
|
|
33
|
-
"@atproto-labs/handle-resolver": "0.3.6",
|
|
34
|
-
"@atproto-labs/identity-resolver": "0.3.6",
|
|
35
|
-
"@atproto-labs/simple-store": "0.3.0",
|
|
36
|
-
"@atproto-labs/simple-store-memory": "0.1.4",
|
|
37
|
-
"@atproto/did": "0.3.0",
|
|
38
|
-
"@atproto/jwk": "0.6.0",
|
|
39
|
-
"@atproto/oauth-types": "0.6.
|
|
40
|
-
"@atproto/xrpc": "0.7.7"
|
|
31
|
+
"@atproto-labs/did-resolver": "^0.2.6",
|
|
32
|
+
"@atproto-labs/fetch": "^0.2.3",
|
|
33
|
+
"@atproto-labs/handle-resolver": "^0.3.6",
|
|
34
|
+
"@atproto-labs/identity-resolver": "^0.3.6",
|
|
35
|
+
"@atproto-labs/simple-store": "^0.3.0",
|
|
36
|
+
"@atproto-labs/simple-store-memory": "^0.1.4",
|
|
37
|
+
"@atproto/did": "^0.3.0",
|
|
38
|
+
"@atproto/jwk": "^0.6.0",
|
|
39
|
+
"@atproto/oauth-types": "^0.6.3",
|
|
40
|
+
"@atproto/xrpc": "^0.7.7"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"typescript": "^5.6.3"
|
|
@@ -49,10 +49,10 @@ export class OAuthAuthorizationServerMetadataResolver extends CachedGetter<
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
async get(
|
|
52
|
-
input: string,
|
|
52
|
+
input: URL | string,
|
|
53
53
|
options?: GetCachedOptions,
|
|
54
54
|
): Promise<OAuthAuthorizationServerMetadata> {
|
|
55
|
-
const issuer = oauthIssuerIdentifierSchema.parse(input)
|
|
55
|
+
const issuer = oauthIssuerIdentifierSchema.parse(String(input))
|
|
56
56
|
if (!this.allowHttpIssuer && issuer.startsWith('http:')) {
|
|
57
57
|
throw new TypeError(
|
|
58
58
|
'Unsecure issuer URL protocol only allowed in development and test environments',
|
package/src/oauth-client.ts
CHANGED
|
@@ -42,36 +42,38 @@ import { OAuthSession } from './oauth-session.js'
|
|
|
42
42
|
import { RuntimeImplementation } from './runtime-implementation.js'
|
|
43
43
|
import { Runtime } from './runtime.js'
|
|
44
44
|
import {
|
|
45
|
-
SessionEventMap,
|
|
46
45
|
SessionGetter,
|
|
46
|
+
SessionHooks,
|
|
47
47
|
SessionStore,
|
|
48
|
+
isExpectedSessionError,
|
|
48
49
|
} from './session-getter.js'
|
|
49
50
|
import { InternalStateData, StateStore } from './state-store.js'
|
|
50
51
|
import { AuthorizeOptions, CallbackOptions, ClientMetadata } from './types.js'
|
|
51
|
-
import { CustomEventTarget } from './util.js'
|
|
52
52
|
import { validateClientMetadata } from './validate-client-metadata.js'
|
|
53
53
|
|
|
54
54
|
// Export all types needed to construct OAuthClientOptions
|
|
55
|
-
export {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
55
|
+
export type {
|
|
56
|
+
AuthorizationServerMetadataCache,
|
|
57
|
+
CreateIdentityResolverOptions,
|
|
58
|
+
DidCache,
|
|
59
|
+
DpopNonceCache,
|
|
60
|
+
Fetch,
|
|
61
|
+
HandleCache,
|
|
62
|
+
HandleResolver,
|
|
63
|
+
InternalStateData,
|
|
64
|
+
OAuthClientMetadata,
|
|
65
|
+
OAuthClientMetadataInput,
|
|
66
|
+
OAuthResponseMode,
|
|
67
|
+
ProtectedResourceMetadataCache,
|
|
68
|
+
RuntimeImplementation,
|
|
69
|
+
SessionHooks,
|
|
70
|
+
SessionStore,
|
|
71
|
+
StateStore,
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
export
|
|
74
|
+
export { Key, Keyset }
|
|
75
|
+
|
|
76
|
+
export type OAuthClientOptions = {
|
|
75
77
|
// Config
|
|
76
78
|
responseMode: OAuthResponseMode
|
|
77
79
|
clientMetadata: Readonly<OAuthClientMetadataInput>
|
|
@@ -102,9 +104,8 @@ export type OAuthClientOptions = CreateIdentityResolverOptions & {
|
|
|
102
104
|
// Services
|
|
103
105
|
runtimeImplementation: RuntimeImplementation
|
|
104
106
|
fetch?: Fetch
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export type OAuthClientEventMap = SessionEventMap
|
|
107
|
+
} & CreateIdentityResolverOptions &
|
|
108
|
+
SessionHooks
|
|
108
109
|
|
|
109
110
|
export type OAuthClientFetchMetadataOptions = {
|
|
110
111
|
clientId: OAuthClientIdDiscoverable
|
|
@@ -112,7 +113,7 @@ export type OAuthClientFetchMetadataOptions = {
|
|
|
112
113
|
signal?: AbortSignal
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
export class OAuthClient
|
|
116
|
+
export class OAuthClient {
|
|
116
117
|
static async fetchMetadata({
|
|
117
118
|
clientId,
|
|
118
119
|
fetch = globalThis.fetch,
|
|
@@ -181,8 +182,6 @@ export class OAuthClient extends CustomEventTarget<OAuthClientEventMap> {
|
|
|
181
182
|
keyset,
|
|
182
183
|
} = options
|
|
183
184
|
|
|
184
|
-
super()
|
|
185
|
-
|
|
186
185
|
this.keyset = keyset
|
|
187
186
|
? keyset instanceof Keyset
|
|
188
187
|
? keyset
|
|
@@ -215,21 +214,13 @@ export class OAuthClient extends CustomEventTarget<OAuthClientEventMap> {
|
|
|
215
214
|
dpopNonceCache,
|
|
216
215
|
)
|
|
217
216
|
|
|
217
|
+
this.stateStore = stateStore
|
|
218
218
|
this.sessionGetter = new SessionGetter(
|
|
219
219
|
sessionStore,
|
|
220
220
|
this.serverFactory,
|
|
221
221
|
this.runtime,
|
|
222
|
+
options,
|
|
222
223
|
)
|
|
223
|
-
this.stateStore = stateStore
|
|
224
|
-
|
|
225
|
-
// Proxy sessionGetter events
|
|
226
|
-
for (const type of ['deleted', 'updated'] as const) {
|
|
227
|
-
this.sessionGetter.addEventListener(type, (event) => {
|
|
228
|
-
if (!this.dispatchCustomEvent(type, event.detail)) {
|
|
229
|
-
event.preventDefault()
|
|
230
|
-
}
|
|
231
|
-
})
|
|
232
|
-
}
|
|
233
224
|
}
|
|
234
225
|
|
|
235
226
|
// Exposed as public API for convenience
|
|
@@ -411,8 +402,7 @@ export class OAuthClient extends CustomEventTarget<OAuthClientEventMap> {
|
|
|
411
402
|
|
|
412
403
|
const server = await this.serverFactory.fromIssuer(
|
|
413
404
|
stateData.iss,
|
|
414
|
-
|
|
415
|
-
stateData.authMethod ?? 'legacy',
|
|
405
|
+
stateData.authMethod,
|
|
416
406
|
stateData.dpopKey,
|
|
417
407
|
)
|
|
418
408
|
|
|
@@ -446,6 +436,15 @@ export class OAuthClient extends CustomEventTarget<OAuthClientEventMap> {
|
|
|
446
436
|
stateData.verifier,
|
|
447
437
|
options?.redirect_uri ?? server.clientMetadata.redirect_uris[0],
|
|
448
438
|
)
|
|
439
|
+
|
|
440
|
+
// We revoke any existing session first to avoid leaving orphaned sessions
|
|
441
|
+
// on the AS.
|
|
442
|
+
try {
|
|
443
|
+
await this.revoke(tokenSet.sub)
|
|
444
|
+
} catch {
|
|
445
|
+
// No existing session, or failed to get it. This is fine.
|
|
446
|
+
}
|
|
447
|
+
|
|
449
448
|
try {
|
|
450
449
|
await this.sessionGetter.setStored(tokenSet.sub, {
|
|
451
450
|
dpopKey: stateData.dpopKey,
|
|
@@ -472,7 +471,7 @@ export class OAuthClient extends CustomEventTarget<OAuthClientEventMap> {
|
|
|
472
471
|
* Load a stored session. This will refresh the token only if needed (about to
|
|
473
472
|
* expire) by default.
|
|
474
473
|
*
|
|
475
|
-
* @
|
|
474
|
+
* @see {@link SessionGetter.restore}
|
|
476
475
|
*/
|
|
477
476
|
async restore(
|
|
478
477
|
sub: string,
|
|
@@ -481,11 +480,8 @@ export class OAuthClient extends CustomEventTarget<OAuthClientEventMap> {
|
|
|
481
480
|
// sub arg is lightly typed for convenience of library user
|
|
482
481
|
assertAtprotoDid(sub)
|
|
483
482
|
|
|
484
|
-
const {
|
|
485
|
-
|
|
486
|
-
authMethod = 'legacy',
|
|
487
|
-
tokenSet,
|
|
488
|
-
} = await this.sessionGetter.getSession(sub, refresh)
|
|
483
|
+
const { dpopKey, authMethod, tokenSet } =
|
|
484
|
+
await this.sessionGetter.getSession(sub, refresh)
|
|
489
485
|
|
|
490
486
|
try {
|
|
491
487
|
const server = await this.serverFactory.fromIssuer(
|
|
@@ -512,14 +508,15 @@ export class OAuthClient extends CustomEventTarget<OAuthClientEventMap> {
|
|
|
512
508
|
// sub arg is lightly typed for convenience of library user
|
|
513
509
|
assertAtprotoDid(sub)
|
|
514
510
|
|
|
515
|
-
const {
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
tokenSet,
|
|
519
|
-
} = await this.sessionGetter.get(sub, {
|
|
520
|
-
allowStale: true,
|
|
511
|
+
const res = await this.sessionGetter.getSession(sub, false).catch((err) => {
|
|
512
|
+
if (isExpectedSessionError(err)) return null
|
|
513
|
+
throw err
|
|
521
514
|
})
|
|
522
515
|
|
|
516
|
+
if (!res) return
|
|
517
|
+
|
|
518
|
+
const { dpopKey, authMethod, tokenSet } = res
|
|
519
|
+
|
|
523
520
|
// NOT using `;(await this.restore(sub, false)).signOut()` because we want
|
|
524
521
|
// the tokens to be deleted even if it was not possible to fetch the issuer
|
|
525
522
|
// data.
|
|
@@ -19,7 +19,7 @@ export type { GetCachedOptions, OAuthProtectedResourceMetadata }
|
|
|
19
19
|
|
|
20
20
|
export type ProtectedResourceMetadataCache = SimpleStore<
|
|
21
21
|
string,
|
|
22
|
-
OAuthProtectedResourceMetadata
|
|
22
|
+
OAuthProtectedResourceMetadata | null
|
|
23
23
|
>
|
|
24
24
|
|
|
25
25
|
export type OAuthProtectedResourceMetadataResolverConfig = {
|
|
@@ -31,7 +31,7 @@ export type OAuthProtectedResourceMetadataResolverConfig = {
|
|
|
31
31
|
*/
|
|
32
32
|
export class OAuthProtectedResourceMetadataResolver extends CachedGetter<
|
|
33
33
|
string,
|
|
34
|
-
OAuthProtectedResourceMetadata
|
|
34
|
+
OAuthProtectedResourceMetadata | null
|
|
35
35
|
> {
|
|
36
36
|
private readonly fetch: Fetch<unknown>
|
|
37
37
|
private readonly allowHttpResource: boolean
|
|
@@ -50,7 +50,7 @@ export class OAuthProtectedResourceMetadataResolver extends CachedGetter<
|
|
|
50
50
|
async get(
|
|
51
51
|
resource: string | URL,
|
|
52
52
|
options?: GetCachedOptions,
|
|
53
|
-
): Promise<OAuthProtectedResourceMetadata> {
|
|
53
|
+
): Promise<OAuthProtectedResourceMetadata | null> {
|
|
54
54
|
const { protocol, origin } = new URL(resource)
|
|
55
55
|
|
|
56
56
|
if (protocol !== 'https:' && protocol !== 'http:') {
|
|
@@ -71,7 +71,7 @@ export class OAuthProtectedResourceMetadataResolver extends CachedGetter<
|
|
|
71
71
|
private async fetchMetadata(
|
|
72
72
|
origin: string,
|
|
73
73
|
options?: GetCachedOptions,
|
|
74
|
-
): Promise<OAuthProtectedResourceMetadata> {
|
|
74
|
+
): Promise<OAuthProtectedResourceMetadata | null> {
|
|
75
75
|
const url = new URL(`/.well-known/oauth-protected-resource`, origin)
|
|
76
76
|
const request = new Request(url, {
|
|
77
77
|
signal: options?.signal,
|
|
@@ -82,6 +82,11 @@ export class OAuthProtectedResourceMetadataResolver extends CachedGetter<
|
|
|
82
82
|
|
|
83
83
|
const response = await this.fetch(request)
|
|
84
84
|
|
|
85
|
+
if (response.status === 404) {
|
|
86
|
+
await cancelBody(response, 'log')
|
|
87
|
+
return null
|
|
88
|
+
}
|
|
89
|
+
|
|
85
90
|
// https://www.rfc-editor.org/rfc/rfc9728.html#section-3.2
|
|
86
91
|
if (response.status !== 200) {
|
|
87
92
|
await cancelBody(response, 'log')
|
package/src/oauth-resolver.ts
CHANGED
|
@@ -112,7 +112,7 @@ export class OAuthResolver {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
public async getAuthorizationServerMetadata(
|
|
115
|
-
issuer: string,
|
|
115
|
+
issuer: string | URL,
|
|
116
116
|
options?: GetCachedOptions,
|
|
117
117
|
): Promise<OAuthAuthorizationServerMetadata> {
|
|
118
118
|
try {
|
|
@@ -135,6 +135,10 @@ export class OAuthResolver {
|
|
|
135
135
|
options,
|
|
136
136
|
)
|
|
137
137
|
|
|
138
|
+
if (!rsMetadata) {
|
|
139
|
+
return this.getAuthorizationServerMetadata(pdsUrl, options)
|
|
140
|
+
}
|
|
141
|
+
|
|
138
142
|
// ATPROTO requires one, and only one, authorization server entry
|
|
139
143
|
if (rsMetadata.authorization_servers?.length !== 1) {
|
|
140
144
|
throw new OAuthResolverError(
|
|
@@ -2,10 +2,7 @@ import { Key, Keyset } from '@atproto/jwk'
|
|
|
2
2
|
import { OAuthAuthorizationServerMetadata } from '@atproto/oauth-types'
|
|
3
3
|
import { Fetch } from '@atproto-labs/fetch'
|
|
4
4
|
import { GetCachedOptions } from './oauth-authorization-server-metadata-resolver.js'
|
|
5
|
-
import {
|
|
6
|
-
ClientAuthMethod,
|
|
7
|
-
negotiateClientAuthMethod,
|
|
8
|
-
} from './oauth-client-auth.js'
|
|
5
|
+
import { ClientAuthMethod } from './oauth-client-auth.js'
|
|
9
6
|
import { OAuthResolver } from './oauth-resolver.js'
|
|
10
7
|
import { DpopNonceCache, OAuthServerAgent } from './oauth-server-agent.js'
|
|
11
8
|
import { Runtime } from './runtime.js'
|
|
@@ -32,7 +29,7 @@ export class OAuthServerFactory {
|
|
|
32
29
|
*/
|
|
33
30
|
async fromIssuer(
|
|
34
31
|
issuer: string,
|
|
35
|
-
authMethod:
|
|
32
|
+
authMethod: ClientAuthMethod,
|
|
36
33
|
dpopKey: Key,
|
|
37
34
|
options?: GetCachedOptions,
|
|
38
35
|
) {
|
|
@@ -41,17 +38,6 @@ export class OAuthServerFactory {
|
|
|
41
38
|
options,
|
|
42
39
|
)
|
|
43
40
|
|
|
44
|
-
if (authMethod === 'legacy') {
|
|
45
|
-
// @NOTE Because we were previously not storing the authMethod in the
|
|
46
|
-
// session data, we provide a backwards compatible implementation by
|
|
47
|
-
// computing it here.
|
|
48
|
-
authMethod = negotiateClientAuthMethod(
|
|
49
|
-
serverMetadata,
|
|
50
|
-
this.clientMetadata,
|
|
51
|
-
this.keyset,
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
41
|
return this.fromMetadata(serverMetadata, authMethod, dpopKey)
|
|
56
42
|
}
|
|
57
43
|
|
package/src/oauth-session.ts
CHANGED
|
@@ -58,10 +58,7 @@ export class OAuthSession {
|
|
|
58
58
|
* if, and only if, they are (about to be) expired. Defaults to `undefined`.
|
|
59
59
|
*/
|
|
60
60
|
protected async getTokenSet(refresh: boolean | 'auto'): Promise<TokenSet> {
|
|
61
|
-
const { tokenSet } = await this.sessionGetter.
|
|
62
|
-
noCache: refresh === true,
|
|
63
|
-
allowStale: refresh === false,
|
|
64
|
-
})
|
|
61
|
+
const { tokenSet } = await this.sessionGetter.getSession(this.sub, refresh)
|
|
65
62
|
|
|
66
63
|
return tokenSet
|
|
67
64
|
}
|