@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,11 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const oauth_types_1 = require("@atproto/oauth-types");
|
|
6
|
-
const constants_js_1 = require("./constants.js");
|
|
7
|
-
const auth_method_unsatisfiable_error_js_1 = require("./errors/auth-method-unsatisfiable-error.js");
|
|
8
|
-
function negotiateClientAuthMethod(serverMetadata, clientMetadata, keyset) {
|
|
1
|
+
import { CLIENT_ASSERTION_TYPE_JWT_BEARER, } from '@atproto/oauth-types';
|
|
2
|
+
import { FALLBACK_ALG } from './constants.js';
|
|
3
|
+
import { AuthMethodUnsatisfiableError } from './errors/auth-method-unsatisfiable-error.js';
|
|
4
|
+
export function negotiateClientAuthMethod(serverMetadata, clientMetadata, keyset) {
|
|
9
5
|
const method = clientMetadata.token_endpoint_auth_method;
|
|
10
6
|
// @NOTE ATproto spec requires that AS support both "none" and
|
|
11
7
|
// "private_key_jwt", and that clients use one of the other. The following
|
|
@@ -30,10 +26,10 @@ function negotiateClientAuthMethod(serverMetadata, clientMetadata, keyset) {
|
|
|
30
26
|
if (key.kid)
|
|
31
27
|
return { method: 'private_key_jwt', kid: key.kid };
|
|
32
28
|
}
|
|
33
|
-
throw new Error(alg.includes(
|
|
34
|
-
? `Client authentication method "${method}" requires at least one "${
|
|
29
|
+
throw new Error(alg.includes(FALLBACK_ALG)
|
|
30
|
+
? `Client authentication method "${method}" requires at least one "${FALLBACK_ALG}" signing key with a "kid" property`
|
|
35
31
|
: // AS is not compliant with the ATproto OAuth spec.
|
|
36
|
-
`Authorization server requires "${method}" authentication method, but does not support "${
|
|
32
|
+
`Authorization server requires "${method}" authentication method, but does not support "${FALLBACK_ALG}" algorithm.`);
|
|
37
33
|
}
|
|
38
34
|
if (method === 'none') {
|
|
39
35
|
return { method: 'none' };
|
|
@@ -48,10 +44,10 @@ function negotiateClientAuthMethod(serverMetadata, clientMetadata, keyset) {
|
|
|
48
44
|
* long usable (either because the AS changed, of because the key is no longer
|
|
49
45
|
* available in the keyset).
|
|
50
46
|
*/
|
|
51
|
-
function createClientCredentialsFactory(authMethod, serverMetadata, clientMetadata, runtime, keyset) {
|
|
47
|
+
export function createClientCredentialsFactory(authMethod, serverMetadata, clientMetadata, runtime, keyset) {
|
|
52
48
|
// Ensure the AS still supports the auth method.
|
|
53
49
|
if (!supportedMethods(serverMetadata).includes(authMethod.method)) {
|
|
54
|
-
throw new
|
|
50
|
+
throw new AuthMethodUnsatisfiableError(`Client authentication method "${authMethod.method}" no longer supported`);
|
|
55
51
|
}
|
|
56
52
|
if (authMethod.method === 'none') {
|
|
57
53
|
return () => ({
|
|
@@ -75,7 +71,7 @@ function createClientCredentialsFactory(authMethod, serverMetadata, clientMetada
|
|
|
75
71
|
return async () => ({
|
|
76
72
|
payload: {
|
|
77
73
|
client_id: clientMetadata.client_id,
|
|
78
|
-
client_assertion_type:
|
|
74
|
+
client_assertion_type: CLIENT_ASSERTION_TYPE_JWT_BEARER,
|
|
79
75
|
client_assertion: await key.createJwt({ alg }, {
|
|
80
76
|
// > The JWT MUST contain an "iss" (issuer) claim that contains a
|
|
81
77
|
// > unique identifier for the entity that issued the JWT.
|
|
@@ -103,12 +99,12 @@ function createClientCredentialsFactory(authMethod, serverMetadata, clientMetada
|
|
|
103
99
|
});
|
|
104
100
|
}
|
|
105
101
|
catch (cause) {
|
|
106
|
-
throw new
|
|
102
|
+
throw new AuthMethodUnsatisfiableError('Failed to load private key', {
|
|
107
103
|
cause,
|
|
108
104
|
});
|
|
109
105
|
}
|
|
110
106
|
}
|
|
111
|
-
throw new
|
|
107
|
+
throw new AuthMethodUnsatisfiableError(
|
|
112
108
|
// @ts-expect-error
|
|
113
109
|
`Unsupported auth method ${authMethod.method}`);
|
|
114
110
|
}
|
|
@@ -124,7 +120,7 @@ function supportedAlgs(serverMetadata) {
|
|
|
124
120
|
// > cryptographic system [for client authentication].
|
|
125
121
|
//
|
|
126
122
|
// https://atproto.com/specs/oauth#confidential-client-authentication
|
|
127
|
-
|
|
123
|
+
FALLBACK_ALG,
|
|
128
124
|
]);
|
|
129
125
|
}
|
|
130
126
|
//# sourceMappingURL=oauth-client-auth.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-client-auth.js","sourceRoot":"","sources":["../src/oauth-client-auth.ts"],"names":[],"mappings":"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","sourcesContent":["import { Keyset } from '@atproto/jwk'\nimport {\n CLIENT_ASSERTION_TYPE_JWT_BEARER,\n OAuthAuthorizationServerMetadata,\n OAuthClientCredentials,\n} from '@atproto/oauth-types'\nimport { FALLBACK_ALG } from './constants.js'\nimport { AuthMethodUnsatisfiableError } from './errors/auth-method-unsatisfiable-error.js'\nimport { Runtime } from './runtime.js'\nimport { ClientMetadata } from './types.js'\nimport { Awaitable } from './util.js'\n\nexport type ClientAuthMethod =\n | { method: 'none' }\n | { method: 'private_key_jwt'; kid: string }\n\nexport function negotiateClientAuthMethod(\n serverMetadata: OAuthAuthorizationServerMetadata,\n clientMetadata: ClientMetadata,\n keyset?: Keyset,\n): ClientAuthMethod {\n const method = clientMetadata.token_endpoint_auth_method\n\n // @NOTE ATproto spec requires that AS support both \"none\" and\n // \"private_key_jwt\", and that clients use one of the other. The following\n // check ensures that the AS is indeed compliant with this client's\n // configuration.\n const methods = supportedMethods(serverMetadata)\n if (!methods.includes(method)) {\n throw new Error(\n `The server does not support \"${method}\" authentication. Supported methods are: ${methods.join(\n ', ',\n )}.`,\n )\n }\n\n if (method === 'private_key_jwt') {\n // Invalid client configuration. This should not happen as\n // \"validateClientMetadata\" already check this.\n if (!keyset) throw new Error('A keyset is required for private_key_jwt')\n\n const alg = supportedAlgs(serverMetadata)\n\n // @NOTE we can't use `keyset.findPrivateKey` here because we can't enforce\n // that the returned key contains a \"kid\". The following implementation is\n // more robust against keysets containing keys without a \"kid\" property.\n for (const key of keyset.list({ alg, usage: 'sign' })) {\n // Return the first key from the key set that matches the server's\n // supported algorithms.\n if (key.kid) return { method: 'private_key_jwt', kid: key.kid }\n }\n\n throw new Error(\n alg.includes(FALLBACK_ALG)\n ? `Client authentication method \"${method}\" requires at least one \"${FALLBACK_ALG}\" signing key with a \"kid\" property`\n : // AS is not compliant with the ATproto OAuth spec.\n `Authorization server requires \"${method}\" authentication method, but does not support \"${FALLBACK_ALG}\" algorithm.`,\n )\n }\n\n if (method === 'none') {\n return { method: 'none' }\n }\n\n throw new Error(\n `The ATProto OAuth spec requires that client use either \"none\" or \"private_key_jwt\" authentication method.` +\n (method === 'client_secret_basic'\n ? ' You might want to explicitly set \"token_endpoint_auth_method\" to one of those values in the client metadata document.'\n : ` You set \"${method}\" which is not allowed.`),\n )\n}\n\nexport type ClientCredentialsFactory = () => Awaitable<{\n headers?: Record<string, string>\n payload?: OAuthClientCredentials\n}>\n\n/**\n * @throws {AuthMethodUnsatisfiableError} if the authentication method is no\n * long usable (either because the AS changed, of because the key is no longer\n * available in the keyset).\n */\nexport function createClientCredentialsFactory(\n authMethod: ClientAuthMethod,\n serverMetadata: OAuthAuthorizationServerMetadata,\n clientMetadata: ClientMetadata,\n runtime: Runtime,\n keyset?: Keyset,\n): ClientCredentialsFactory {\n // Ensure the AS still supports the auth method.\n if (!supportedMethods(serverMetadata).includes(authMethod.method)) {\n throw new AuthMethodUnsatisfiableError(\n `Client authentication method \"${authMethod.method}\" no longer supported`,\n )\n }\n\n if (authMethod.method === 'none') {\n return () => ({\n payload: {\n client_id: clientMetadata.client_id,\n },\n })\n }\n\n if (authMethod.method === 'private_key_jwt') {\n try {\n // The client used to be a confidential client but no longer has a keyset.\n if (!keyset) throw new Error('A keyset is required for private_key_jwt')\n\n // @NOTE throws if no matching key can be found\n const { key, alg } = keyset.findPrivateKey({\n usage: 'sign',\n kid: authMethod.kid,\n alg: supportedAlgs(serverMetadata),\n })\n\n // https://www.rfc-editor.org/rfc/rfc7523.html#section-3\n return async () => ({\n payload: {\n client_id: clientMetadata.client_id,\n client_assertion_type: CLIENT_ASSERTION_TYPE_JWT_BEARER,\n client_assertion: await key.createJwt(\n { alg },\n {\n // > The JWT MUST contain an \"iss\" (issuer) claim that contains a\n // > unique identifier for the entity that issued the JWT.\n iss: clientMetadata.client_id,\n // > For client authentication, the subject MUST be the\n // > \"client_id\" of the OAuth client.\n sub: clientMetadata.client_id,\n // > The JWT MUST contain an \"aud\" (audience) claim containing a value\n // > that identifies the authorization server as an intended audience.\n // > The token endpoint URL of the authorization server MAY be used as a\n // > value for an \"aud\" element to identify the authorization server as an\n // > intended audience of the JWT.\n aud: serverMetadata.issuer,\n // > The JWT MAY contain a \"jti\" (JWT ID) claim that provides a\n // > unique identifier for the token.\n jti: await runtime.generateNonce(),\n // > The JWT MAY contain an \"iat\" (issued at) claim that\n // > identifies the time at which the JWT was issued.\n iat: Math.floor(Date.now() / 1000),\n // > The JWT MUST contain an \"exp\" (expiration time) claim that\n // > limits the time window during which the JWT can be used.\n exp: Math.floor(Date.now() / 1000) + 60, // 1 minute\n },\n ),\n },\n })\n } catch (cause) {\n throw new AuthMethodUnsatisfiableError('Failed to load private key', {\n cause,\n })\n }\n }\n\n throw new AuthMethodUnsatisfiableError(\n // @ts-expect-error\n `Unsupported auth method ${authMethod.method}`,\n )\n}\n\nfunction supportedMethods(serverMetadata: OAuthAuthorizationServerMetadata) {\n return serverMetadata['token_endpoint_auth_methods_supported']\n}\n\nfunction supportedAlgs(serverMetadata: OAuthAuthorizationServerMetadata) {\n return (\n serverMetadata['token_endpoint_auth_signing_alg_values_supported'] ?? [\n // @NOTE If not specified, assume that the server supports the ES256\n // algorithm, as prescribed by the spec:\n //\n // > Clients and Authorization Servers currently must support the ES256\n // > cryptographic system [for client authentication].\n //\n // https://atproto.com/specs/oauth#confidential-client-authentication\n FALLBACK_ALG,\n ]\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"oauth-client-auth.js","sourceRoot":"","sources":["../src/oauth-client-auth.ts"],"names":[],"mappings":"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","sourcesContent":["import { Keyset } from '@atproto/jwk'\nimport {\n CLIENT_ASSERTION_TYPE_JWT_BEARER,\n OAuthAuthorizationServerMetadata,\n OAuthClientCredentials,\n} from '@atproto/oauth-types'\nimport { FALLBACK_ALG } from './constants.js'\nimport { AuthMethodUnsatisfiableError } from './errors/auth-method-unsatisfiable-error.js'\nimport { Runtime } from './runtime.js'\nimport { ClientMetadata } from './types.js'\nimport { Awaitable } from './util.js'\n\nexport type ClientAuthMethod =\n | { method: 'none' }\n | { method: 'private_key_jwt'; kid: string }\n\nexport function negotiateClientAuthMethod(\n serverMetadata: OAuthAuthorizationServerMetadata,\n clientMetadata: ClientMetadata,\n keyset?: Keyset,\n): ClientAuthMethod {\n const method = clientMetadata.token_endpoint_auth_method\n\n // @NOTE ATproto spec requires that AS support both \"none\" and\n // \"private_key_jwt\", and that clients use one of the other. The following\n // check ensures that the AS is indeed compliant with this client's\n // configuration.\n const methods = supportedMethods(serverMetadata)\n if (!methods.includes(method)) {\n throw new Error(\n `The server does not support \"${method}\" authentication. Supported methods are: ${methods.join(\n ', ',\n )}.`,\n )\n }\n\n if (method === 'private_key_jwt') {\n // Invalid client configuration. This should not happen as\n // \"validateClientMetadata\" already check this.\n if (!keyset) throw new Error('A keyset is required for private_key_jwt')\n\n const alg = supportedAlgs(serverMetadata)\n\n // @NOTE we can't use `keyset.findPrivateKey` here because we can't enforce\n // that the returned key contains a \"kid\". The following implementation is\n // more robust against keysets containing keys without a \"kid\" property.\n for (const key of keyset.list({ alg, usage: 'sign' })) {\n // Return the first key from the key set that matches the server's\n // supported algorithms.\n if (key.kid) return { method: 'private_key_jwt', kid: key.kid }\n }\n\n throw new Error(\n alg.includes(FALLBACK_ALG)\n ? `Client authentication method \"${method}\" requires at least one \"${FALLBACK_ALG}\" signing key with a \"kid\" property`\n : // AS is not compliant with the ATproto OAuth spec.\n `Authorization server requires \"${method}\" authentication method, but does not support \"${FALLBACK_ALG}\" algorithm.`,\n )\n }\n\n if (method === 'none') {\n return { method: 'none' }\n }\n\n throw new Error(\n `The ATProto OAuth spec requires that client use either \"none\" or \"private_key_jwt\" authentication method.` +\n (method === 'client_secret_basic'\n ? ' You might want to explicitly set \"token_endpoint_auth_method\" to one of those values in the client metadata document.'\n : ` You set \"${method}\" which is not allowed.`),\n )\n}\n\nexport type ClientCredentialsFactory = () => Awaitable<{\n headers?: Record<string, string>\n payload?: OAuthClientCredentials\n}>\n\n/**\n * @throws {AuthMethodUnsatisfiableError} if the authentication method is no\n * long usable (either because the AS changed, of because the key is no longer\n * available in the keyset).\n */\nexport function createClientCredentialsFactory(\n authMethod: ClientAuthMethod,\n serverMetadata: OAuthAuthorizationServerMetadata,\n clientMetadata: ClientMetadata,\n runtime: Runtime,\n keyset?: Keyset,\n): ClientCredentialsFactory {\n // Ensure the AS still supports the auth method.\n if (!supportedMethods(serverMetadata).includes(authMethod.method)) {\n throw new AuthMethodUnsatisfiableError(\n `Client authentication method \"${authMethod.method}\" no longer supported`,\n )\n }\n\n if (authMethod.method === 'none') {\n return () => ({\n payload: {\n client_id: clientMetadata.client_id,\n },\n })\n }\n\n if (authMethod.method === 'private_key_jwt') {\n try {\n // The client used to be a confidential client but no longer has a keyset.\n if (!keyset) throw new Error('A keyset is required for private_key_jwt')\n\n // @NOTE throws if no matching key can be found\n const { key, alg } = keyset.findPrivateKey({\n usage: 'sign',\n kid: authMethod.kid,\n alg: supportedAlgs(serverMetadata),\n })\n\n // https://www.rfc-editor.org/rfc/rfc7523.html#section-3\n return async () => ({\n payload: {\n client_id: clientMetadata.client_id,\n client_assertion_type: CLIENT_ASSERTION_TYPE_JWT_BEARER,\n client_assertion: await key.createJwt(\n { alg },\n {\n // > The JWT MUST contain an \"iss\" (issuer) claim that contains a\n // > unique identifier for the entity that issued the JWT.\n iss: clientMetadata.client_id,\n // > For client authentication, the subject MUST be the\n // > \"client_id\" of the OAuth client.\n sub: clientMetadata.client_id,\n // > The JWT MUST contain an \"aud\" (audience) claim containing a value\n // > that identifies the authorization server as an intended audience.\n // > The token endpoint URL of the authorization server MAY be used as a\n // > value for an \"aud\" element to identify the authorization server as an\n // > intended audience of the JWT.\n aud: serverMetadata.issuer,\n // > The JWT MAY contain a \"jti\" (JWT ID) claim that provides a\n // > unique identifier for the token.\n jti: await runtime.generateNonce(),\n // > The JWT MAY contain an \"iat\" (issued at) claim that\n // > identifies the time at which the JWT was issued.\n iat: Math.floor(Date.now() / 1000),\n // > The JWT MUST contain an \"exp\" (expiration time) claim that\n // > limits the time window during which the JWT can be used.\n exp: Math.floor(Date.now() / 1000) + 60, // 1 minute\n },\n ),\n },\n })\n } catch (cause) {\n throw new AuthMethodUnsatisfiableError('Failed to load private key', {\n cause,\n })\n }\n }\n\n throw new AuthMethodUnsatisfiableError(\n // @ts-expect-error\n `Unsupported auth method ${authMethod.method}`,\n )\n}\n\nfunction supportedMethods(serverMetadata: OAuthAuthorizationServerMetadata) {\n return serverMetadata['token_endpoint_auth_methods_supported']\n}\n\nfunction supportedAlgs(serverMetadata: OAuthAuthorizationServerMetadata) {\n return (\n serverMetadata['token_endpoint_auth_signing_alg_values_supported'] ?? [\n // @NOTE If not specified, assume that the server supports the ES256\n // algorithm, as prescribed by the spec:\n //\n // > Clients and Authorization Servers currently must support the ES256\n // > cryptographic system [for client authentication].\n //\n // https://atproto.com/specs/oauth#confidential-client-authentication\n FALLBACK_ALG,\n ]\n )\n}\n"]}
|
package/dist/oauth-client.js
CHANGED
|
@@ -1,28 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const runtime_js_1 = require("./runtime.js");
|
|
23
|
-
const session_getter_js_1 = require("./session-getter.js");
|
|
24
|
-
const validate_client_metadata_js_1 = require("./validate-client-metadata.js");
|
|
25
|
-
class OAuthClient {
|
|
1
|
+
import { Key, Keyset } from '@atproto/jwk';
|
|
2
|
+
import { oauthClientMetadataSchema, } from '@atproto/oauth-types';
|
|
3
|
+
import { assertAtprotoDid, } from '@atproto-labs/did-resolver';
|
|
4
|
+
import { HANDLE_INVALID } from '@atproto-labs/identity-resolver';
|
|
5
|
+
import { SimpleStoreMemory } from '@atproto-labs/simple-store-memory';
|
|
6
|
+
import { FALLBACK_ALG } from './constants.js';
|
|
7
|
+
import { AuthMethodUnsatisfiableError } from './errors/auth-method-unsatisfiable-error.js';
|
|
8
|
+
import { TokenRevokedError } from './errors/token-revoked-error.js';
|
|
9
|
+
import { createIdentityResolver, } from './identity-resolver.js';
|
|
10
|
+
import { OAuthAuthorizationServerMetadataResolver, } from './oauth-authorization-server-metadata-resolver.js';
|
|
11
|
+
import { OAuthCallbackError } from './oauth-callback-error.js';
|
|
12
|
+
import { negotiateClientAuthMethod } from './oauth-client-auth.js';
|
|
13
|
+
import { OAuthProtectedResourceMetadataResolver, } from './oauth-protected-resource-metadata-resolver.js';
|
|
14
|
+
import { OAuthResolver } from './oauth-resolver.js';
|
|
15
|
+
import { OAuthServerFactory } from './oauth-server-factory.js';
|
|
16
|
+
import { OAuthSession } from './oauth-session.js';
|
|
17
|
+
import { Runtime } from './runtime.js';
|
|
18
|
+
import { SessionGetter, isExpectedSessionError, } from './session-getter.js';
|
|
19
|
+
import { validateClientMetadata } from './validate-client-metadata.js';
|
|
20
|
+
export { Key, Keyset };
|
|
21
|
+
export class OAuthClient {
|
|
26
22
|
static async fetchMetadata({ clientId, fetch = globalThis.fetch, signal, }) {
|
|
27
23
|
signal?.throwIfAborted();
|
|
28
24
|
const request = new Request(clientId, {
|
|
@@ -42,86 +38,29 @@ class OAuthClient {
|
|
|
42
38
|
}
|
|
43
39
|
const json = await response.json();
|
|
44
40
|
signal?.throwIfAborted();
|
|
45
|
-
return
|
|
41
|
+
return oauthClientMetadataSchema.parse(json);
|
|
46
42
|
}
|
|
47
43
|
constructor(options) {
|
|
48
|
-
|
|
49
|
-
Object.defineProperty(this, "clientMetadata", {
|
|
50
|
-
enumerable: true,
|
|
51
|
-
configurable: true,
|
|
52
|
-
writable: true,
|
|
53
|
-
value: void 0
|
|
54
|
-
});
|
|
55
|
-
Object.defineProperty(this, "responseMode", {
|
|
56
|
-
enumerable: true,
|
|
57
|
-
configurable: true,
|
|
58
|
-
writable: true,
|
|
59
|
-
value: void 0
|
|
60
|
-
});
|
|
61
|
-
Object.defineProperty(this, "keyset", {
|
|
62
|
-
enumerable: true,
|
|
63
|
-
configurable: true,
|
|
64
|
-
writable: true,
|
|
65
|
-
value: void 0
|
|
66
|
-
});
|
|
67
|
-
// Services
|
|
68
|
-
Object.defineProperty(this, "runtime", {
|
|
69
|
-
enumerable: true,
|
|
70
|
-
configurable: true,
|
|
71
|
-
writable: true,
|
|
72
|
-
value: void 0
|
|
73
|
-
});
|
|
74
|
-
Object.defineProperty(this, "fetch", {
|
|
75
|
-
enumerable: true,
|
|
76
|
-
configurable: true,
|
|
77
|
-
writable: true,
|
|
78
|
-
value: void 0
|
|
79
|
-
});
|
|
80
|
-
Object.defineProperty(this, "oauthResolver", {
|
|
81
|
-
enumerable: true,
|
|
82
|
-
configurable: true,
|
|
83
|
-
writable: true,
|
|
84
|
-
value: void 0
|
|
85
|
-
});
|
|
86
|
-
Object.defineProperty(this, "serverFactory", {
|
|
87
|
-
enumerable: true,
|
|
88
|
-
configurable: true,
|
|
89
|
-
writable: true,
|
|
90
|
-
value: void 0
|
|
91
|
-
});
|
|
92
|
-
// Stores
|
|
93
|
-
Object.defineProperty(this, "sessionGetter", {
|
|
94
|
-
enumerable: true,
|
|
95
|
-
configurable: true,
|
|
96
|
-
writable: true,
|
|
97
|
-
value: void 0
|
|
98
|
-
});
|
|
99
|
-
Object.defineProperty(this, "stateStore", {
|
|
100
|
-
enumerable: true,
|
|
101
|
-
configurable: true,
|
|
102
|
-
writable: true,
|
|
103
|
-
value: void 0
|
|
104
|
-
});
|
|
105
|
-
const { stateStore, sessionStore, dpopNonceCache = new simple_store_memory_1.SimpleStoreMemory({ ttl: 60e3, max: 100 }), authorizationServerMetadataCache = new simple_store_memory_1.SimpleStoreMemory({
|
|
44
|
+
const { stateStore, sessionStore, dpopNonceCache = new SimpleStoreMemory({ ttl: 60e3, max: 100 }), authorizationServerMetadataCache = new SimpleStoreMemory({
|
|
106
45
|
ttl: 60e3,
|
|
107
46
|
max: 100,
|
|
108
|
-
}), protectedResourceMetadataCache = new
|
|
47
|
+
}), protectedResourceMetadataCache = new SimpleStoreMemory({
|
|
109
48
|
ttl: 60e3,
|
|
110
49
|
max: 100,
|
|
111
50
|
}), responseMode, clientMetadata, runtimeImplementation, keyset, } = options;
|
|
112
51
|
this.keyset = keyset
|
|
113
|
-
? keyset instanceof
|
|
52
|
+
? keyset instanceof Keyset
|
|
114
53
|
? keyset
|
|
115
|
-
: new
|
|
54
|
+
: new Keyset(keyset)
|
|
116
55
|
: undefined;
|
|
117
|
-
this.clientMetadata =
|
|
56
|
+
this.clientMetadata = validateClientMetadata(clientMetadata, this.keyset);
|
|
118
57
|
this.responseMode = responseMode;
|
|
119
|
-
this.runtime = new
|
|
58
|
+
this.runtime = new Runtime(runtimeImplementation);
|
|
120
59
|
this.fetch = options.fetch ?? globalThis.fetch;
|
|
121
|
-
this.oauthResolver = new
|
|
122
|
-
this.serverFactory = new
|
|
60
|
+
this.oauthResolver = new OAuthResolver(createIdentityResolver(options), new OAuthProtectedResourceMetadataResolver(protectedResourceMetadataCache, this.fetch, { allowHttpResource: options.allowHttp }), new OAuthAuthorizationServerMetadataResolver(authorizationServerMetadataCache, this.fetch, { allowHttpIssuer: options.allowHttp }));
|
|
61
|
+
this.serverFactory = new OAuthServerFactory(this.clientMetadata, this.runtime, this.oauthResolver, this.fetch, this.keyset, dpopNonceCache);
|
|
123
62
|
this.stateStore = stateStore;
|
|
124
|
-
this.sessionGetter = new
|
|
63
|
+
this.sessionGetter = new SessionGetter(sessionStore, this.serverFactory, this.runtime, options);
|
|
125
64
|
}
|
|
126
65
|
// Exposed as public API for convenience
|
|
127
66
|
get identityResolver() {
|
|
@@ -140,8 +79,8 @@ class OAuthClient {
|
|
|
140
79
|
signal,
|
|
141
80
|
});
|
|
142
81
|
const pkce = await this.runtime.generatePKCE();
|
|
143
|
-
const dpopKey = await this.runtime.generateKey(metadata.dpop_signing_alg_values_supported || [
|
|
144
|
-
const authMethod =
|
|
82
|
+
const dpopKey = await this.runtime.generateKey(metadata.dpop_signing_alg_values_supported || [FALLBACK_ALG]);
|
|
83
|
+
const authMethod = negotiateClientAuthMethod(metadata, this.clientMetadata, this.keyset);
|
|
145
84
|
const state = await this.runtime.generateNonce();
|
|
146
85
|
await this.stateStore.set(state, {
|
|
147
86
|
iss: metadata.issuer,
|
|
@@ -158,7 +97,7 @@ class OAuthClient {
|
|
|
158
97
|
code_challenge_method: pkce.method,
|
|
159
98
|
state,
|
|
160
99
|
login_hint: identityInfo
|
|
161
|
-
? identityInfo.handle !==
|
|
100
|
+
? identityInfo.handle !== HANDLE_INVALID
|
|
162
101
|
? identityInfo.handle
|
|
163
102
|
: identityInfo.did
|
|
164
103
|
: undefined,
|
|
@@ -217,14 +156,14 @@ class OAuthClient {
|
|
|
217
156
|
const responseJwt = params.get('response');
|
|
218
157
|
if (responseJwt != null) {
|
|
219
158
|
// https://openid.net/specs/oauth-v2-jarm.html
|
|
220
|
-
throw new
|
|
159
|
+
throw new OAuthCallbackError(params, 'JARM not supported');
|
|
221
160
|
}
|
|
222
161
|
const issuerParam = params.get('iss');
|
|
223
162
|
const stateParam = params.get('state');
|
|
224
163
|
const errorParam = params.get('error');
|
|
225
164
|
const codeParam = params.get('code');
|
|
226
165
|
if (!stateParam) {
|
|
227
|
-
throw new
|
|
166
|
+
throw new OAuthCallbackError(params, 'Missing "state" parameter');
|
|
228
167
|
}
|
|
229
168
|
const stateData = await this.stateStore.get(stateParam);
|
|
230
169
|
if (stateData) {
|
|
@@ -232,26 +171,26 @@ class OAuthClient {
|
|
|
232
171
|
await this.stateStore.del(stateParam);
|
|
233
172
|
}
|
|
234
173
|
else {
|
|
235
|
-
throw new
|
|
174
|
+
throw new OAuthCallbackError(params, `Unknown authorization session "${stateParam}"`);
|
|
236
175
|
}
|
|
237
176
|
try {
|
|
238
177
|
if (errorParam != null) {
|
|
239
|
-
throw new
|
|
178
|
+
throw new OAuthCallbackError(params, undefined, stateData.appState);
|
|
240
179
|
}
|
|
241
180
|
if (!codeParam) {
|
|
242
|
-
throw new
|
|
181
|
+
throw new OAuthCallbackError(params, 'Missing "code" query param', stateData.appState);
|
|
243
182
|
}
|
|
244
183
|
const server = await this.serverFactory.fromIssuer(stateData.iss, stateData.authMethod, stateData.dpopKey);
|
|
245
184
|
if (issuerParam != null) {
|
|
246
185
|
if (!server.issuer) {
|
|
247
|
-
throw new
|
|
186
|
+
throw new OAuthCallbackError(params, 'Issuer not found in metadata', stateData.appState);
|
|
248
187
|
}
|
|
249
188
|
if (server.issuer !== issuerParam) {
|
|
250
|
-
throw new
|
|
189
|
+
throw new OAuthCallbackError(params, 'Issuer mismatch', stateData.appState);
|
|
251
190
|
}
|
|
252
191
|
}
|
|
253
192
|
else if (server.serverMetadata.authorization_response_iss_parameter_supported) {
|
|
254
|
-
throw new
|
|
193
|
+
throw new OAuthCallbackError(params, 'iss missing from the response', stateData.appState);
|
|
255
194
|
}
|
|
256
195
|
const tokenSet = await server.exchangeCode(codeParam, stateData.verifier, options?.redirect_uri ?? server.clientMetadata.redirect_uris[0]);
|
|
257
196
|
// We revoke any existing session first to avoid leaving orphaned sessions
|
|
@@ -279,7 +218,7 @@ class OAuthClient {
|
|
|
279
218
|
catch (err) {
|
|
280
219
|
// Make sure, whatever the underlying error, that the appState is
|
|
281
220
|
// available in the calling code
|
|
282
|
-
throw
|
|
221
|
+
throw OAuthCallbackError.from(err, params, stateData.appState);
|
|
283
222
|
}
|
|
284
223
|
}
|
|
285
224
|
/**
|
|
@@ -290,7 +229,7 @@ class OAuthClient {
|
|
|
290
229
|
*/
|
|
291
230
|
async restore(sub, refresh = 'auto') {
|
|
292
231
|
// sub arg is lightly typed for convenience of library user
|
|
293
|
-
|
|
232
|
+
assertAtprotoDid(sub);
|
|
294
233
|
const { dpopKey, authMethod, tokenSet } = await this.sessionGetter.getSession(sub, refresh);
|
|
295
234
|
try {
|
|
296
235
|
const server = await this.serverFactory.fromIssuer(tokenSet.iss, authMethod, dpopKey, {
|
|
@@ -300,7 +239,7 @@ class OAuthClient {
|
|
|
300
239
|
return this.createSession(server, sub);
|
|
301
240
|
}
|
|
302
241
|
catch (err) {
|
|
303
|
-
if (err instanceof
|
|
242
|
+
if (err instanceof AuthMethodUnsatisfiableError) {
|
|
304
243
|
await this.sessionGetter.delStored(sub, err);
|
|
305
244
|
}
|
|
306
245
|
throw err;
|
|
@@ -308,9 +247,9 @@ class OAuthClient {
|
|
|
308
247
|
}
|
|
309
248
|
async revoke(sub) {
|
|
310
249
|
// sub arg is lightly typed for convenience of library user
|
|
311
|
-
|
|
250
|
+
assertAtprotoDid(sub);
|
|
312
251
|
const res = await this.sessionGetter.getSession(sub, false).catch((err) => {
|
|
313
|
-
if (
|
|
252
|
+
if (isExpectedSessionError(err))
|
|
314
253
|
return null;
|
|
315
254
|
throw err;
|
|
316
255
|
});
|
|
@@ -325,12 +264,11 @@ class OAuthClient {
|
|
|
325
264
|
await server.revoke(tokenSet.access_token);
|
|
326
265
|
}
|
|
327
266
|
finally {
|
|
328
|
-
await this.sessionGetter.delStored(sub, new
|
|
267
|
+
await this.sessionGetter.delStored(sub, new TokenRevokedError(sub));
|
|
329
268
|
}
|
|
330
269
|
}
|
|
331
270
|
createSession(server, sub) {
|
|
332
|
-
return new
|
|
271
|
+
return new OAuthSession(server, sub, this.sessionGetter, this.fetch);
|
|
333
272
|
}
|
|
334
273
|
}
|
|
335
|
-
exports.OAuthClient = OAuthClient;
|
|
336
274
|
//# sourceMappingURL=oauth-client.js.map
|
package/dist/oauth-client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-client.js","sourceRoot":"","sources":["../src/oauth-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { Key, Keyset } from '@atproto/jwk'\nimport {\n OAuthAuthorizationRequestParameters,\n OAuthClientIdDiscoverable,\n OAuthClientMetadata,\n OAuthClientMetadataInput,\n OAuthResponseMode,\n oauthClientMetadataSchema,\n} from '@atproto/oauth-types'\nimport {\n AtprotoDid,\n DidCache,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n type DidResolverCommonOptions,\n assertAtprotoDid,\n} from '@atproto-labs/did-resolver'\nimport { Fetch } from '@atproto-labs/fetch'\nimport { HandleCache, HandleResolver } from '@atproto-labs/handle-resolver'\nimport { HANDLE_INVALID } from '@atproto-labs/identity-resolver'\nimport { SimpleStoreMemory } from '@atproto-labs/simple-store-memory'\nimport { FALLBACK_ALG } from './constants.js'\nimport { AuthMethodUnsatisfiableError } from './errors/auth-method-unsatisfiable-error.js'\nimport { TokenRevokedError } from './errors/token-revoked-error.js'\nimport {\n CreateIdentityResolverOptions,\n createIdentityResolver,\n} from './identity-resolver.js'\nimport {\n AuthorizationServerMetadataCache,\n OAuthAuthorizationServerMetadataResolver,\n} from './oauth-authorization-server-metadata-resolver.js'\nimport { OAuthCallbackError } from './oauth-callback-error.js'\nimport { negotiateClientAuthMethod } from './oauth-client-auth.js'\nimport {\n OAuthProtectedResourceMetadataResolver,\n ProtectedResourceMetadataCache,\n} from './oauth-protected-resource-metadata-resolver.js'\nimport { OAuthResolver } from './oauth-resolver.js'\nimport { DpopNonceCache, OAuthServerAgent } from './oauth-server-agent.js'\nimport { OAuthServerFactory } from './oauth-server-factory.js'\nimport { OAuthSession } from './oauth-session.js'\nimport { RuntimeImplementation } from './runtime-implementation.js'\nimport { Runtime } from './runtime.js'\nimport {\n SessionGetter,\n SessionHooks,\n SessionStore,\n isExpectedSessionError,\n} from './session-getter.js'\nimport { InternalStateData, StateStore } from './state-store.js'\nimport { AuthorizeOptions, CallbackOptions, ClientMetadata } from './types.js'\nimport { validateClientMetadata } from './validate-client-metadata.js'\n\n// Export all types needed to construct OAuthClientOptions\nexport type {\n AuthorizationServerMetadataCache,\n CreateIdentityResolverOptions,\n DidCache,\n DpopNonceCache,\n Fetch,\n HandleCache,\n HandleResolver,\n InternalStateData,\n OAuthClientMetadata,\n OAuthClientMetadataInput,\n OAuthResponseMode,\n ProtectedResourceMetadataCache,\n RuntimeImplementation,\n SessionHooks,\n SessionStore,\n StateStore,\n}\n\nexport { Key, Keyset }\n\nexport type OAuthClientOptions = {\n // Config\n responseMode: OAuthResponseMode\n clientMetadata: Readonly<OAuthClientMetadataInput>\n keyset?: Keyset | Iterable<Key | undefined | null | false>\n /**\n * Determines if the client will allow communicating with the OAuth Servers\n * (Authorization & Resource), or to retrieve \"did:web\" documents, over\n * unsafe HTTP connections. It is recommended to set this to `true` only for\n * development purposes.\n *\n * @note This does not affect the identity resolution mechanism, which will\n * allow HTTP connections to the PLC Directory (if the provided directory url\n * is \"http:\" based).\n * @default false\n * @see {@link OAuthProtectedResourceMetadataResolver.allowHttpResource}\n * @see {@link OAuthAuthorizationServerMetadataResolver.allowHttpIssuer}\n * @see {@link DidResolverCommonOptions.allowHttp}\n */\n allowHttp?: boolean\n\n // Stores\n stateStore: StateStore\n sessionStore: SessionStore\n authorizationServerMetadataCache?: AuthorizationServerMetadataCache\n protectedResourceMetadataCache?: ProtectedResourceMetadataCache\n dpopNonceCache?: DpopNonceCache\n\n // Services\n runtimeImplementation: RuntimeImplementation\n fetch?: Fetch\n} & CreateIdentityResolverOptions &\n SessionHooks\n\nexport type OAuthClientFetchMetadataOptions = {\n clientId: OAuthClientIdDiscoverable\n fetch?: Fetch\n signal?: AbortSignal\n}\n\nexport class OAuthClient {\n static async fetchMetadata({\n clientId,\n fetch = globalThis.fetch,\n signal,\n }: OAuthClientFetchMetadataOptions) {\n signal?.throwIfAborted()\n\n const request = new Request(clientId, {\n redirect: 'error',\n signal: signal,\n })\n const response = await fetch(request)\n\n if (response.status !== 200) {\n response.body?.cancel?.()\n throw new TypeError(`Failed to fetch client metadata: ${response.status}`)\n }\n\n // https://www.ietf.org/archive/id/draft-ietf-oauth-client-id-metadata-document-00.html#section-4.1\n const mime = response.headers.get('content-type')?.split(';')[0].trim()\n if (mime !== 'application/json') {\n response.body?.cancel?.()\n throw new TypeError(`Invalid client metadata content type: ${mime}`)\n }\n\n const json: unknown = await response.json()\n\n signal?.throwIfAborted()\n\n return oauthClientMetadataSchema.parse(json)\n }\n\n // Config\n readonly clientMetadata: ClientMetadata\n readonly responseMode: OAuthResponseMode\n readonly keyset?: Keyset\n\n // Services\n readonly runtime: Runtime\n readonly fetch: Fetch\n readonly oauthResolver: OAuthResolver\n readonly serverFactory: OAuthServerFactory\n\n // Stores\n protected readonly sessionGetter: SessionGetter\n protected readonly stateStore: StateStore\n\n constructor(options: OAuthClientOptions) {\n const {\n stateStore,\n sessionStore,\n\n dpopNonceCache = new SimpleStoreMemory({ ttl: 60e3, max: 100 }),\n authorizationServerMetadataCache = new SimpleStoreMemory({\n ttl: 60e3,\n max: 100,\n }),\n protectedResourceMetadataCache = new SimpleStoreMemory({\n ttl: 60e3,\n max: 100,\n }),\n\n responseMode,\n clientMetadata,\n runtimeImplementation,\n keyset,\n } = options\n\n this.keyset = keyset\n ? keyset instanceof Keyset\n ? keyset\n : new Keyset(keyset)\n : undefined\n this.clientMetadata = validateClientMetadata(clientMetadata, this.keyset)\n this.responseMode = responseMode\n\n this.runtime = new Runtime(runtimeImplementation)\n this.fetch = options.fetch ?? globalThis.fetch\n this.oauthResolver = new OAuthResolver(\n createIdentityResolver(options),\n new OAuthProtectedResourceMetadataResolver(\n protectedResourceMetadataCache,\n this.fetch,\n { allowHttpResource: options.allowHttp },\n ),\n new OAuthAuthorizationServerMetadataResolver(\n authorizationServerMetadataCache,\n this.fetch,\n { allowHttpIssuer: options.allowHttp },\n ),\n )\n this.serverFactory = new OAuthServerFactory(\n this.clientMetadata,\n this.runtime,\n this.oauthResolver,\n this.fetch,\n this.keyset,\n dpopNonceCache,\n )\n\n this.stateStore = stateStore\n this.sessionGetter = new SessionGetter(\n sessionStore,\n this.serverFactory,\n this.runtime,\n options,\n )\n }\n\n // Exposed as public API for convenience\n get identityResolver() {\n return this.oauthResolver.identityResolver\n }\n\n get jwks() {\n return this.keyset?.publicJwks ?? ({ keys: [] as const } as const)\n }\n\n async authorize(\n input: string,\n { signal, ...options }: AuthorizeOptions = {},\n ): Promise<URL> {\n const redirectUri =\n options?.redirect_uri ?? this.clientMetadata.redirect_uris[0]\n if (!this.clientMetadata.redirect_uris.includes(redirectUri)) {\n // The server will enforce this, but let's catch it early\n throw new TypeError('Invalid redirect_uri')\n }\n\n const { identityInfo, metadata } = await this.oauthResolver.resolve(input, {\n signal,\n })\n\n const pkce = await this.runtime.generatePKCE()\n const dpopKey = await this.runtime.generateKey(\n metadata.dpop_signing_alg_values_supported || [FALLBACK_ALG],\n )\n\n const authMethod = negotiateClientAuthMethod(\n metadata,\n this.clientMetadata,\n this.keyset,\n )\n const state = await this.runtime.generateNonce()\n\n await this.stateStore.set(state, {\n iss: metadata.issuer,\n dpopKey,\n authMethod,\n verifier: pkce.verifier,\n appState: options?.state,\n })\n\n const parameters: OAuthAuthorizationRequestParameters = {\n ...options,\n\n client_id: this.clientMetadata.client_id,\n redirect_uri: redirectUri,\n code_challenge: pkce.challenge,\n code_challenge_method: pkce.method,\n state,\n login_hint: identityInfo\n ? identityInfo.handle !== HANDLE_INVALID\n ? identityInfo.handle\n : identityInfo.did\n : undefined,\n response_mode: this.responseMode,\n response_type: 'code' as const,\n scope: options?.scope ?? this.clientMetadata.scope,\n }\n\n const authorizationUrl = new URL(metadata.authorization_endpoint)\n\n // Since the user will be redirected to the authorization_endpoint url using\n // a browser, we need to make sure that the url is valid.\n if (\n authorizationUrl.protocol !== 'https:' &&\n authorizationUrl.protocol !== 'http:'\n ) {\n throw new TypeError(\n `Invalid authorization endpoint protocol: ${authorizationUrl.protocol}`,\n )\n }\n\n if (metadata.pushed_authorization_request_endpoint) {\n const server = await this.serverFactory.fromMetadata(\n metadata,\n authMethod,\n dpopKey,\n )\n const parResponse = await server.request(\n 'pushed_authorization_request',\n parameters,\n )\n\n authorizationUrl.searchParams.set(\n 'client_id',\n this.clientMetadata.client_id,\n )\n authorizationUrl.searchParams.set('request_uri', parResponse.request_uri)\n return authorizationUrl\n } else if (metadata.require_pushed_authorization_requests) {\n throw new Error(\n 'Server requires pushed authorization requests (PAR) but no PAR endpoint is available',\n )\n } else {\n for (const [key, value] of Object.entries(parameters)) {\n if (value) authorizationUrl.searchParams.set(key, String(value))\n }\n\n // Length of the URL that will be sent to the server\n const urlLength =\n authorizationUrl.pathname.length + authorizationUrl.search.length\n if (urlLength < 2048) {\n return authorizationUrl\n } else if (!metadata.pushed_authorization_request_endpoint) {\n throw new Error('Login URL too long')\n }\n }\n\n throw new Error(\n 'Server does not support pushed authorization requests (PAR)',\n )\n }\n\n /**\n * This method allows the client to proactively revoke the request_uri it\n * created through PAR.\n */\n async abortRequest(authorizeUrl: URL) {\n const requestUri = authorizeUrl.searchParams.get('request_uri')\n if (!requestUri) return\n\n // @NOTE This is not implemented here because, 1) the request server should\n // invalidate the request_uri after some delay anyways, and 2) I am not sure\n // that the revocation endpoint is even supposed to support this (and I\n // don't want to spend the time checking now).\n\n // @TODO investigate actual necessity & feasibility of this feature\n }\n\n async callback(\n params: URLSearchParams,\n options: CallbackOptions = {},\n ): Promise<{\n session: OAuthSession\n state: string | null\n }> {\n const responseJwt = params.get('response')\n if (responseJwt != null) {\n // https://openid.net/specs/oauth-v2-jarm.html\n throw new OAuthCallbackError(params, 'JARM not supported')\n }\n\n const issuerParam = params.get('iss')\n const stateParam = params.get('state')\n const errorParam = params.get('error')\n const codeParam = params.get('code')\n\n if (!stateParam) {\n throw new OAuthCallbackError(params, 'Missing \"state\" parameter')\n }\n const stateData = await this.stateStore.get(stateParam)\n if (stateData) {\n // Prevent any kind of replay\n await this.stateStore.del(stateParam)\n } else {\n throw new OAuthCallbackError(\n params,\n `Unknown authorization session \"${stateParam}\"`,\n )\n }\n\n try {\n if (errorParam != null) {\n throw new OAuthCallbackError(params, undefined, stateData.appState)\n }\n\n if (!codeParam) {\n throw new OAuthCallbackError(\n params,\n 'Missing \"code\" query param',\n stateData.appState,\n )\n }\n\n const server = await this.serverFactory.fromIssuer(\n stateData.iss,\n stateData.authMethod,\n stateData.dpopKey,\n )\n\n if (issuerParam != null) {\n if (!server.issuer) {\n throw new OAuthCallbackError(\n params,\n 'Issuer not found in metadata',\n stateData.appState,\n )\n }\n if (server.issuer !== issuerParam) {\n throw new OAuthCallbackError(\n params,\n 'Issuer mismatch',\n stateData.appState,\n )\n }\n } else if (\n server.serverMetadata.authorization_response_iss_parameter_supported\n ) {\n throw new OAuthCallbackError(\n params,\n 'iss missing from the response',\n stateData.appState,\n )\n }\n\n const tokenSet = await server.exchangeCode(\n codeParam,\n stateData.verifier,\n options?.redirect_uri ?? server.clientMetadata.redirect_uris[0],\n )\n\n // We revoke any existing session first to avoid leaving orphaned sessions\n // on the AS.\n try {\n await this.revoke(tokenSet.sub)\n } catch {\n // No existing session, or failed to get it. This is fine.\n }\n\n try {\n await this.sessionGetter.setStored(tokenSet.sub, {\n dpopKey: stateData.dpopKey,\n authMethod: server.authMethod,\n tokenSet,\n })\n\n const session = this.createSession(server, tokenSet.sub)\n\n return { session, state: stateData.appState ?? null }\n } catch (err) {\n await server.revoke(tokenSet.refresh_token || tokenSet.access_token)\n\n throw err\n }\n } catch (err) {\n // Make sure, whatever the underlying error, that the appState is\n // available in the calling code\n throw OAuthCallbackError.from(err, params, stateData.appState)\n }\n }\n\n /**\n * Load a stored session. This will refresh the token only if needed (about to\n * expire) by default.\n *\n * @see {@link SessionGetter.restore}\n */\n async restore(\n sub: string,\n refresh: boolean | 'auto' = 'auto',\n ): Promise<OAuthSession> {\n // sub arg is lightly typed for convenience of library user\n assertAtprotoDid(sub)\n\n const { dpopKey, authMethod, tokenSet } =\n await this.sessionGetter.getSession(sub, refresh)\n\n try {\n const server = await this.serverFactory.fromIssuer(\n tokenSet.iss,\n authMethod,\n dpopKey,\n {\n noCache: refresh === true,\n allowStale: refresh === false,\n },\n )\n\n return this.createSession(server, sub)\n } catch (err) {\n if (err instanceof AuthMethodUnsatisfiableError) {\n await this.sessionGetter.delStored(sub, err)\n }\n\n throw err\n }\n }\n\n async revoke(sub: string) {\n // sub arg is lightly typed for convenience of library user\n assertAtprotoDid(sub)\n\n const res = await this.sessionGetter.getSession(sub, false).catch((err) => {\n if (isExpectedSessionError(err)) return null\n throw err\n })\n\n if (!res) return\n\n const { dpopKey, authMethod, tokenSet } = res\n\n // NOT using `;(await this.restore(sub, false)).signOut()` because we want\n // the tokens to be deleted even if it was not possible to fetch the issuer\n // data.\n try {\n const server = await this.serverFactory.fromIssuer(\n tokenSet.iss,\n authMethod,\n dpopKey,\n )\n await server.revoke(tokenSet.access_token)\n } finally {\n await this.sessionGetter.delStored(sub, new TokenRevokedError(sub))\n }\n }\n\n protected createSession(\n server: OAuthServerAgent,\n sub: AtprotoDid,\n ): OAuthSession {\n return new OAuthSession(server, sub, this.sessionGetter, this.fetch)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"oauth-client.js","sourceRoot":"","sources":["../src/oauth-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { Key, Keyset } from '@atproto/jwk'\nimport {\n OAuthAuthorizationRequestParameters,\n OAuthClientIdDiscoverable,\n OAuthClientMetadata,\n OAuthClientMetadataInput,\n OAuthResponseMode,\n oauthClientMetadataSchema,\n} from '@atproto/oauth-types'\nimport {\n AtprotoDid,\n DidCache,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n type DidResolverCommonOptions,\n assertAtprotoDid,\n} from '@atproto-labs/did-resolver'\nimport { Fetch } from '@atproto-labs/fetch'\nimport { HandleCache, HandleResolver } from '@atproto-labs/handle-resolver'\nimport { HANDLE_INVALID } from '@atproto-labs/identity-resolver'\nimport { SimpleStoreMemory } from '@atproto-labs/simple-store-memory'\nimport { FALLBACK_ALG } from './constants.js'\nimport { AuthMethodUnsatisfiableError } from './errors/auth-method-unsatisfiable-error.js'\nimport { TokenRevokedError } from './errors/token-revoked-error.js'\nimport {\n CreateIdentityResolverOptions,\n createIdentityResolver,\n} from './identity-resolver.js'\nimport {\n AuthorizationServerMetadataCache,\n OAuthAuthorizationServerMetadataResolver,\n} from './oauth-authorization-server-metadata-resolver.js'\nimport { OAuthCallbackError } from './oauth-callback-error.js'\nimport { negotiateClientAuthMethod } from './oauth-client-auth.js'\nimport {\n OAuthProtectedResourceMetadataResolver,\n ProtectedResourceMetadataCache,\n} from './oauth-protected-resource-metadata-resolver.js'\nimport { OAuthResolver } from './oauth-resolver.js'\nimport { DpopNonceCache, OAuthServerAgent } from './oauth-server-agent.js'\nimport { OAuthServerFactory } from './oauth-server-factory.js'\nimport { OAuthSession } from './oauth-session.js'\nimport { RuntimeImplementation } from './runtime-implementation.js'\nimport { Runtime } from './runtime.js'\nimport {\n SessionGetter,\n SessionHooks,\n SessionStore,\n isExpectedSessionError,\n} from './session-getter.js'\nimport { InternalStateData, StateStore } from './state-store.js'\nimport { AuthorizeOptions, CallbackOptions, ClientMetadata } from './types.js'\nimport { validateClientMetadata } from './validate-client-metadata.js'\n\n// Export all types needed to construct OAuthClientOptions\nexport type {\n AuthorizationServerMetadataCache,\n CreateIdentityResolverOptions,\n DidCache,\n DpopNonceCache,\n Fetch,\n HandleCache,\n HandleResolver,\n InternalStateData,\n OAuthClientMetadata,\n OAuthClientMetadataInput,\n OAuthResponseMode,\n ProtectedResourceMetadataCache,\n RuntimeImplementation,\n SessionHooks,\n SessionStore,\n StateStore,\n}\n\nexport { Key, Keyset }\n\nexport type OAuthClientOptions = {\n // Config\n responseMode: OAuthResponseMode\n clientMetadata: Readonly<OAuthClientMetadataInput>\n keyset?: Keyset | Iterable<Key | undefined | null | false>\n /**\n * Determines if the client will allow communicating with the OAuth Servers\n * (Authorization & Resource), or to retrieve \"did:web\" documents, over\n * unsafe HTTP connections. It is recommended to set this to `true` only for\n * development purposes.\n *\n * @note This does not affect the identity resolution mechanism, which will\n * allow HTTP connections to the PLC Directory (if the provided directory url\n * is \"http:\" based).\n * @default false\n * @see {@link OAuthProtectedResourceMetadataResolver.allowHttpResource}\n * @see {@link OAuthAuthorizationServerMetadataResolver.allowHttpIssuer}\n * @see {@link DidResolverCommonOptions.allowHttp}\n */\n allowHttp?: boolean\n\n // Stores\n stateStore: StateStore\n sessionStore: SessionStore\n authorizationServerMetadataCache?: AuthorizationServerMetadataCache\n protectedResourceMetadataCache?: ProtectedResourceMetadataCache\n dpopNonceCache?: DpopNonceCache\n\n // Services\n runtimeImplementation: RuntimeImplementation\n fetch?: Fetch\n} & CreateIdentityResolverOptions &\n SessionHooks\n\nexport type OAuthClientFetchMetadataOptions = {\n clientId: OAuthClientIdDiscoverable\n fetch?: Fetch\n signal?: AbortSignal\n}\n\nexport class OAuthClient {\n static async fetchMetadata({\n clientId,\n fetch = globalThis.fetch,\n signal,\n }: OAuthClientFetchMetadataOptions) {\n signal?.throwIfAborted()\n\n const request = new Request(clientId, {\n redirect: 'error',\n signal: signal,\n })\n const response = await fetch(request)\n\n if (response.status !== 200) {\n response.body?.cancel?.()\n throw new TypeError(`Failed to fetch client metadata: ${response.status}`)\n }\n\n // https://www.ietf.org/archive/id/draft-ietf-oauth-client-id-metadata-document-00.html#section-4.1\n const mime = response.headers.get('content-type')?.split(';')[0].trim()\n if (mime !== 'application/json') {\n response.body?.cancel?.()\n throw new TypeError(`Invalid client metadata content type: ${mime}`)\n }\n\n const json: unknown = await response.json()\n\n signal?.throwIfAborted()\n\n return oauthClientMetadataSchema.parse(json)\n }\n\n // Config\n readonly clientMetadata: ClientMetadata\n readonly responseMode: OAuthResponseMode\n readonly keyset?: Keyset\n\n // Services\n readonly runtime: Runtime\n readonly fetch: Fetch\n readonly oauthResolver: OAuthResolver\n readonly serverFactory: OAuthServerFactory\n\n // Stores\n protected readonly sessionGetter: SessionGetter\n protected readonly stateStore: StateStore\n\n constructor(options: OAuthClientOptions) {\n const {\n stateStore,\n sessionStore,\n\n dpopNonceCache = new SimpleStoreMemory({ ttl: 60e3, max: 100 }),\n authorizationServerMetadataCache = new SimpleStoreMemory({\n ttl: 60e3,\n max: 100,\n }),\n protectedResourceMetadataCache = new SimpleStoreMemory({\n ttl: 60e3,\n max: 100,\n }),\n\n responseMode,\n clientMetadata,\n runtimeImplementation,\n keyset,\n } = options\n\n this.keyset = keyset\n ? keyset instanceof Keyset\n ? keyset\n : new Keyset(keyset)\n : undefined\n this.clientMetadata = validateClientMetadata(clientMetadata, this.keyset)\n this.responseMode = responseMode\n\n this.runtime = new Runtime(runtimeImplementation)\n this.fetch = options.fetch ?? globalThis.fetch\n this.oauthResolver = new OAuthResolver(\n createIdentityResolver(options),\n new OAuthProtectedResourceMetadataResolver(\n protectedResourceMetadataCache,\n this.fetch,\n { allowHttpResource: options.allowHttp },\n ),\n new OAuthAuthorizationServerMetadataResolver(\n authorizationServerMetadataCache,\n this.fetch,\n { allowHttpIssuer: options.allowHttp },\n ),\n )\n this.serverFactory = new OAuthServerFactory(\n this.clientMetadata,\n this.runtime,\n this.oauthResolver,\n this.fetch,\n this.keyset,\n dpopNonceCache,\n )\n\n this.stateStore = stateStore\n this.sessionGetter = new SessionGetter(\n sessionStore,\n this.serverFactory,\n this.runtime,\n options,\n )\n }\n\n // Exposed as public API for convenience\n get identityResolver() {\n return this.oauthResolver.identityResolver\n }\n\n get jwks() {\n return this.keyset?.publicJwks ?? ({ keys: [] as const } as const)\n }\n\n async authorize(\n input: string,\n { signal, ...options }: AuthorizeOptions = {},\n ): Promise<URL> {\n const redirectUri =\n options?.redirect_uri ?? this.clientMetadata.redirect_uris[0]\n if (!this.clientMetadata.redirect_uris.includes(redirectUri)) {\n // The server will enforce this, but let's catch it early\n throw new TypeError('Invalid redirect_uri')\n }\n\n const { identityInfo, metadata } = await this.oauthResolver.resolve(input, {\n signal,\n })\n\n const pkce = await this.runtime.generatePKCE()\n const dpopKey = await this.runtime.generateKey(\n metadata.dpop_signing_alg_values_supported || [FALLBACK_ALG],\n )\n\n const authMethod = negotiateClientAuthMethod(\n metadata,\n this.clientMetadata,\n this.keyset,\n )\n const state = await this.runtime.generateNonce()\n\n await this.stateStore.set(state, {\n iss: metadata.issuer,\n dpopKey,\n authMethod,\n verifier: pkce.verifier,\n appState: options?.state,\n })\n\n const parameters: OAuthAuthorizationRequestParameters = {\n ...options,\n\n client_id: this.clientMetadata.client_id,\n redirect_uri: redirectUri,\n code_challenge: pkce.challenge,\n code_challenge_method: pkce.method,\n state,\n login_hint: identityInfo\n ? identityInfo.handle !== HANDLE_INVALID\n ? identityInfo.handle\n : identityInfo.did\n : undefined,\n response_mode: this.responseMode,\n response_type: 'code' as const,\n scope: options?.scope ?? this.clientMetadata.scope,\n }\n\n const authorizationUrl = new URL(metadata.authorization_endpoint)\n\n // Since the user will be redirected to the authorization_endpoint url using\n // a browser, we need to make sure that the url is valid.\n if (\n authorizationUrl.protocol !== 'https:' &&\n authorizationUrl.protocol !== 'http:'\n ) {\n throw new TypeError(\n `Invalid authorization endpoint protocol: ${authorizationUrl.protocol}`,\n )\n }\n\n if (metadata.pushed_authorization_request_endpoint) {\n const server = await this.serverFactory.fromMetadata(\n metadata,\n authMethod,\n dpopKey,\n )\n const parResponse = await server.request(\n 'pushed_authorization_request',\n parameters,\n )\n\n authorizationUrl.searchParams.set(\n 'client_id',\n this.clientMetadata.client_id,\n )\n authorizationUrl.searchParams.set('request_uri', parResponse.request_uri)\n return authorizationUrl\n } else if (metadata.require_pushed_authorization_requests) {\n throw new Error(\n 'Server requires pushed authorization requests (PAR) but no PAR endpoint is available',\n )\n } else {\n for (const [key, value] of Object.entries(parameters)) {\n if (value) authorizationUrl.searchParams.set(key, String(value))\n }\n\n // Length of the URL that will be sent to the server\n const urlLength =\n authorizationUrl.pathname.length + authorizationUrl.search.length\n if (urlLength < 2048) {\n return authorizationUrl\n } else if (!metadata.pushed_authorization_request_endpoint) {\n throw new Error('Login URL too long')\n }\n }\n\n throw new Error(\n 'Server does not support pushed authorization requests (PAR)',\n )\n }\n\n /**\n * This method allows the client to proactively revoke the request_uri it\n * created through PAR.\n */\n async abortRequest(authorizeUrl: URL) {\n const requestUri = authorizeUrl.searchParams.get('request_uri')\n if (!requestUri) return\n\n // @NOTE This is not implemented here because, 1) the request server should\n // invalidate the request_uri after some delay anyways, and 2) I am not sure\n // that the revocation endpoint is even supposed to support this (and I\n // don't want to spend the time checking now).\n\n // @TODO investigate actual necessity & feasibility of this feature\n }\n\n async callback(\n params: URLSearchParams,\n options: CallbackOptions = {},\n ): Promise<{\n session: OAuthSession\n state: string | null\n }> {\n const responseJwt = params.get('response')\n if (responseJwt != null) {\n // https://openid.net/specs/oauth-v2-jarm.html\n throw new OAuthCallbackError(params, 'JARM not supported')\n }\n\n const issuerParam = params.get('iss')\n const stateParam = params.get('state')\n const errorParam = params.get('error')\n const codeParam = params.get('code')\n\n if (!stateParam) {\n throw new OAuthCallbackError(params, 'Missing \"state\" parameter')\n }\n const stateData = await this.stateStore.get(stateParam)\n if (stateData) {\n // Prevent any kind of replay\n await this.stateStore.del(stateParam)\n } else {\n throw new OAuthCallbackError(\n params,\n `Unknown authorization session \"${stateParam}\"`,\n )\n }\n\n try {\n if (errorParam != null) {\n throw new OAuthCallbackError(params, undefined, stateData.appState)\n }\n\n if (!codeParam) {\n throw new OAuthCallbackError(\n params,\n 'Missing \"code\" query param',\n stateData.appState,\n )\n }\n\n const server = await this.serverFactory.fromIssuer(\n stateData.iss,\n stateData.authMethod,\n stateData.dpopKey,\n )\n\n if (issuerParam != null) {\n if (!server.issuer) {\n throw new OAuthCallbackError(\n params,\n 'Issuer not found in metadata',\n stateData.appState,\n )\n }\n if (server.issuer !== issuerParam) {\n throw new OAuthCallbackError(\n params,\n 'Issuer mismatch',\n stateData.appState,\n )\n }\n } else if (\n server.serverMetadata.authorization_response_iss_parameter_supported\n ) {\n throw new OAuthCallbackError(\n params,\n 'iss missing from the response',\n stateData.appState,\n )\n }\n\n const tokenSet = await server.exchangeCode(\n codeParam,\n stateData.verifier,\n options?.redirect_uri ?? server.clientMetadata.redirect_uris[0],\n )\n\n // We revoke any existing session first to avoid leaving orphaned sessions\n // on the AS.\n try {\n await this.revoke(tokenSet.sub)\n } catch {\n // No existing session, or failed to get it. This is fine.\n }\n\n try {\n await this.sessionGetter.setStored(tokenSet.sub, {\n dpopKey: stateData.dpopKey,\n authMethod: server.authMethod,\n tokenSet,\n })\n\n const session = this.createSession(server, tokenSet.sub)\n\n return { session, state: stateData.appState ?? null }\n } catch (err) {\n await server.revoke(tokenSet.refresh_token || tokenSet.access_token)\n\n throw err\n }\n } catch (err) {\n // Make sure, whatever the underlying error, that the appState is\n // available in the calling code\n throw OAuthCallbackError.from(err, params, stateData.appState)\n }\n }\n\n /**\n * Load a stored session. This will refresh the token only if needed (about to\n * expire) by default.\n *\n * @see {@link SessionGetter.restore}\n */\n async restore(\n sub: string,\n refresh: boolean | 'auto' = 'auto',\n ): Promise<OAuthSession> {\n // sub arg is lightly typed for convenience of library user\n assertAtprotoDid(sub)\n\n const { dpopKey, authMethod, tokenSet } =\n await this.sessionGetter.getSession(sub, refresh)\n\n try {\n const server = await this.serverFactory.fromIssuer(\n tokenSet.iss,\n authMethod,\n dpopKey,\n {\n noCache: refresh === true,\n allowStale: refresh === false,\n },\n )\n\n return this.createSession(server, sub)\n } catch (err) {\n if (err instanceof AuthMethodUnsatisfiableError) {\n await this.sessionGetter.delStored(sub, err)\n }\n\n throw err\n }\n }\n\n async revoke(sub: string) {\n // sub arg is lightly typed for convenience of library user\n assertAtprotoDid(sub)\n\n const res = await this.sessionGetter.getSession(sub, false).catch((err) => {\n if (isExpectedSessionError(err)) return null\n throw err\n })\n\n if (!res) return\n\n const { dpopKey, authMethod, tokenSet } = res\n\n // NOT using `;(await this.restore(sub, false)).signOut()` because we want\n // the tokens to be deleted even if it was not possible to fetch the issuer\n // data.\n try {\n const server = await this.serverFactory.fromIssuer(\n tokenSet.iss,\n authMethod,\n dpopKey,\n )\n await server.revoke(tokenSet.access_token)\n } finally {\n await this.sessionGetter.delStored(sub, new TokenRevokedError(sub))\n }\n }\n\n protected createSession(\n server: OAuthServerAgent,\n sub: AtprotoDid,\n ): OAuthSession {\n return new OAuthSession(server, sub, this.sessionGetter, this.fetch)\n }\n}\n"]}
|
|
@@ -1,29 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const fetch_1 = require("@atproto-labs/fetch");
|
|
6
|
-
const simple_store_1 = require("@atproto-labs/simple-store");
|
|
7
|
-
const util_js_1 = require("./util.js");
|
|
1
|
+
import { oauthProtectedResourceMetadataSchema, } from '@atproto/oauth-types';
|
|
2
|
+
import { FetchResponseError, bindFetch, cancelBody, } from '@atproto-labs/fetch';
|
|
3
|
+
import { CachedGetter, } from '@atproto-labs/simple-store';
|
|
4
|
+
import { contentMime } from './util.js';
|
|
8
5
|
/**
|
|
9
6
|
* @see {@link https://www.rfc-editor.org/rfc/rfc9728.html}
|
|
10
7
|
*/
|
|
11
|
-
class OAuthProtectedResourceMetadataResolver extends
|
|
8
|
+
export class OAuthProtectedResourceMetadataResolver extends CachedGetter {
|
|
12
9
|
constructor(cache, fetch = globalThis.fetch, config) {
|
|
13
10
|
super(async (origin, options) => this.fetchMetadata(origin, options), cache);
|
|
14
|
-
|
|
15
|
-
enumerable: true,
|
|
16
|
-
configurable: true,
|
|
17
|
-
writable: true,
|
|
18
|
-
value: void 0
|
|
19
|
-
});
|
|
20
|
-
Object.defineProperty(this, "allowHttpResource", {
|
|
21
|
-
enumerable: true,
|
|
22
|
-
configurable: true,
|
|
23
|
-
writable: true,
|
|
24
|
-
value: void 0
|
|
25
|
-
});
|
|
26
|
-
this.fetch = (0, fetch_1.bindFetch)(fetch);
|
|
11
|
+
this.fetch = bindFetch(fetch);
|
|
27
12
|
this.allowHttpResource = config?.allowHttpResource === true;
|
|
28
13
|
}
|
|
29
14
|
async get(resource, options) {
|
|
@@ -46,19 +31,19 @@ class OAuthProtectedResourceMetadataResolver extends simple_store_1.CachedGetter
|
|
|
46
31
|
});
|
|
47
32
|
const response = await this.fetch(request);
|
|
48
33
|
if (response.status === 404) {
|
|
49
|
-
await
|
|
34
|
+
await cancelBody(response, 'log');
|
|
50
35
|
return null;
|
|
51
36
|
}
|
|
52
37
|
// https://www.rfc-editor.org/rfc/rfc9728.html#section-3.2
|
|
53
38
|
if (response.status !== 200) {
|
|
54
|
-
await
|
|
55
|
-
throw await
|
|
39
|
+
await cancelBody(response, 'log');
|
|
40
|
+
throw await FetchResponseError.from(response, `Unexpected status code ${response.status} for "${url}"`, undefined, { cause: request });
|
|
56
41
|
}
|
|
57
|
-
if (
|
|
58
|
-
await
|
|
59
|
-
throw await
|
|
42
|
+
if (contentMime(response.headers) !== 'application/json') {
|
|
43
|
+
await cancelBody(response, 'log');
|
|
44
|
+
throw await FetchResponseError.from(response, `Unexpected content type for "${url}"`, undefined, { cause: request });
|
|
60
45
|
}
|
|
61
|
-
const metadata =
|
|
46
|
+
const metadata = oauthProtectedResourceMetadataSchema.parse(await response.json());
|
|
62
47
|
// https://www.rfc-editor.org/rfc/rfc9728.html#section-3.3
|
|
63
48
|
if (metadata.resource !== origin) {
|
|
64
49
|
throw new TypeError(`Invalid issuer ${metadata.resource}`);
|
|
@@ -66,5 +51,4 @@ class OAuthProtectedResourceMetadataResolver extends simple_store_1.CachedGetter
|
|
|
66
51
|
return metadata;
|
|
67
52
|
}
|
|
68
53
|
}
|
|
69
|
-
exports.OAuthProtectedResourceMetadataResolver = OAuthProtectedResourceMetadataResolver;
|
|
70
54
|
//# sourceMappingURL=oauth-protected-resource-metadata-resolver.js.map
|