@atproto/oauth-client 0.6.1 → 0.7.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 +28 -0
- package/dist/constants.js +1 -4
- package/dist/constants.js.map +1 -1
- package/dist/errors/auth-method-unsatisfiable-error.js +1 -5
- package/dist/errors/auth-method-unsatisfiable-error.js.map +1 -1
- package/dist/errors/token-invalid-error.js +2 -11
- package/dist/errors/token-invalid-error.js.map +1 -1
- package/dist/errors/token-refresh-error.js +2 -11
- package/dist/errors/token-refresh-error.js.map +1 -1
- package/dist/errors/token-revoked-error.js +2 -11
- package/dist/errors/token-revoked-error.js.map +1 -1
- package/dist/fetch-dpop.js +6 -9
- package/dist/fetch-dpop.js.map +1 -1
- package/dist/identity-resolver.js +1 -5
- package/dist/identity-resolver.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -44
- package/dist/index.js.map +1 -1
- package/dist/lock.js +1 -5
- package/dist/lock.js.map +1 -1
- package/dist/oauth-authorization-server-metadata-resolver.js +13 -29
- package/dist/oauth-authorization-server-metadata-resolver.js.map +1 -1
- package/dist/oauth-callback-error.js +3 -17
- package/dist/oauth-callback-error.js.map +1 -1
- package/dist/oauth-client-auth.js +13 -17
- package/dist/oauth-client-auth.js.map +1 -1
- package/dist/oauth-client.js +49 -111
- package/dist/oauth-client.js.map +1 -1
- package/dist/oauth-protected-resource-metadata-resolver.js +13 -29
- package/dist/oauth-protected-resource-metadata-resolver.js.map +1 -1
- package/dist/oauth-resolver-error.js +3 -7
- package/dist/oauth-resolver-error.js.map +1 -1
- package/dist/oauth-resolver.js +15 -34
- package/dist/oauth-resolver.js.map +1 -1
- package/dist/oauth-response-error.js +6 -32
- package/dist/oauth-response-error.js.map +1 -1
- package/dist/oauth-server-agent.js +23 -79
- package/dist/oauth-server-agent.js.map +1 -1
- package/dist/oauth-server-factory.js +9 -43
- package/dist/oauth-server-factory.js.map +1 -1
- package/dist/oauth-session.js +12 -37
- package/dist/oauth-session.js.map +1 -1
- package/dist/runtime-implementation.d.ts +1 -1
- package/dist/runtime-implementation.d.ts.map +1 -1
- package/dist/runtime-implementation.js +1 -2
- package/dist/runtime-implementation.js.map +1 -1
- package/dist/runtime.js +8 -29
- package/dist/runtime.js.map +1 -1
- package/dist/session-getter.js +23 -38
- package/dist/session-getter.js.map +1 -1
- package/dist/state-store.js +1 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +6 -9
- package/dist/types.js.map +1 -1
- package/dist/util.js +3 -9
- package/dist/util.js.map +1 -1
- package/dist/validate-client-metadata.js +9 -12
- package/dist/validate-client-metadata.js.map +1 -1
- package/package.json +17 -16
- package/src/core-js.d.ts +1 -0
- package/src/index.ts +1 -1
- package/src/runtime-implementation.ts +2 -2
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-protected-resource-metadata-resolver.js","sourceRoot":"","sources":["../src/oauth-protected-resource-metadata-resolver.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"oauth-protected-resource-metadata-resolver.js","sourceRoot":"","sources":["../src/oauth-protected-resource-metadata-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n OAuthProtectedResourceMetadata,\n oauthProtectedResourceMetadataSchema,\n} from '@atproto/oauth-types'\nimport {\n Fetch,\n FetchResponseError,\n bindFetch,\n cancelBody,\n} from '@atproto-labs/fetch'\nimport {\n CachedGetter,\n GetCachedOptions,\n SimpleStore,\n} from '@atproto-labs/simple-store'\nimport { contentMime } from './util.js'\n\nexport type { GetCachedOptions, OAuthProtectedResourceMetadata }\n\nexport type ProtectedResourceMetadataCache = SimpleStore<\n string,\n OAuthProtectedResourceMetadata | null\n>\n\nexport type OAuthProtectedResourceMetadataResolverConfig = {\n allowHttpResource?: boolean\n}\n\n/**\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html}\n */\nexport class OAuthProtectedResourceMetadataResolver extends CachedGetter<\n string,\n OAuthProtectedResourceMetadata | null\n> {\n private readonly fetch: Fetch<unknown>\n private readonly allowHttpResource: boolean\n\n constructor(\n cache: ProtectedResourceMetadataCache,\n fetch: Fetch = globalThis.fetch,\n config?: OAuthProtectedResourceMetadataResolverConfig,\n ) {\n super(async (origin, options) => this.fetchMetadata(origin, options), cache)\n\n this.fetch = bindFetch(fetch)\n this.allowHttpResource = config?.allowHttpResource === true\n }\n\n async get(\n resource: string | URL,\n options?: GetCachedOptions,\n ): Promise<OAuthProtectedResourceMetadata | null> {\n const { protocol, origin } = new URL(resource)\n\n if (protocol !== 'https:' && protocol !== 'http:') {\n throw new TypeError(\n `Invalid protected resource metadata URL protocol: ${protocol}`,\n )\n }\n\n if (protocol === 'http:' && !this.allowHttpResource) {\n throw new TypeError(\n `Unsecure resource metadata URL (${protocol}) only allowed in development and test environments`,\n )\n }\n\n return super.get(origin, options)\n }\n\n private async fetchMetadata(\n origin: string,\n options?: GetCachedOptions,\n ): Promise<OAuthProtectedResourceMetadata | null> {\n const url = new URL(`/.well-known/oauth-protected-resource`, origin)\n const request = new Request(url, {\n signal: options?.signal,\n headers: { accept: 'application/json' },\n cache: options?.noCache ? 'no-cache' : undefined,\n redirect: 'manual', // response must be 200 OK\n })\n\n const response = await this.fetch(request)\n\n if (response.status === 404) {\n await cancelBody(response, 'log')\n return null\n }\n\n // https://www.rfc-editor.org/rfc/rfc9728.html#section-3.2\n if (response.status !== 200) {\n await cancelBody(response, 'log')\n throw await FetchResponseError.from(\n response,\n `Unexpected status code ${response.status} for \"${url}\"`,\n undefined,\n { cause: request },\n )\n }\n\n if (contentMime(response.headers) !== 'application/json') {\n await cancelBody(response, 'log')\n throw await FetchResponseError.from(\n response,\n `Unexpected content type for \"${url}\"`,\n undefined,\n { cause: request },\n )\n }\n\n const metadata = oauthProtectedResourceMetadataSchema.parse(\n await response.json(),\n )\n\n // https://www.rfc-editor.org/rfc/rfc9728.html#section-3.3\n if (metadata.resource !== origin) {\n throw new TypeError(`Invalid issuer ${metadata.resource}`)\n }\n\n return metadata\n }\n}\n"]}
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.OAuthResolverError = void 0;
|
|
4
|
-
const zod_1 = require("zod");
|
|
5
|
-
class OAuthResolverError extends Error {
|
|
1
|
+
import { ZodError } from 'zod';
|
|
2
|
+
export class OAuthResolverError extends Error {
|
|
6
3
|
constructor(message, options) {
|
|
7
4
|
super(message, options);
|
|
8
5
|
}
|
|
9
6
|
static from(cause, message) {
|
|
10
7
|
if (cause instanceof OAuthResolverError)
|
|
11
8
|
return cause;
|
|
12
|
-
const validationReason = cause instanceof
|
|
9
|
+
const validationReason = cause instanceof ZodError
|
|
13
10
|
? `${cause.errors[0].path} ${cause.errors[0].message}`
|
|
14
11
|
: null;
|
|
15
12
|
const fullMessage = (message ?? `Unable to resolve identity`) +
|
|
@@ -19,5 +16,4 @@ class OAuthResolverError extends Error {
|
|
|
19
16
|
});
|
|
20
17
|
}
|
|
21
18
|
}
|
|
22
|
-
exports.OAuthResolverError = OAuthResolverError;
|
|
23
19
|
//# sourceMappingURL=oauth-resolver-error.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-resolver-error.js","sourceRoot":"","sources":["../src/oauth-resolver-error.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"oauth-resolver-error.js","sourceRoot":"","sources":["../src/oauth-resolver-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAA;AAE9B,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IACzB,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,KAAc,EAAE,OAAgB;QAC1C,IAAI,KAAK,YAAY,kBAAkB;YAAE,OAAO,KAAK,CAAA;QACrD,MAAM,gBAAgB,GACpB,KAAK,YAAY,QAAQ;YACvB,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE;YACtD,CAAC,CAAC,IAAI,CAAA;QACV,MAAM,WAAW,GACf,CAAC,OAAO,IAAI,4BAA4B,CAAC;YACzC,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,gBAAgB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QACpD,OAAO,IAAI,kBAAkB,CAAC,WAAW,EAAE;YACzC,KAAK;SACN,CAAC,CAAA;IACJ,CAAC;CACF","sourcesContent":["import { ZodError } from 'zod'\n\nexport class OAuthResolverError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options)\n }\n\n static from(cause: unknown, message?: string): OAuthResolverError {\n if (cause instanceof OAuthResolverError) return cause\n const validationReason =\n cause instanceof ZodError\n ? `${cause.errors[0].path} ${cause.errors[0].message}`\n : null\n const fullMessage =\n (message ?? `Unable to resolve identity`) +\n (validationReason ? ` (${validationReason})` : '')\n return new OAuthResolverError(fullMessage, {\n cause,\n })\n }\n}\n"]}
|
package/dist/oauth-resolver.js
CHANGED
|
@@ -1,29 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const oauth_types_1 = require("@atproto/oauth-types");
|
|
6
|
-
const oauth_resolver_error_js_1 = require("./oauth-resolver-error.js");
|
|
7
|
-
class OAuthResolver {
|
|
1
|
+
import { extractPdsUrl } from '@atproto/did';
|
|
2
|
+
import { oauthIssuerIdentifierSchema, } from '@atproto/oauth-types';
|
|
3
|
+
import { OAuthResolverError } from './oauth-resolver-error.js';
|
|
4
|
+
export class OAuthResolver {
|
|
8
5
|
constructor(identityResolver, protectedResourceMetadataResolver, authorizationServerMetadataResolver) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
writable: true,
|
|
13
|
-
value: identityResolver
|
|
14
|
-
});
|
|
15
|
-
Object.defineProperty(this, "protectedResourceMetadataResolver", {
|
|
16
|
-
enumerable: true,
|
|
17
|
-
configurable: true,
|
|
18
|
-
writable: true,
|
|
19
|
-
value: protectedResourceMetadataResolver
|
|
20
|
-
});
|
|
21
|
-
Object.defineProperty(this, "authorizationServerMetadataResolver", {
|
|
22
|
-
enumerable: true,
|
|
23
|
-
configurable: true,
|
|
24
|
-
writable: true,
|
|
25
|
-
value: authorizationServerMetadataResolver
|
|
26
|
-
});
|
|
6
|
+
this.identityResolver = identityResolver;
|
|
7
|
+
this.protectedResourceMetadataResolver = protectedResourceMetadataResolver;
|
|
8
|
+
this.authorizationServerMetadataResolver = authorizationServerMetadataResolver;
|
|
27
9
|
}
|
|
28
10
|
/**
|
|
29
11
|
* @param input - A handle, DID, PDS URL or Entryway URL
|
|
@@ -47,10 +29,10 @@ class OAuthResolver {
|
|
|
47
29
|
return { metadata };
|
|
48
30
|
}
|
|
49
31
|
catch (err) {
|
|
50
|
-
if (!options?.signal?.aborted && err instanceof
|
|
32
|
+
if (!options?.signal?.aborted && err instanceof OAuthResolverError) {
|
|
51
33
|
try {
|
|
52
34
|
// Fallback to trying to fetch as an issuer (Entryway)
|
|
53
|
-
const result =
|
|
35
|
+
const result = oauthIssuerIdentifierSchema.safeParse(input);
|
|
54
36
|
if (result.success) {
|
|
55
37
|
const metadata = await this.getAuthorizationServerMetadata(result.data, options);
|
|
56
38
|
return { metadata };
|
|
@@ -66,7 +48,7 @@ class OAuthResolver {
|
|
|
66
48
|
async resolveFromIdentity(input, options) {
|
|
67
49
|
const identityInfo = await this.resolveIdentity(input, options);
|
|
68
50
|
options?.signal?.throwIfAborted();
|
|
69
|
-
const pds =
|
|
51
|
+
const pds = extractPdsUrl(identityInfo.didDoc);
|
|
70
52
|
const metadata = await this.getResourceServerMetadata(pds, options);
|
|
71
53
|
return { identityInfo, metadata, pds };
|
|
72
54
|
}
|
|
@@ -75,7 +57,7 @@ class OAuthResolver {
|
|
|
75
57
|
return await this.identityResolver.resolve(input, options);
|
|
76
58
|
}
|
|
77
59
|
catch (cause) {
|
|
78
|
-
throw
|
|
60
|
+
throw OAuthResolverError.from(cause, `Failed to resolve identity: ${input}`);
|
|
79
61
|
}
|
|
80
62
|
}
|
|
81
63
|
async getAuthorizationServerMetadata(issuer, options) {
|
|
@@ -83,7 +65,7 @@ class OAuthResolver {
|
|
|
83
65
|
return await this.authorizationServerMetadataResolver.get(issuer, options);
|
|
84
66
|
}
|
|
85
67
|
catch (cause) {
|
|
86
|
-
throw
|
|
68
|
+
throw OAuthResolverError.from(cause, `Failed to resolve OAuth server metadata for issuer: ${issuer}`);
|
|
87
69
|
}
|
|
88
70
|
}
|
|
89
71
|
async getResourceServerMetadata(pdsUrl, options) {
|
|
@@ -94,7 +76,7 @@ class OAuthResolver {
|
|
|
94
76
|
}
|
|
95
77
|
// ATPROTO requires one, and only one, authorization server entry
|
|
96
78
|
if (rsMetadata.authorization_servers?.length !== 1) {
|
|
97
|
-
throw new
|
|
79
|
+
throw new OAuthResolverError(rsMetadata.authorization_servers?.length
|
|
98
80
|
? `Unable to determine authorization server for PDS: ${pdsUrl}`
|
|
99
81
|
: `No authorization servers found for PDS: ${pdsUrl}`);
|
|
100
82
|
}
|
|
@@ -104,15 +86,14 @@ class OAuthResolver {
|
|
|
104
86
|
// https://www.rfc-editor.org/rfc/rfc9728.html#section-4
|
|
105
87
|
if (asMetadata.protected_resources) {
|
|
106
88
|
if (!asMetadata.protected_resources.includes(rsMetadata.resource)) {
|
|
107
|
-
throw new
|
|
89
|
+
throw new OAuthResolverError(`PDS "${pdsUrl}" not protected by issuer "${issuer}"`);
|
|
108
90
|
}
|
|
109
91
|
}
|
|
110
92
|
return asMetadata;
|
|
111
93
|
}
|
|
112
94
|
catch (cause) {
|
|
113
|
-
throw
|
|
95
|
+
throw OAuthResolverError.from(cause, `Failed to resolve OAuth server metadata for resource: ${pdsUrl}`);
|
|
114
96
|
}
|
|
115
97
|
}
|
|
116
98
|
}
|
|
117
|
-
exports.OAuthResolver = OAuthResolver;
|
|
118
99
|
//# sourceMappingURL=oauth-resolver.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-resolver.js","sourceRoot":"","sources":["../src/oauth-resolver.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"oauth-resolver.js","sourceRoot":"","sources":["../src/oauth-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { extractPdsUrl } from '@atproto/did'\nimport {\n OAuthAuthorizationServerMetadata,\n oauthIssuerIdentifierSchema,\n} from '@atproto/oauth-types'\nimport {\n IdentityInfo,\n IdentityResolver,\n ResolveIdentityOptions,\n} from '@atproto-labs/identity-resolver'\nimport {\n GetCachedOptions,\n OAuthAuthorizationServerMetadataResolver,\n} from './oauth-authorization-server-metadata-resolver.js'\nimport { OAuthProtectedResourceMetadataResolver } from './oauth-protected-resource-metadata-resolver.js'\nimport { OAuthResolverError } from './oauth-resolver-error.js'\n\nexport type { GetCachedOptions }\nexport type ResolveOAuthOptions = GetCachedOptions & ResolveIdentityOptions\n\nexport class OAuthResolver {\n constructor(\n readonly identityResolver: IdentityResolver,\n readonly protectedResourceMetadataResolver: OAuthProtectedResourceMetadataResolver,\n readonly authorizationServerMetadataResolver: OAuthAuthorizationServerMetadataResolver,\n ) {}\n\n /**\n * @param input - A handle, DID, PDS URL or Entryway URL\n */\n public async resolve(\n input: string,\n options?: ResolveOAuthOptions,\n ): Promise<{\n identityInfo?: IdentityInfo\n metadata: OAuthAuthorizationServerMetadata\n }> {\n // Allow using an entryway, or PDS url, directly as login input (e.g.\n // when the user forgot their handle, or when the handle does not\n // resolve to a DID)\n return /^https?:\\/\\//.test(input)\n ? this.resolveFromService(input, options)\n : this.resolveFromIdentity(input, options)\n }\n\n /**\n * @note this method can be used to verify if a particular uri supports OAuth\n * based sign-in (for compatibility with legacy implementation).\n */\n public async resolveFromService(\n input: string,\n options?: ResolveOAuthOptions,\n ): Promise<{\n metadata: OAuthAuthorizationServerMetadata\n }> {\n try {\n // Assume first that input is a PDS URL (as required by ATPROTO)\n const metadata = await this.getResourceServerMetadata(input, options)\n return { metadata }\n } catch (err) {\n if (!options?.signal?.aborted && err instanceof OAuthResolverError) {\n try {\n // Fallback to trying to fetch as an issuer (Entryway)\n const result = oauthIssuerIdentifierSchema.safeParse(input)\n if (result.success) {\n const metadata = await this.getAuthorizationServerMetadata(\n result.data,\n options,\n )\n return { metadata }\n }\n } catch {\n // Fallback failed, throw original error\n }\n }\n\n throw err\n }\n }\n\n public async resolveFromIdentity(\n input: string,\n options?: ResolveOAuthOptions,\n ): Promise<{\n identityInfo: IdentityInfo\n metadata: OAuthAuthorizationServerMetadata\n pds: URL\n }> {\n const identityInfo = await this.resolveIdentity(input, options)\n\n options?.signal?.throwIfAborted()\n\n const pds = extractPdsUrl(identityInfo.didDoc)\n\n const metadata = await this.getResourceServerMetadata(pds, options)\n\n return { identityInfo, metadata, pds }\n }\n\n public async resolveIdentity(\n input: string,\n options?: ResolveIdentityOptions,\n ): Promise<IdentityInfo> {\n try {\n return await this.identityResolver.resolve(input, options)\n } catch (cause) {\n throw OAuthResolverError.from(\n cause,\n `Failed to resolve identity: ${input}`,\n )\n }\n }\n\n public async getAuthorizationServerMetadata(\n issuer: string | URL,\n options?: GetCachedOptions,\n ): Promise<OAuthAuthorizationServerMetadata> {\n try {\n return await this.authorizationServerMetadataResolver.get(issuer, options)\n } catch (cause) {\n throw OAuthResolverError.from(\n cause,\n `Failed to resolve OAuth server metadata for issuer: ${issuer}`,\n )\n }\n }\n\n public async getResourceServerMetadata(\n pdsUrl: string | URL,\n options?: GetCachedOptions,\n ) {\n try {\n const rsMetadata = await this.protectedResourceMetadataResolver.get(\n pdsUrl,\n options,\n )\n\n if (!rsMetadata) {\n return this.getAuthorizationServerMetadata(pdsUrl, options)\n }\n\n // ATPROTO requires one, and only one, authorization server entry\n if (rsMetadata.authorization_servers?.length !== 1) {\n throw new OAuthResolverError(\n rsMetadata.authorization_servers?.length\n ? `Unable to determine authorization server for PDS: ${pdsUrl}`\n : `No authorization servers found for PDS: ${pdsUrl}`,\n )\n }\n\n const issuer = rsMetadata.authorization_servers![0]!\n\n options?.signal?.throwIfAborted()\n\n const asMetadata = await this.getAuthorizationServerMetadata(\n issuer,\n options,\n )\n\n // https://www.rfc-editor.org/rfc/rfc9728.html#section-4\n if (asMetadata.protected_resources) {\n if (!asMetadata.protected_resources.includes(rsMetadata.resource)) {\n throw new OAuthResolverError(\n `PDS \"${pdsUrl}\" not protected by issuer \"${issuer}\"`,\n )\n }\n }\n\n return asMetadata\n } catch (cause) {\n throw OAuthResolverError.from(\n cause,\n `Failed to resolve OAuth server metadata for resource: ${pdsUrl}`,\n )\n }\n }\n}\n"]}
|
|
@@ -1,40 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.OAuthResponseError = void 0;
|
|
4
|
-
const util_js_1 = require("./util.js");
|
|
5
|
-
class OAuthResponseError extends Error {
|
|
1
|
+
import { ifString } from './util.js';
|
|
2
|
+
export class OAuthResponseError extends Error {
|
|
6
3
|
constructor(response, payload) {
|
|
7
4
|
const objPayload = typeof payload === 'object' ? payload : undefined;
|
|
8
|
-
const error =
|
|
9
|
-
const errorDescription =
|
|
5
|
+
const error = ifString(objPayload?.['error']);
|
|
6
|
+
const errorDescription = ifString(objPayload?.['error_description']);
|
|
10
7
|
const messageError = error ? `"${error}"` : 'unknown';
|
|
11
8
|
const messageDesc = errorDescription ? `: ${errorDescription}` : '';
|
|
12
9
|
const message = `OAuth ${messageError} error${messageDesc}`;
|
|
13
10
|
super(message);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
configurable: true,
|
|
17
|
-
writable: true,
|
|
18
|
-
value: response
|
|
19
|
-
});
|
|
20
|
-
Object.defineProperty(this, "payload", {
|
|
21
|
-
enumerable: true,
|
|
22
|
-
configurable: true,
|
|
23
|
-
writable: true,
|
|
24
|
-
value: payload
|
|
25
|
-
});
|
|
26
|
-
Object.defineProperty(this, "error", {
|
|
27
|
-
enumerable: true,
|
|
28
|
-
configurable: true,
|
|
29
|
-
writable: true,
|
|
30
|
-
value: void 0
|
|
31
|
-
});
|
|
32
|
-
Object.defineProperty(this, "errorDescription", {
|
|
33
|
-
enumerable: true,
|
|
34
|
-
configurable: true,
|
|
35
|
-
writable: true,
|
|
36
|
-
value: void 0
|
|
37
|
-
});
|
|
11
|
+
this.response = response;
|
|
12
|
+
this.payload = payload;
|
|
38
13
|
this.error = error;
|
|
39
14
|
this.errorDescription = errorDescription;
|
|
40
15
|
}
|
|
@@ -45,5 +20,4 @@ class OAuthResponseError extends Error {
|
|
|
45
20
|
return this.response.headers;
|
|
46
21
|
}
|
|
47
22
|
}
|
|
48
|
-
exports.OAuthResponseError = OAuthResponseError;
|
|
49
23
|
//# sourceMappingURL=oauth-response-error.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-response-error.js","sourceRoot":"","sources":["../src/oauth-response-error.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"oauth-response-error.js","sourceRoot":"","sources":["../src/oauth-response-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAEpC,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAI3C,YACkB,QAAkB,EAClB,OAAa;QAE7B,MAAM,UAAU,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;QACpE,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,CAAC,CAAA;QAC7C,MAAM,gBAAgB,GAAG,QAAQ,CAAC,UAAU,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAA;QAEpE,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,SAAS,CAAA;QACrD,MAAM,WAAW,GAAG,gBAAgB,CAAC,CAAC,CAAC,KAAK,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QACnE,MAAM,OAAO,GAAG,SAAS,YAAY,SAAS,WAAW,EAAE,CAAA;QAE3D,KAAK,CAAC,OAAO,CAAC,CAAA;QAXE,aAAQ,GAAR,QAAQ,CAAU;QAClB,YAAO,GAAP,OAAO,CAAM;QAY7B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAA;IAC1C,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAA;IAC7B,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAA;IAC9B,CAAC;CACF","sourcesContent":["import { Json } from '@atproto-labs/fetch'\nimport { ifString } from './util.js'\n\nexport class OAuthResponseError extends Error {\n readonly error?: string\n readonly errorDescription?: string\n\n constructor(\n public readonly response: Response,\n public readonly payload: Json,\n ) {\n const objPayload = typeof payload === 'object' ? payload : undefined\n const error = ifString(objPayload?.['error'])\n const errorDescription = ifString(objPayload?.['error_description'])\n\n const messageError = error ? `\"${error}\"` : 'unknown'\n const messageDesc = errorDescription ? `: ${errorDescription}` : ''\n const message = `OAuth ${messageError} error${messageDesc}`\n\n super(message)\n\n this.error = error\n this.errorDescription = errorDescription\n }\n\n get status() {\n return this.response.status\n }\n\n get headers() {\n return this.response.headers\n }\n}\n"]}
|
|
@@ -1,80 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const oauth_client_auth_js_1 = require("./oauth-client-auth.js");
|
|
9
|
-
const oauth_response_error_js_1 = require("./oauth-response-error.js");
|
|
10
|
-
class OAuthServerAgent {
|
|
1
|
+
import { atprotoOAuthTokenResponseSchema, oauthParResponseSchema, } from '@atproto/oauth-types';
|
|
2
|
+
import { bindFetch, fetchJsonProcessor } from '@atproto-labs/fetch';
|
|
3
|
+
import { TokenRefreshError } from './errors/token-refresh-error.js';
|
|
4
|
+
import { dpopFetchWrapper } from './fetch-dpop.js';
|
|
5
|
+
import { createClientCredentialsFactory, } from './oauth-client-auth.js';
|
|
6
|
+
import { OAuthResponseError } from './oauth-response-error.js';
|
|
7
|
+
export class OAuthServerAgent {
|
|
11
8
|
/**
|
|
12
9
|
* @throws see {@link createClientCredentialsFactory}
|
|
13
10
|
*/
|
|
14
11
|
constructor(authMethod, dpopKey, serverMetadata, clientMetadata, dpopNonces, oauthResolver, runtime, keyset, fetch) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
});
|
|
27
|
-
Object.defineProperty(this, "serverMetadata", {
|
|
28
|
-
enumerable: true,
|
|
29
|
-
configurable: true,
|
|
30
|
-
writable: true,
|
|
31
|
-
value: serverMetadata
|
|
32
|
-
});
|
|
33
|
-
Object.defineProperty(this, "clientMetadata", {
|
|
34
|
-
enumerable: true,
|
|
35
|
-
configurable: true,
|
|
36
|
-
writable: true,
|
|
37
|
-
value: clientMetadata
|
|
38
|
-
});
|
|
39
|
-
Object.defineProperty(this, "dpopNonces", {
|
|
40
|
-
enumerable: true,
|
|
41
|
-
configurable: true,
|
|
42
|
-
writable: true,
|
|
43
|
-
value: dpopNonces
|
|
44
|
-
});
|
|
45
|
-
Object.defineProperty(this, "oauthResolver", {
|
|
46
|
-
enumerable: true,
|
|
47
|
-
configurable: true,
|
|
48
|
-
writable: true,
|
|
49
|
-
value: oauthResolver
|
|
50
|
-
});
|
|
51
|
-
Object.defineProperty(this, "runtime", {
|
|
52
|
-
enumerable: true,
|
|
53
|
-
configurable: true,
|
|
54
|
-
writable: true,
|
|
55
|
-
value: runtime
|
|
56
|
-
});
|
|
57
|
-
Object.defineProperty(this, "keyset", {
|
|
58
|
-
enumerable: true,
|
|
59
|
-
configurable: true,
|
|
60
|
-
writable: true,
|
|
61
|
-
value: keyset
|
|
62
|
-
});
|
|
63
|
-
Object.defineProperty(this, "dpopFetch", {
|
|
64
|
-
enumerable: true,
|
|
65
|
-
configurable: true,
|
|
66
|
-
writable: true,
|
|
67
|
-
value: void 0
|
|
68
|
-
});
|
|
69
|
-
Object.defineProperty(this, "clientCredentialsFactory", {
|
|
70
|
-
enumerable: true,
|
|
71
|
-
configurable: true,
|
|
72
|
-
writable: true,
|
|
73
|
-
value: void 0
|
|
74
|
-
});
|
|
75
|
-
this.clientCredentialsFactory = (0, oauth_client_auth_js_1.createClientCredentialsFactory)(authMethod, serverMetadata, clientMetadata, runtime, keyset);
|
|
76
|
-
this.dpopFetch = (0, fetch_dpop_js_1.dpopFetchWrapper)({
|
|
77
|
-
fetch: (0, fetch_1.bindFetch)(fetch),
|
|
12
|
+
this.authMethod = authMethod;
|
|
13
|
+
this.dpopKey = dpopKey;
|
|
14
|
+
this.serverMetadata = serverMetadata;
|
|
15
|
+
this.clientMetadata = clientMetadata;
|
|
16
|
+
this.dpopNonces = dpopNonces;
|
|
17
|
+
this.oauthResolver = oauthResolver;
|
|
18
|
+
this.runtime = runtime;
|
|
19
|
+
this.keyset = keyset;
|
|
20
|
+
this.clientCredentialsFactory = createClientCredentialsFactory(authMethod, serverMetadata, clientMetadata, runtime, keyset);
|
|
21
|
+
this.dpopFetch = dpopFetchWrapper({
|
|
22
|
+
fetch: bindFetch(fetch),
|
|
78
23
|
key: dpopKey,
|
|
79
24
|
supportedAlgs: serverMetadata.dpop_signing_alg_values_supported,
|
|
80
25
|
sha256: async (v) => runtime.sha256(v),
|
|
@@ -129,7 +74,7 @@ class OAuthServerAgent {
|
|
|
129
74
|
}
|
|
130
75
|
async refresh(tokenSet) {
|
|
131
76
|
if (!tokenSet.refresh_token) {
|
|
132
|
-
throw new
|
|
77
|
+
throw new TokenRefreshError(tokenSet.sub, 'No refresh token available');
|
|
133
78
|
}
|
|
134
79
|
// /!\ IMPORTANT /!\
|
|
135
80
|
//
|
|
@@ -198,23 +143,22 @@ class OAuthServerAgent {
|
|
|
198
143
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
199
144
|
},
|
|
200
145
|
body: wwwFormUrlEncode({ ...payload, ...auth.payload }),
|
|
201
|
-
}).then(
|
|
146
|
+
}).then(fetchJsonProcessor());
|
|
202
147
|
if (response.ok) {
|
|
203
148
|
switch (endpoint) {
|
|
204
149
|
case 'token':
|
|
205
|
-
return
|
|
150
|
+
return atprotoOAuthTokenResponseSchema.parse(json);
|
|
206
151
|
case 'pushed_authorization_request':
|
|
207
|
-
return
|
|
152
|
+
return oauthParResponseSchema.parse(json);
|
|
208
153
|
default:
|
|
209
154
|
return json;
|
|
210
155
|
}
|
|
211
156
|
}
|
|
212
157
|
else {
|
|
213
|
-
throw new
|
|
158
|
+
throw new OAuthResponseError(response, json);
|
|
214
159
|
}
|
|
215
160
|
}
|
|
216
161
|
}
|
|
217
|
-
exports.OAuthServerAgent = OAuthServerAgent;
|
|
218
162
|
function wwwFormUrlEncode(payload) {
|
|
219
163
|
return new URLSearchParams(Object.entries(payload)
|
|
220
164
|
.filter(entryHasDefinedValue)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-server-agent.js","sourceRoot":"","sources":["../src/oauth-server-agent.ts"],"names":[],"mappings":"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","sourcesContent":["import { AtprotoDid } from '@atproto/did'\nimport { Key, Keyset } from '@atproto/jwk'\nimport {\n AtprotoOAuthScope,\n AtprotoOAuthTokenResponse,\n OAuthAuthorizationRequestPar,\n OAuthAuthorizationServerMetadata,\n OAuthEndpointName,\n OAuthParResponse,\n OAuthRedirectUri,\n OAuthTokenRequest,\n atprotoOAuthTokenResponseSchema,\n oauthParResponseSchema,\n} from '@atproto/oauth-types'\nimport { Fetch, Json, bindFetch, fetchJsonProcessor } from '@atproto-labs/fetch'\nimport { SimpleStore } from '@atproto-labs/simple-store'\nimport { TokenRefreshError } from './errors/token-refresh-error.js'\nimport { dpopFetchWrapper } from './fetch-dpop.js'\nimport {\n ClientAuthMethod,\n ClientCredentialsFactory,\n createClientCredentialsFactory,\n} from './oauth-client-auth.js'\nimport { OAuthResolver } from './oauth-resolver.js'\nimport { OAuthResponseError } from './oauth-response-error.js'\nimport { Runtime } from './runtime.js'\nimport { ClientMetadata } from './types.js'\n\nexport type { AtprotoOAuthScope, AtprotoOAuthTokenResponse }\n\nexport type TokenSet = {\n iss: string\n sub: AtprotoDid\n aud: string\n scope: AtprotoOAuthScope\n\n refresh_token?: string\n access_token: string\n token_type: 'DPoP'\n /** ISO Date */\n expires_at?: string\n}\n\nexport type DpopNonceCache = SimpleStore<string, string>\n\nexport class OAuthServerAgent {\n protected dpopFetch: Fetch<unknown>\n protected clientCredentialsFactory: ClientCredentialsFactory\n\n /**\n * @throws see {@link createClientCredentialsFactory}\n */\n constructor(\n readonly authMethod: ClientAuthMethod,\n readonly dpopKey: Key,\n readonly serverMetadata: OAuthAuthorizationServerMetadata,\n readonly clientMetadata: ClientMetadata,\n readonly dpopNonces: DpopNonceCache,\n readonly oauthResolver: OAuthResolver,\n readonly runtime: Runtime,\n readonly keyset?: Keyset,\n fetch?: Fetch,\n ) {\n this.clientCredentialsFactory = createClientCredentialsFactory(\n authMethod,\n serverMetadata,\n clientMetadata,\n runtime,\n keyset,\n )\n\n this.dpopFetch = dpopFetchWrapper<void>({\n fetch: bindFetch(fetch),\n key: dpopKey,\n supportedAlgs: serverMetadata.dpop_signing_alg_values_supported,\n sha256: async (v) => runtime.sha256(v),\n nonces: dpopNonces,\n isAuthServer: true,\n })\n }\n\n get issuer() {\n return this.serverMetadata.issuer\n }\n\n async revoke(token: string) {\n try {\n await this.request('revocation', { token })\n } catch {\n // Don't care\n }\n }\n\n async exchangeCode(\n code: string,\n codeVerifier?: string,\n redirectUri?: OAuthRedirectUri,\n ): Promise<TokenSet> {\n const now = Date.now()\n\n const tokenResponse = await this.request('token', {\n grant_type: 'authorization_code',\n // redirectUri should always be passed by the calling code, but if it is\n // not, default to the first redirect_uri registered for the client:\n redirect_uri: redirectUri ?? this.clientMetadata.redirect_uris[0],\n code,\n code_verifier: codeVerifier,\n })\n\n try {\n // /!\\ IMPORTANT /!\\\n //\n // The tokenResponse MUST always be valid before the \"sub\" it contains\n // can be trusted (see Atproto's OAuth spec for details).\n const aud = await this.verifyIssuer(tokenResponse.sub)\n\n return {\n aud,\n sub: tokenResponse.sub,\n iss: this.issuer,\n\n scope: tokenResponse.scope,\n refresh_token: tokenResponse.refresh_token,\n access_token: tokenResponse.access_token,\n token_type: tokenResponse.token_type,\n\n expires_at:\n typeof tokenResponse.expires_in === 'number'\n ? new Date(now + tokenResponse.expires_in * 1000).toISOString()\n : undefined,\n }\n } catch (err) {\n await this.revoke(tokenResponse.access_token)\n\n throw err\n }\n }\n\n async refresh(tokenSet: TokenSet): Promise<TokenSet> {\n if (!tokenSet.refresh_token) {\n throw new TokenRefreshError(tokenSet.sub, 'No refresh token available')\n }\n\n // /!\\ IMPORTANT /!\\\n //\n // The \"sub\" MUST be a DID, whose issuer authority is indeed the server we\n // are trying to obtain credentials from. Note that we are doing this\n // *before* we actually try to refresh the token:\n // 1) To avoid unnecessary refresh\n // 2) So that the refresh is the last async operation, ensuring as few\n // async operations happen before the result gets a chance to be stored.\n const aud = await this.verifyIssuer(tokenSet.sub)\n\n const now = Date.now()\n\n const tokenResponse = await this.request('token', {\n grant_type: 'refresh_token',\n refresh_token: tokenSet.refresh_token,\n })\n\n return {\n aud,\n sub: tokenSet.sub,\n iss: this.issuer,\n\n scope: tokenResponse.scope,\n refresh_token: tokenResponse.refresh_token,\n access_token: tokenResponse.access_token,\n token_type: tokenResponse.token_type,\n\n expires_at:\n typeof tokenResponse.expires_in === 'number'\n ? new Date(now + tokenResponse.expires_in * 1000).toISOString()\n : undefined,\n }\n }\n\n /**\n * VERY IMPORTANT ! Always call this to process token responses.\n *\n * Whenever an OAuth token response is received, we **MUST** verify that the\n * \"sub\" is a DID, whose issuer authority is indeed the server we just\n * obtained credentials from. This check is a critical step to actually be\n * able to use the \"sub\" (DID) as being the actual user's identifier.\n *\n * @returns The user's PDS URL (the resource server for the user)\n */\n protected async verifyIssuer(sub: AtprotoDid): Promise<string> {\n const resolved = await this.oauthResolver.resolveFromIdentity(sub, {\n noCache: true,\n allowStale: false,\n signal: AbortSignal.timeout(10e3),\n })\n\n if (this.issuer !== resolved.metadata.issuer) {\n // Best case scenario; the user switched PDS. Worst case scenario; a bad\n // actor is trying to impersonate a user. In any case, we must not allow\n // this token to be used.\n throw new TypeError('Issuer mismatch')\n }\n\n return resolved.pds.href\n }\n\n async request<Endpoint extends OAuthEndpointName>(\n endpoint: Endpoint,\n payload: Endpoint extends 'token'\n ? OAuthTokenRequest\n : Endpoint extends 'pushed_authorization_request'\n ? OAuthAuthorizationRequestPar\n : Record<string, unknown>,\n ): Promise<\n Endpoint extends 'token'\n ? AtprotoOAuthTokenResponse\n : Endpoint extends 'pushed_authorization_request'\n ? OAuthParResponse\n : Json\n >\n async request(\n endpoint: OAuthEndpointName,\n payload: Record<string, unknown>,\n ): Promise<unknown> {\n const url = this.serverMetadata[`${endpoint}_endpoint`]\n if (!url) throw new Error(`No ${endpoint} endpoint available`)\n\n const auth = await this.clientCredentialsFactory()\n\n // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-3.2.2\n // https://datatracker.ietf.org/doc/html/rfc7009#section-2.1\n // https://datatracker.ietf.org/doc/html/rfc7662#section-2.1\n // https://datatracker.ietf.org/doc/html/rfc9126#section-2\n const { response, json } = await this.dpopFetch(url, {\n method: 'POST',\n headers: {\n ...auth.headers,\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: wwwFormUrlEncode({ ...payload, ...auth.payload }),\n }).then(fetchJsonProcessor())\n\n if (response.ok) {\n switch (endpoint) {\n case 'token':\n return atprotoOAuthTokenResponseSchema.parse(json)\n case 'pushed_authorization_request':\n return oauthParResponseSchema.parse(json)\n default:\n return json\n }\n } else {\n throw new OAuthResponseError(response, json)\n }\n }\n}\n\nfunction wwwFormUrlEncode(payload: Record<string, undefined | unknown>) {\n return new URLSearchParams(\n Object.entries(payload)\n .filter(entryHasDefinedValue)\n .map(stringifyEntryValue),\n ).toString()\n}\n\nfunction entryHasDefinedValue(\n entry: [string, unknown],\n): entry is [string, null | NonNullable<unknown>] {\n return entry[1] !== undefined\n}\n\nfunction stringifyEntryValue(entry: [string, unknown]): [string, string] {\n const name = entry[0]\n const value = entry[1]\n\n switch (typeof value) {\n case 'string':\n return [name, value]\n case 'number':\n case 'boolean':\n return [name, String(value)]\n default: {\n const enc = JSON.stringify(value)\n if (enc === undefined) {\n throw new Error(`Unsupported value type for ${name}: ${String(value)}`)\n }\n return [name, enc]\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"oauth-server-agent.js","sourceRoot":"","sources":["../src/oauth-server-agent.ts"],"names":[],"mappings":"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","sourcesContent":["import { AtprotoDid } from '@atproto/did'\nimport { Key, Keyset } from '@atproto/jwk'\nimport {\n AtprotoOAuthScope,\n AtprotoOAuthTokenResponse,\n OAuthAuthorizationRequestPar,\n OAuthAuthorizationServerMetadata,\n OAuthEndpointName,\n OAuthParResponse,\n OAuthRedirectUri,\n OAuthTokenRequest,\n atprotoOAuthTokenResponseSchema,\n oauthParResponseSchema,\n} from '@atproto/oauth-types'\nimport { Fetch, Json, bindFetch, fetchJsonProcessor } from '@atproto-labs/fetch'\nimport { SimpleStore } from '@atproto-labs/simple-store'\nimport { TokenRefreshError } from './errors/token-refresh-error.js'\nimport { dpopFetchWrapper } from './fetch-dpop.js'\nimport {\n ClientAuthMethod,\n ClientCredentialsFactory,\n createClientCredentialsFactory,\n} from './oauth-client-auth.js'\nimport { OAuthResolver } from './oauth-resolver.js'\nimport { OAuthResponseError } from './oauth-response-error.js'\nimport { Runtime } from './runtime.js'\nimport { ClientMetadata } from './types.js'\n\nexport type { AtprotoOAuthScope, AtprotoOAuthTokenResponse }\n\nexport type TokenSet = {\n iss: string\n sub: AtprotoDid\n aud: string\n scope: AtprotoOAuthScope\n\n refresh_token?: string\n access_token: string\n token_type: 'DPoP'\n /** ISO Date */\n expires_at?: string\n}\n\nexport type DpopNonceCache = SimpleStore<string, string>\n\nexport class OAuthServerAgent {\n protected dpopFetch: Fetch<unknown>\n protected clientCredentialsFactory: ClientCredentialsFactory\n\n /**\n * @throws see {@link createClientCredentialsFactory}\n */\n constructor(\n readonly authMethod: ClientAuthMethod,\n readonly dpopKey: Key,\n readonly serverMetadata: OAuthAuthorizationServerMetadata,\n readonly clientMetadata: ClientMetadata,\n readonly dpopNonces: DpopNonceCache,\n readonly oauthResolver: OAuthResolver,\n readonly runtime: Runtime,\n readonly keyset?: Keyset,\n fetch?: Fetch,\n ) {\n this.clientCredentialsFactory = createClientCredentialsFactory(\n authMethod,\n serverMetadata,\n clientMetadata,\n runtime,\n keyset,\n )\n\n this.dpopFetch = dpopFetchWrapper<void>({\n fetch: bindFetch(fetch),\n key: dpopKey,\n supportedAlgs: serverMetadata.dpop_signing_alg_values_supported,\n sha256: async (v) => runtime.sha256(v),\n nonces: dpopNonces,\n isAuthServer: true,\n })\n }\n\n get issuer() {\n return this.serverMetadata.issuer\n }\n\n async revoke(token: string) {\n try {\n await this.request('revocation', { token })\n } catch {\n // Don't care\n }\n }\n\n async exchangeCode(\n code: string,\n codeVerifier?: string,\n redirectUri?: OAuthRedirectUri,\n ): Promise<TokenSet> {\n const now = Date.now()\n\n const tokenResponse = await this.request('token', {\n grant_type: 'authorization_code',\n // redirectUri should always be passed by the calling code, but if it is\n // not, default to the first redirect_uri registered for the client:\n redirect_uri: redirectUri ?? this.clientMetadata.redirect_uris[0],\n code,\n code_verifier: codeVerifier,\n })\n\n try {\n // /!\\ IMPORTANT /!\\\n //\n // The tokenResponse MUST always be valid before the \"sub\" it contains\n // can be trusted (see Atproto's OAuth spec for details).\n const aud = await this.verifyIssuer(tokenResponse.sub)\n\n return {\n aud,\n sub: tokenResponse.sub,\n iss: this.issuer,\n\n scope: tokenResponse.scope,\n refresh_token: tokenResponse.refresh_token,\n access_token: tokenResponse.access_token,\n token_type: tokenResponse.token_type,\n\n expires_at:\n typeof tokenResponse.expires_in === 'number'\n ? new Date(now + tokenResponse.expires_in * 1000).toISOString()\n : undefined,\n }\n } catch (err) {\n await this.revoke(tokenResponse.access_token)\n\n throw err\n }\n }\n\n async refresh(tokenSet: TokenSet): Promise<TokenSet> {\n if (!tokenSet.refresh_token) {\n throw new TokenRefreshError(tokenSet.sub, 'No refresh token available')\n }\n\n // /!\\ IMPORTANT /!\\\n //\n // The \"sub\" MUST be a DID, whose issuer authority is indeed the server we\n // are trying to obtain credentials from. Note that we are doing this\n // *before* we actually try to refresh the token:\n // 1) To avoid unnecessary refresh\n // 2) So that the refresh is the last async operation, ensuring as few\n // async operations happen before the result gets a chance to be stored.\n const aud = await this.verifyIssuer(tokenSet.sub)\n\n const now = Date.now()\n\n const tokenResponse = await this.request('token', {\n grant_type: 'refresh_token',\n refresh_token: tokenSet.refresh_token,\n })\n\n return {\n aud,\n sub: tokenSet.sub,\n iss: this.issuer,\n\n scope: tokenResponse.scope,\n refresh_token: tokenResponse.refresh_token,\n access_token: tokenResponse.access_token,\n token_type: tokenResponse.token_type,\n\n expires_at:\n typeof tokenResponse.expires_in === 'number'\n ? new Date(now + tokenResponse.expires_in * 1000).toISOString()\n : undefined,\n }\n }\n\n /**\n * VERY IMPORTANT ! Always call this to process token responses.\n *\n * Whenever an OAuth token response is received, we **MUST** verify that the\n * \"sub\" is a DID, whose issuer authority is indeed the server we just\n * obtained credentials from. This check is a critical step to actually be\n * able to use the \"sub\" (DID) as being the actual user's identifier.\n *\n * @returns The user's PDS URL (the resource server for the user)\n */\n protected async verifyIssuer(sub: AtprotoDid): Promise<string> {\n const resolved = await this.oauthResolver.resolveFromIdentity(sub, {\n noCache: true,\n allowStale: false,\n signal: AbortSignal.timeout(10e3),\n })\n\n if (this.issuer !== resolved.metadata.issuer) {\n // Best case scenario; the user switched PDS. Worst case scenario; a bad\n // actor is trying to impersonate a user. In any case, we must not allow\n // this token to be used.\n throw new TypeError('Issuer mismatch')\n }\n\n return resolved.pds.href\n }\n\n async request<Endpoint extends OAuthEndpointName>(\n endpoint: Endpoint,\n payload: Endpoint extends 'token'\n ? OAuthTokenRequest\n : Endpoint extends 'pushed_authorization_request'\n ? OAuthAuthorizationRequestPar\n : Record<string, unknown>,\n ): Promise<\n Endpoint extends 'token'\n ? AtprotoOAuthTokenResponse\n : Endpoint extends 'pushed_authorization_request'\n ? OAuthParResponse\n : Json\n >\n async request(\n endpoint: OAuthEndpointName,\n payload: Record<string, unknown>,\n ): Promise<unknown> {\n const url = this.serverMetadata[`${endpoint}_endpoint`]\n if (!url) throw new Error(`No ${endpoint} endpoint available`)\n\n const auth = await this.clientCredentialsFactory()\n\n // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-3.2.2\n // https://datatracker.ietf.org/doc/html/rfc7009#section-2.1\n // https://datatracker.ietf.org/doc/html/rfc7662#section-2.1\n // https://datatracker.ietf.org/doc/html/rfc9126#section-2\n const { response, json } = await this.dpopFetch(url, {\n method: 'POST',\n headers: {\n ...auth.headers,\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: wwwFormUrlEncode({ ...payload, ...auth.payload }),\n }).then(fetchJsonProcessor())\n\n if (response.ok) {\n switch (endpoint) {\n case 'token':\n return atprotoOAuthTokenResponseSchema.parse(json)\n case 'pushed_authorization_request':\n return oauthParResponseSchema.parse(json)\n default:\n return json\n }\n } else {\n throw new OAuthResponseError(response, json)\n }\n }\n}\n\nfunction wwwFormUrlEncode(payload: Record<string, undefined | unknown>) {\n return new URLSearchParams(\n Object.entries(payload)\n .filter(entryHasDefinedValue)\n .map(stringifyEntryValue),\n ).toString()\n}\n\nfunction entryHasDefinedValue(\n entry: [string, unknown],\n): entry is [string, null | NonNullable<unknown>] {\n return entry[1] !== undefined\n}\n\nfunction stringifyEntryValue(entry: [string, unknown]): [string, string] {\n const name = entry[0]\n const value = entry[1]\n\n switch (typeof value) {\n case 'string':\n return [name, value]\n case 'number':\n case 'boolean':\n return [name, String(value)]\n default: {\n const enc = JSON.stringify(value)\n if (enc === undefined) {\n throw new Error(`Unsupported value type for ${name}: ${String(value)}`)\n }\n return [name, enc]\n }\n }\n}\n"]}
|
|
@@ -1,45 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.OAuthServerFactory = void 0;
|
|
4
|
-
const oauth_server_agent_js_1 = require("./oauth-server-agent.js");
|
|
5
|
-
class OAuthServerFactory {
|
|
1
|
+
import { OAuthServerAgent } from './oauth-server-agent.js';
|
|
2
|
+
export class OAuthServerFactory {
|
|
6
3
|
constructor(clientMetadata, runtime, resolver, fetch, keyset, dpopNonceCache) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Object.defineProperty(this, "runtime", {
|
|
14
|
-
enumerable: true,
|
|
15
|
-
configurable: true,
|
|
16
|
-
writable: true,
|
|
17
|
-
value: runtime
|
|
18
|
-
});
|
|
19
|
-
Object.defineProperty(this, "resolver", {
|
|
20
|
-
enumerable: true,
|
|
21
|
-
configurable: true,
|
|
22
|
-
writable: true,
|
|
23
|
-
value: resolver
|
|
24
|
-
});
|
|
25
|
-
Object.defineProperty(this, "fetch", {
|
|
26
|
-
enumerable: true,
|
|
27
|
-
configurable: true,
|
|
28
|
-
writable: true,
|
|
29
|
-
value: fetch
|
|
30
|
-
});
|
|
31
|
-
Object.defineProperty(this, "keyset", {
|
|
32
|
-
enumerable: true,
|
|
33
|
-
configurable: true,
|
|
34
|
-
writable: true,
|
|
35
|
-
value: keyset
|
|
36
|
-
});
|
|
37
|
-
Object.defineProperty(this, "dpopNonceCache", {
|
|
38
|
-
enumerable: true,
|
|
39
|
-
configurable: true,
|
|
40
|
-
writable: true,
|
|
41
|
-
value: dpopNonceCache
|
|
42
|
-
});
|
|
4
|
+
this.clientMetadata = clientMetadata;
|
|
5
|
+
this.runtime = runtime;
|
|
6
|
+
this.resolver = resolver;
|
|
7
|
+
this.fetch = fetch;
|
|
8
|
+
this.keyset = keyset;
|
|
9
|
+
this.dpopNonceCache = dpopNonceCache;
|
|
43
10
|
}
|
|
44
11
|
/**
|
|
45
12
|
* @param authMethod `undefined` means that we are restoring a session that
|
|
@@ -58,8 +25,7 @@ class OAuthServerFactory {
|
|
|
58
25
|
* @throws see {@link OAuthServerAgent}
|
|
59
26
|
*/
|
|
60
27
|
async fromMetadata(serverMetadata, authMethod, dpopKey) {
|
|
61
|
-
return new
|
|
28
|
+
return new OAuthServerAgent(authMethod, dpopKey, serverMetadata, this.clientMetadata, this.dpopNonceCache, this.resolver, this.runtime, this.keyset, this.fetch);
|
|
62
29
|
}
|
|
63
30
|
}
|
|
64
|
-
exports.OAuthServerFactory = OAuthServerFactory;
|
|
65
31
|
//# sourceMappingURL=oauth-server-factory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-server-factory.js","sourceRoot":"","sources":["../src/oauth-server-factory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"oauth-server-factory.js","sourceRoot":"","sources":["../src/oauth-server-factory.ts"],"names":[],"mappings":"AAMA,OAAO,EAAkB,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAI1E,MAAM,OAAO,kBAAkB;IAC7B,YACW,cAA8B,EAC9B,OAAgB,EAChB,QAAuB,EACvB,KAAY,EACZ,MAA0B,EAC1B,cAA8B;QAL9B,mBAAc,GAAd,cAAc,CAAgB;QAC9B,YAAO,GAAP,OAAO,CAAS;QAChB,aAAQ,GAAR,QAAQ,CAAe;QACvB,UAAK,GAAL,KAAK,CAAO;QACZ,WAAM,GAAN,MAAM,CAAoB;QAC1B,mBAAc,GAAd,cAAc,CAAgB;IACtC,CAAC;IAEJ;;;;;;;;OAQG;IACH,KAAK,CAAC,UAAU,CACd,MAAc,EACd,UAA4B,EAC5B,OAAY,EACZ,OAA0B;QAE1B,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,8BAA8B,CACvE,MAAM,EACN,OAAO,CACR,CAAA;QAED,OAAO,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,UAAU,EAAE,OAAO,CAAC,CAAA;IAC/D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAChB,cAAgD,EAChD,UAA4B,EAC5B,OAAY;QAEZ,OAAO,IAAI,gBAAgB,CACzB,UAAU,EACV,OAAO,EACP,cAAc,EACd,IAAI,CAAC,cAAc,EACnB,IAAI,CAAC,cAAc,EACnB,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,OAAO,EACZ,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,KAAK,CACX,CAAA;IACH,CAAC;CACF","sourcesContent":["import { Key, Keyset } from '@atproto/jwk'\nimport { OAuthAuthorizationServerMetadata } from '@atproto/oauth-types'\nimport { Fetch } from '@atproto-labs/fetch'\nimport { GetCachedOptions } from './oauth-authorization-server-metadata-resolver.js'\nimport { ClientAuthMethod } from './oauth-client-auth.js'\nimport { OAuthResolver } from './oauth-resolver.js'\nimport { DpopNonceCache, OAuthServerAgent } from './oauth-server-agent.js'\nimport { Runtime } from './runtime.js'\nimport { ClientMetadata } from './types.js'\n\nexport class OAuthServerFactory {\n constructor(\n readonly clientMetadata: ClientMetadata,\n readonly runtime: Runtime,\n readonly resolver: OAuthResolver,\n readonly fetch: Fetch,\n readonly keyset: Keyset | undefined,\n readonly dpopNonceCache: DpopNonceCache,\n ) {}\n\n /**\n * @param authMethod `undefined` means that we are restoring a session that\n * was created before we started storing the `authMethod` in the session. In\n * that case, we will use the first key from the keyset.\n *\n * Support for this might be removed in the future.\n *\n * @throws see {@link OAuthServerFactory.fromMetadata}\n */\n async fromIssuer(\n issuer: string,\n authMethod: ClientAuthMethod,\n dpopKey: Key,\n options?: GetCachedOptions,\n ) {\n const serverMetadata = await this.resolver.getAuthorizationServerMetadata(\n issuer,\n options,\n )\n\n return this.fromMetadata(serverMetadata, authMethod, dpopKey)\n }\n\n /**\n * @throws see {@link OAuthServerAgent}\n */\n async fromMetadata(\n serverMetadata: OAuthAuthorizationServerMetadata,\n authMethod: ClientAuthMethod,\n dpopKey: Key,\n ) {\n return new OAuthServerAgent(\n authMethod,\n dpopKey,\n serverMetadata,\n this.clientMetadata,\n this.dpopNonceCache,\n this.resolver,\n this.runtime,\n this.keyset,\n this.fetch,\n )\n }\n}\n"]}
|