@atproto/oauth-client 0.0.0-spaces-alpha-20260818021221

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.
Files changed (112) hide show
  1. package/CHANGELOG.md +810 -0
  2. package/LICENSE.txt +7 -0
  3. package/README.md +387 -0
  4. package/dist/constants.d.ts +5 -0
  5. package/dist/constants.d.ts.map +1 -0
  6. package/dist/constants.js +5 -0
  7. package/dist/constants.js.map +1 -0
  8. package/dist/errors/auth-method-unsatisfiable-error.d.ts +3 -0
  9. package/dist/errors/auth-method-unsatisfiable-error.d.ts.map +1 -0
  10. package/dist/errors/auth-method-unsatisfiable-error.js +3 -0
  11. package/dist/errors/auth-method-unsatisfiable-error.js.map +1 -0
  12. package/dist/errors/token-invalid-error.d.ts +7 -0
  13. package/dist/errors/token-invalid-error.d.ts.map +1 -0
  14. package/dist/errors/token-invalid-error.js +7 -0
  15. package/dist/errors/token-invalid-error.js.map +1 -0
  16. package/dist/errors/token-refresh-error.d.ts +7 -0
  17. package/dist/errors/token-refresh-error.d.ts.map +1 -0
  18. package/dist/errors/token-refresh-error.js +7 -0
  19. package/dist/errors/token-refresh-error.js.map +1 -0
  20. package/dist/errors/token-revoked-error.d.ts +7 -0
  21. package/dist/errors/token-revoked-error.d.ts.map +1 -0
  22. package/dist/errors/token-revoked-error.js +7 -0
  23. package/dist/errors/token-revoked-error.js.map +1 -0
  24. package/dist/fetch-dpop.d.ts +20 -0
  25. package/dist/fetch-dpop.d.ts.map +1 -0
  26. package/dist/fetch-dpop.js +176 -0
  27. package/dist/fetch-dpop.js.map +1 -0
  28. package/dist/identity-resolver.d.ts +2 -0
  29. package/dist/identity-resolver.d.ts.map +1 -0
  30. package/dist/identity-resolver.js +21 -0
  31. package/dist/identity-resolver.js.map +1 -0
  32. package/dist/index.d.ts +25 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +25 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/lock.d.ts +3 -0
  37. package/dist/lock.d.ts.map +1 -0
  38. package/dist/lock.js +29 -0
  39. package/dist/lock.js.map +1 -0
  40. package/dist/oauth-authorization-server-metadata-resolver.d.ts +25 -0
  41. package/dist/oauth-authorization-server-metadata-resolver.d.ts.map +1 -0
  42. package/dist/oauth-authorization-server-metadata-resolver.js +54 -0
  43. package/dist/oauth-authorization-server-metadata-resolver.js.map +1 -0
  44. package/dist/oauth-callback-error.d.ts +7 -0
  45. package/dist/oauth-callback-error.d.ts.map +1 -0
  46. package/dist/oauth-callback-error.js +14 -0
  47. package/dist/oauth-callback-error.js.map +1 -0
  48. package/dist/oauth-client-auth.d.ts +23 -0
  49. package/dist/oauth-client-auth.d.ts.map +1 -0
  50. package/dist/oauth-client-auth.js +126 -0
  51. package/dist/oauth-client-auth.js.map +1 -0
  52. package/dist/oauth-client.d.ts +338 -0
  53. package/dist/oauth-client.d.ts.map +1 -0
  54. package/dist/oauth-client.js +274 -0
  55. package/dist/oauth-client.js.map +1 -0
  56. package/dist/oauth-protected-resource-metadata-resolver.d.ts +25 -0
  57. package/dist/oauth-protected-resource-metadata-resolver.d.ts.map +1 -0
  58. package/dist/oauth-protected-resource-metadata-resolver.js +54 -0
  59. package/dist/oauth-protected-resource-metadata-resolver.js.map +1 -0
  60. package/dist/oauth-resolver-error.d.ts +7 -0
  61. package/dist/oauth-resolver-error.d.ts.map +1 -0
  62. package/dist/oauth-resolver-error.js +19 -0
  63. package/dist/oauth-resolver-error.js.map +1 -0
  64. package/dist/oauth-resolver.d.ts +73 -0
  65. package/dist/oauth-resolver.d.ts.map +1 -0
  66. package/dist/oauth-resolver.js +99 -0
  67. package/dist/oauth-resolver.js.map +1 -0
  68. package/dist/oauth-response-error.d.ts +11 -0
  69. package/dist/oauth-response-error.d.ts.map +1 -0
  70. package/dist/oauth-response-error.js +22 -0
  71. package/dist/oauth-response-error.js.map +1 -0
  72. package/dist/oauth-server-agent.d.ts +55 -0
  73. package/dist/oauth-server-agent.d.ts.map +1 -0
  74. package/dist/oauth-server-agent.js +189 -0
  75. package/dist/oauth-server-agent.js.map +1 -0
  76. package/dist/oauth-server-factory.d.ts +33 -0
  77. package/dist/oauth-server-factory.d.ts.map +1 -0
  78. package/dist/oauth-server-factory.js +31 -0
  79. package/dist/oauth-server-factory.js.map +1 -0
  80. package/dist/oauth-session.d.ts +34 -0
  81. package/dist/oauth-session.d.ts.map +1 -0
  82. package/dist/oauth-session.js +120 -0
  83. package/dist/oauth-session.js.map +1 -0
  84. package/dist/runtime-implementation.d.ts +17 -0
  85. package/dist/runtime-implementation.d.ts.map +1 -0
  86. package/dist/runtime-implementation.js +2 -0
  87. package/dist/runtime-implementation.js.map +1 -0
  88. package/dist/runtime.d.ts +26 -0
  89. package/dist/runtime.d.ts.map +1 -0
  90. package/dist/runtime.js +100 -0
  91. package/dist/runtime.js.map +1 -0
  92. package/dist/session-getter.d.ts +44 -0
  93. package/dist/session-getter.d.ts.map +1 -0
  94. package/dist/session-getter.js +265 -0
  95. package/dist/session-getter.js.map +1 -0
  96. package/dist/state-store.d.ts +23 -0
  97. package/dist/state-store.d.ts.map +1 -0
  98. package/dist/state-store.js +2 -0
  99. package/dist/state-store.js.map +1 -0
  100. package/dist/types.d.ts +430 -0
  101. package/dist/types.d.ts.map +1 -0
  102. package/dist/types.js +9 -0
  103. package/dist/types.js.map +1 -0
  104. package/dist/util.d.ts +20 -0
  105. package/dist/util.d.ts.map +1 -0
  106. package/dist/util.js +68 -0
  107. package/dist/util.js.map +1 -0
  108. package/dist/validate-client-metadata.d.ts +5 -0
  109. package/dist/validate-client-metadata.d.ts.map +1 -0
  110. package/dist/validate-client-metadata.js +76 -0
  111. package/dist/validate-client-metadata.js.map +1 -0
  112. package/package.json +55 -0
@@ -0,0 +1,99 @@
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 {
5
+ constructor(identityResolver, protectedResourceMetadataResolver, authorizationServerMetadataResolver) {
6
+ this.identityResolver = identityResolver;
7
+ this.protectedResourceMetadataResolver = protectedResourceMetadataResolver;
8
+ this.authorizationServerMetadataResolver = authorizationServerMetadataResolver;
9
+ }
10
+ /**
11
+ * @param input - A handle, DID, PDS URL or Entryway URL
12
+ */
13
+ async resolve(input, options) {
14
+ // Allow using an entryway, or PDS url, directly as login input (e.g.
15
+ // when the user forgot their handle, or when the handle does not
16
+ // resolve to a DID)
17
+ return /^https?:\/\//.test(input)
18
+ ? this.resolveFromService(input, options)
19
+ : this.resolveFromIdentity(input, options);
20
+ }
21
+ /**
22
+ * @note this method can be used to verify if a particular uri supports OAuth
23
+ * based sign-in (for compatibility with legacy implementation).
24
+ */
25
+ async resolveFromService(input, options) {
26
+ try {
27
+ // Assume first that input is a PDS URL (as required by ATPROTO)
28
+ const metadata = await this.getResourceServerMetadata(input, options);
29
+ return { metadata };
30
+ }
31
+ catch (err) {
32
+ if (!options?.signal?.aborted && err instanceof OAuthResolverError) {
33
+ try {
34
+ // Fallback to trying to fetch as an issuer (Entryway)
35
+ const result = oauthIssuerIdentifierSchema.safeParse(input);
36
+ if (result.success) {
37
+ const metadata = await this.getAuthorizationServerMetadata(result.data, options);
38
+ return { metadata };
39
+ }
40
+ }
41
+ catch {
42
+ // Fallback failed, throw original error
43
+ }
44
+ }
45
+ throw err;
46
+ }
47
+ }
48
+ async resolveFromIdentity(input, options) {
49
+ const identityInfo = await this.resolveIdentity(input, options);
50
+ options?.signal?.throwIfAborted();
51
+ const pds = extractPdsUrl(identityInfo.didDoc);
52
+ const metadata = await this.getResourceServerMetadata(pds, options);
53
+ return { identityInfo, metadata, pds };
54
+ }
55
+ async resolveIdentity(input, options) {
56
+ try {
57
+ return await this.identityResolver.resolve(input, options);
58
+ }
59
+ catch (cause) {
60
+ throw OAuthResolverError.from(cause, `Failed to resolve identity: ${input}`);
61
+ }
62
+ }
63
+ async getAuthorizationServerMetadata(issuer, options) {
64
+ try {
65
+ return await this.authorizationServerMetadataResolver.get(issuer, options);
66
+ }
67
+ catch (cause) {
68
+ throw OAuthResolverError.from(cause, `Failed to resolve OAuth server metadata for issuer: ${issuer}`);
69
+ }
70
+ }
71
+ async getResourceServerMetadata(pdsUrl, options) {
72
+ try {
73
+ const rsMetadata = await this.protectedResourceMetadataResolver.get(pdsUrl, options);
74
+ if (!rsMetadata) {
75
+ return this.getAuthorizationServerMetadata(pdsUrl, options);
76
+ }
77
+ // ATPROTO requires one, and only one, authorization server entry
78
+ if (rsMetadata.authorization_servers?.length !== 1) {
79
+ throw new OAuthResolverError(rsMetadata.authorization_servers?.length
80
+ ? `Unable to determine authorization server for PDS: ${pdsUrl}`
81
+ : `No authorization servers found for PDS: ${pdsUrl}`);
82
+ }
83
+ const issuer = rsMetadata.authorization_servers[0];
84
+ options?.signal?.throwIfAborted();
85
+ const asMetadata = await this.getAuthorizationServerMetadata(issuer, options);
86
+ // https://www.rfc-editor.org/rfc/rfc9728.html#section-4
87
+ if (asMetadata.protected_resources) {
88
+ if (!asMetadata.protected_resources.includes(rsMetadata.resource)) {
89
+ throw new OAuthResolverError(`PDS "${pdsUrl}" not protected by issuer "${issuer}"`);
90
+ }
91
+ }
92
+ return asMetadata;
93
+ }
94
+ catch (cause) {
95
+ throw OAuthResolverError.from(cause, `Failed to resolve OAuth server metadata for resource: ${pdsUrl}`);
96
+ }
97
+ }
98
+ }
99
+ //# sourceMappingURL=oauth-resolver.js.map
@@ -0,0 +1 @@
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 type OAuthAuthorizationServerMetadata,\n oauthIssuerIdentifierSchema,\n} from '@atproto/oauth-types'\nimport type {\n IdentityInfo,\n IdentityResolver,\n ResolveIdentityOptions,\n} from '@atproto-labs/identity-resolver'\nimport type {\n GetCachedOptions,\n OAuthAuthorizationServerMetadataResolver,\n} from './oauth-authorization-server-metadata-resolver.js'\nimport type { 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"]}
@@ -0,0 +1,11 @@
1
+ import type { Json } from '@atproto-labs/fetch';
2
+ export declare class OAuthResponseError extends Error {
3
+ readonly response: Response;
4
+ readonly payload: Json;
5
+ readonly error?: string;
6
+ readonly errorDescription?: string;
7
+ constructor(response: Response, payload: Json);
8
+ get status(): number;
9
+ get headers(): Headers;
10
+ }
11
+ //# sourceMappingURL=oauth-response-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-response-error.d.ts","sourceRoot":"","sources":["../src/oauth-response-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAG/C,qBAAa,kBAAmB,SAAQ,KAAK;aAKzB,QAAQ,EAAE,QAAQ;aAClB,OAAO,EAAE,IAAI;IAL/B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAElC,YACkB,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,IAAI,EAa9B;IAED,IAAI,MAAM,WAET;IAED,IAAI,OAAO,YAEV;CACF"}
@@ -0,0 +1,22 @@
1
+ import { ifString } from './util.js';
2
+ export class OAuthResponseError extends Error {
3
+ constructor(response, payload) {
4
+ const error = ifString(payload?.['error']);
5
+ const errorDescription = ifString(payload?.['error_description']);
6
+ const messageError = error ? `"${error}"` : 'unknown';
7
+ const messageDesc = errorDescription ? `: ${errorDescription}` : '';
8
+ const message = `OAuth ${messageError} error${messageDesc}`;
9
+ super(message);
10
+ this.response = response;
11
+ this.payload = payload;
12
+ this.error = error;
13
+ this.errorDescription = errorDescription;
14
+ }
15
+ get status() {
16
+ return this.response.status;
17
+ }
18
+ get headers() {
19
+ return this.response.headers;
20
+ }
21
+ }
22
+ //# sourceMappingURL=oauth-response-error.js.map
@@ -0,0 +1 @@
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,KAAK,GAAG,QAAQ,CAAE,OAAe,EAAE,CAAC,OAAO,CAAC,CAAC,CAAA;QACnD,MAAM,gBAAgB,GAAG,QAAQ,CAAE,OAAe,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAA;QAE1E,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;wBAVE,QAAQ;uBACR,OAAO;QAWvB,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 type { 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 error = ifString((payload as any)?.['error'])\n const errorDescription = ifString((payload as any)?.['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"]}
@@ -0,0 +1,55 @@
1
+ import type { AtprotoDid } from '@atproto/did';
2
+ import type { Key, Keyset } from '@atproto/jwk';
3
+ import { type AtprotoOAuthScope, type AtprotoOAuthTokenResponse, type OAuthAuthorizationRequestPar, type OAuthAuthorizationServerMetadata, type OAuthEndpointName, type OAuthParResponse, type OAuthRedirectUri, type OAuthTokenRequest } from '@atproto/oauth-types';
4
+ import { type Fetch, type Json } from '@atproto-labs/fetch';
5
+ import type { SimpleStore } from '@atproto-labs/simple-store';
6
+ import { type ClientAuthMethod, type ClientCredentialsFactory } from './oauth-client-auth.js';
7
+ import type { OAuthResolver } from './oauth-resolver.js';
8
+ import type { Runtime } from './runtime.js';
9
+ import type { ClientMetadata } from './types.js';
10
+ export type { AtprotoDid, AtprotoOAuthScope, AtprotoOAuthTokenResponse };
11
+ export type TokenSet = {
12
+ iss: string;
13
+ sub: AtprotoDid;
14
+ aud: string;
15
+ scope: AtprotoOAuthScope;
16
+ refresh_token?: string;
17
+ access_token: string;
18
+ token_type: 'DPoP';
19
+ /** ISO Date */
20
+ expires_at?: string;
21
+ };
22
+ export type DpopNonceCache = SimpleStore<string, string>;
23
+ export declare class OAuthServerAgent {
24
+ readonly authMethod: ClientAuthMethod;
25
+ readonly dpopKey: Key;
26
+ readonly serverMetadata: OAuthAuthorizationServerMetadata;
27
+ readonly clientMetadata: ClientMetadata;
28
+ readonly dpopNonces: DpopNonceCache;
29
+ readonly oauthResolver: OAuthResolver;
30
+ readonly runtime: Runtime;
31
+ readonly keyset?: Keyset | undefined;
32
+ protected dpopFetch: Fetch<unknown>;
33
+ protected clientCredentialsFactory: ClientCredentialsFactory;
34
+ /**
35
+ * @throws see {@link createClientCredentialsFactory}
36
+ */
37
+ constructor(authMethod: ClientAuthMethod, dpopKey: Key, serverMetadata: OAuthAuthorizationServerMetadata, clientMetadata: ClientMetadata, dpopNonces: DpopNonceCache, oauthResolver: OAuthResolver, runtime: Runtime, keyset?: Keyset | undefined, fetch?: Fetch);
38
+ get issuer(): "http://127.0.0.1" | "http://localhost" | `http://127.0.0.1#${string}` | `http://127.0.0.1/${string}` | `http://127.0.0.1:${string}` | `http://127.0.0.1?${string}` | `http://[::1]${string}` | `http://localhost#${string}` | `http://localhost/${string}` | `http://localhost:${string}` | `http://localhost?${string}` | `https://${string}`;
39
+ revoke(token: string): Promise<void>;
40
+ exchangeCode(code: string, codeVerifier?: string, redirectUri?: OAuthRedirectUri): Promise<TokenSet>;
41
+ refresh(tokenSet: TokenSet): Promise<TokenSet>;
42
+ /**
43
+ * VERY IMPORTANT ! Always call this to process token responses.
44
+ *
45
+ * Whenever an OAuth token response is received, we **MUST** verify that the
46
+ * "sub" is a DID, whose issuer authority is indeed the server we just
47
+ * obtained credentials from. This check is a critical step to actually be
48
+ * able to use the "sub" (DID) as being the actual user's identifier.
49
+ *
50
+ * @returns The user's PDS URL (the resource server for the user)
51
+ */
52
+ protected verifyIssuer(sub: AtprotoDid): Promise<string>;
53
+ request<Endpoint extends OAuthEndpointName>(endpoint: Endpoint, payload: Endpoint extends 'token' ? OAuthTokenRequest : Endpoint extends 'pushed_authorization_request' ? OAuthAuthorizationRequestPar : Record<string, unknown>): Promise<Endpoint extends 'token' ? AtprotoOAuthTokenResponse : Endpoint extends 'pushed_authorization_request' ? OAuthParResponse : Json>;
54
+ }
55
+ //# sourceMappingURL=oauth-server-agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-server-agent.d.ts","sourceRoot":"","sources":["../src/oauth-server-agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,189 @@
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
+ import { timeoutSignal } from './util.js';
8
+ export class OAuthServerAgent {
9
+ /**
10
+ * @throws see {@link createClientCredentialsFactory}
11
+ */
12
+ constructor(authMethod, dpopKey, serverMetadata, clientMetadata, dpopNonces, oauthResolver, runtime, keyset, fetch) {
13
+ this.authMethod = authMethod;
14
+ this.dpopKey = dpopKey;
15
+ this.serverMetadata = serverMetadata;
16
+ this.clientMetadata = clientMetadata;
17
+ this.dpopNonces = dpopNonces;
18
+ this.oauthResolver = oauthResolver;
19
+ this.runtime = runtime;
20
+ this.keyset = keyset;
21
+ this.clientCredentialsFactory = createClientCredentialsFactory(authMethod, serverMetadata, clientMetadata, runtime, keyset);
22
+ this.dpopFetch = dpopFetchWrapper({
23
+ fetch: bindFetch(fetch),
24
+ key: dpopKey,
25
+ supportedAlgs: serverMetadata.dpop_signing_alg_values_supported,
26
+ sha256: async (v) => runtime.sha256(v),
27
+ nonces: dpopNonces,
28
+ isAuthServer: true,
29
+ });
30
+ }
31
+ get issuer() {
32
+ return this.serverMetadata.issuer;
33
+ }
34
+ async revoke(token) {
35
+ try {
36
+ await this.request('revocation', { token });
37
+ }
38
+ catch {
39
+ // Don't care
40
+ }
41
+ }
42
+ async exchangeCode(code, codeVerifier, redirectUri) {
43
+ const now = Date.now();
44
+ const tokenResponse = await this.request('token', {
45
+ grant_type: 'authorization_code',
46
+ // redirectUri should always be passed by the calling code, but if it is
47
+ // not, default to the first redirect_uri registered for the client:
48
+ redirect_uri: redirectUri ?? this.clientMetadata.redirect_uris[0],
49
+ code,
50
+ code_verifier: codeVerifier,
51
+ });
52
+ try {
53
+ // /!\ IMPORTANT /!\
54
+ //
55
+ // The tokenResponse MUST always be valid before the "sub" it contains
56
+ // can be trusted (see Atproto's OAuth spec for details).
57
+ const aud = await this.verifyIssuer(tokenResponse.sub);
58
+ return {
59
+ aud,
60
+ sub: tokenResponse.sub,
61
+ iss: this.issuer,
62
+ scope: tokenResponse.scope,
63
+ refresh_token: tokenResponse.refresh_token,
64
+ access_token: tokenResponse.access_token,
65
+ token_type: tokenResponse.token_type,
66
+ expires_at: typeof tokenResponse.expires_in === 'number'
67
+ ? new Date(now + tokenResponse.expires_in * 1000).toISOString()
68
+ : undefined,
69
+ };
70
+ }
71
+ catch (err) {
72
+ await this.revoke(tokenResponse.access_token);
73
+ throw err;
74
+ }
75
+ }
76
+ async refresh(tokenSet) {
77
+ if (!tokenSet.refresh_token) {
78
+ throw new TokenRefreshError(tokenSet.sub, 'No refresh token available');
79
+ }
80
+ // /!\ IMPORTANT /!\
81
+ //
82
+ // The "sub" MUST be a DID, whose issuer authority is indeed the server we
83
+ // are trying to obtain credentials from. Note that we are doing this
84
+ // *before* we actually try to refresh the token:
85
+ // 1) To avoid unnecessary refresh
86
+ // 2) So that the refresh is the last async operation, ensuring as few
87
+ // async operations happen before the result gets a chance to be stored.
88
+ const aud = await this.verifyIssuer(tokenSet.sub);
89
+ const now = Date.now();
90
+ const tokenResponse = await this.request('token', {
91
+ grant_type: 'refresh_token',
92
+ refresh_token: tokenSet.refresh_token,
93
+ });
94
+ return {
95
+ aud,
96
+ sub: tokenSet.sub,
97
+ iss: this.issuer,
98
+ scope: tokenResponse.scope,
99
+ refresh_token: tokenResponse.refresh_token,
100
+ access_token: tokenResponse.access_token,
101
+ token_type: tokenResponse.token_type,
102
+ expires_at: typeof tokenResponse.expires_in === 'number'
103
+ ? new Date(now + tokenResponse.expires_in * 1000).toISOString()
104
+ : undefined,
105
+ };
106
+ }
107
+ /**
108
+ * VERY IMPORTANT ! Always call this to process token responses.
109
+ *
110
+ * Whenever an OAuth token response is received, we **MUST** verify that the
111
+ * "sub" is a DID, whose issuer authority is indeed the server we just
112
+ * obtained credentials from. This check is a critical step to actually be
113
+ * able to use the "sub" (DID) as being the actual user's identifier.
114
+ *
115
+ * @returns The user's PDS URL (the resource server for the user)
116
+ */
117
+ async verifyIssuer(sub) {
118
+ const resolved = await this.oauthResolver.resolveFromIdentity(sub, {
119
+ noCache: true,
120
+ allowStale: false,
121
+ signal: timeoutSignal(10e3),
122
+ });
123
+ if (this.issuer !== resolved.metadata.issuer) {
124
+ // Best case scenario; the user switched PDS. Worst case scenario; a bad
125
+ // actor is trying to impersonate a user. In any case, we must not allow
126
+ // this token to be used.
127
+ throw new TypeError('Issuer mismatch');
128
+ }
129
+ return resolved.pds.href;
130
+ }
131
+ async request(endpoint, payload) {
132
+ const url = this.serverMetadata[`${endpoint}_endpoint`];
133
+ if (!url)
134
+ throw new Error(`No ${endpoint} endpoint available`);
135
+ const auth = await this.clientCredentialsFactory();
136
+ // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-3.2.2
137
+ // https://datatracker.ietf.org/doc/html/rfc7009#section-2.1
138
+ // https://datatracker.ietf.org/doc/html/rfc7662#section-2.1
139
+ // https://datatracker.ietf.org/doc/html/rfc9126#section-2
140
+ const { response, json } = await this.dpopFetch(url, {
141
+ method: 'POST',
142
+ headers: {
143
+ ...auth.headers,
144
+ 'Content-Type': 'application/x-www-form-urlencoded',
145
+ },
146
+ body: wwwFormUrlEncode({ ...payload, ...auth.payload }),
147
+ }).then(fetchJsonProcessor());
148
+ if (response.ok) {
149
+ switch (endpoint) {
150
+ case 'token':
151
+ return atprotoOAuthTokenResponseSchema.parse(json);
152
+ case 'pushed_authorization_request':
153
+ return oauthParResponseSchema.parse(json);
154
+ default:
155
+ return json;
156
+ }
157
+ }
158
+ else {
159
+ throw new OAuthResponseError(response, json);
160
+ }
161
+ }
162
+ }
163
+ function wwwFormUrlEncode(payload) {
164
+ return new URLSearchParams(Object.entries(payload)
165
+ .filter(entryHasDefinedValue)
166
+ .map(stringifyEntryValue)).toString();
167
+ }
168
+ function entryHasDefinedValue(entry) {
169
+ return entry[1] !== undefined;
170
+ }
171
+ function stringifyEntryValue(entry) {
172
+ const name = entry[0];
173
+ const value = entry[1];
174
+ switch (typeof value) {
175
+ case 'string':
176
+ return [name, value];
177
+ case 'number':
178
+ case 'boolean':
179
+ return [name, String(value)];
180
+ default: {
181
+ const enc = JSON.stringify(value);
182
+ if (enc === undefined) {
183
+ throw new Error(`Unsupported value type for ${name}: ${String(value)}`);
184
+ }
185
+ return [name, enc];
186
+ }
187
+ }
188
+ }
189
+ //# sourceMappingURL=oauth-server-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-server-agent.js","sourceRoot":"","sources":["../src/oauth-server-agent.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AtprotoDid } from '@atproto/did'\nimport type { Key, Keyset } from '@atproto/jwk'\nimport {\n type AtprotoOAuthScope,\n type AtprotoOAuthTokenResponse,\n type OAuthAuthorizationRequestPar,\n type OAuthAuthorizationServerMetadata,\n type OAuthEndpointName,\n type OAuthParResponse,\n type OAuthRedirectUri,\n type OAuthTokenRequest,\n atprotoOAuthTokenResponseSchema,\n oauthParResponseSchema,\n} from '@atproto/oauth-types'\nimport {\n type Fetch,\n type Json,\n bindFetch,\n fetchJsonProcessor,\n} from '@atproto-labs/fetch'\nimport type { SimpleStore } from '@atproto-labs/simple-store'\nimport { TokenRefreshError } from './errors/token-refresh-error.js'\nimport { dpopFetchWrapper } from './fetch-dpop.js'\nimport {\n type ClientAuthMethod,\n type ClientCredentialsFactory,\n createClientCredentialsFactory,\n} from './oauth-client-auth.js'\nimport type { OAuthResolver } from './oauth-resolver.js'\nimport { OAuthResponseError } from './oauth-response-error.js'\nimport type { Runtime } from './runtime.js'\nimport type { ClientMetadata } from './types.js'\nimport { timeoutSignal } from './util.js'\n\nexport type { AtprotoDid, 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: timeoutSignal(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"]}
@@ -0,0 +1,33 @@
1
+ import type { Key, Keyset } from '@atproto/jwk';
2
+ import type { OAuthAuthorizationServerMetadata } from '@atproto/oauth-types';
3
+ import type { Fetch } from '@atproto-labs/fetch';
4
+ import type { GetCachedOptions } from './oauth-authorization-server-metadata-resolver.js';
5
+ import type { ClientAuthMethod } from './oauth-client-auth.js';
6
+ import type { OAuthResolver } from './oauth-resolver.js';
7
+ import { type DpopNonceCache, OAuthServerAgent } from './oauth-server-agent.js';
8
+ import type { Runtime } from './runtime.js';
9
+ import type { ClientMetadata } from './types.js';
10
+ export declare class OAuthServerFactory {
11
+ readonly clientMetadata: ClientMetadata;
12
+ readonly runtime: Runtime;
13
+ readonly resolver: OAuthResolver;
14
+ readonly fetch: Fetch;
15
+ readonly keyset: Keyset | undefined;
16
+ readonly dpopNonceCache: DpopNonceCache;
17
+ constructor(clientMetadata: ClientMetadata, runtime: Runtime, resolver: OAuthResolver, fetch: Fetch, keyset: Keyset | undefined, dpopNonceCache: DpopNonceCache);
18
+ /**
19
+ * @param authMethod `undefined` means that we are restoring a session that
20
+ * was created before we started storing the `authMethod` in the session. In
21
+ * that case, we will use the first key from the keyset.
22
+ *
23
+ * Support for this might be removed in the future.
24
+ *
25
+ * @throws see {@link OAuthServerFactory.fromMetadata}
26
+ */
27
+ fromIssuer(issuer: string, authMethod: ClientAuthMethod, dpopKey: Key, options?: GetCachedOptions): Promise<OAuthServerAgent>;
28
+ /**
29
+ * @throws see {@link OAuthServerAgent}
30
+ */
31
+ fromMetadata(serverMetadata: OAuthAuthorizationServerMetadata, authMethod: ClientAuthMethod, dpopKey: Key): Promise<OAuthServerAgent>;
32
+ }
33
+ //# sourceMappingURL=oauth-server-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-server-factory.d.ts","sourceRoot":"","sources":["../src/oauth-server-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,sBAAsB,CAAA;AAC5E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mDAAmD,CAAA;AACzF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,KAAK,cAAc,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC/E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,qBAAa,kBAAkB;IAE3B,QAAQ,CAAC,cAAc,EAAE,cAAc;IACvC,QAAQ,CAAC,OAAO,EAAE,OAAO;IACzB,QAAQ,CAAC,QAAQ,EAAE,aAAa;IAChC,QAAQ,CAAC,KAAK,EAAE,KAAK;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS;IACnC,QAAQ,CAAC,cAAc,EAAE,cAAc;IANzC,YACW,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,aAAa,EACvB,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,cAAc,EAAE,cAAc,EACrC;IAEJ;;;;;;;;OAQG;IACG,UAAU,CACd,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,gBAAgB,EAC5B,OAAO,EAAE,GAAG,EACZ,OAAO,CAAC,EAAE,gBAAgB,6BAQ3B;IAED;;OAEG;IACG,YAAY,CAChB,cAAc,EAAE,gCAAgC,EAChD,UAAU,EAAE,gBAAgB,EAC5B,OAAO,EAAE,GAAG,6BAab;CACF"}
@@ -0,0 +1,31 @@
1
+ import { OAuthServerAgent } from './oauth-server-agent.js';
2
+ export class OAuthServerFactory {
3
+ constructor(clientMetadata, runtime, resolver, fetch, keyset, dpopNonceCache) {
4
+ this.clientMetadata = clientMetadata;
5
+ this.runtime = runtime;
6
+ this.resolver = resolver;
7
+ this.fetch = fetch;
8
+ this.keyset = keyset;
9
+ this.dpopNonceCache = dpopNonceCache;
10
+ }
11
+ /**
12
+ * @param authMethod `undefined` means that we are restoring a session that
13
+ * was created before we started storing the `authMethod` in the session. In
14
+ * that case, we will use the first key from the keyset.
15
+ *
16
+ * Support for this might be removed in the future.
17
+ *
18
+ * @throws see {@link OAuthServerFactory.fromMetadata}
19
+ */
20
+ async fromIssuer(issuer, authMethod, dpopKey, options) {
21
+ const serverMetadata = await this.resolver.getAuthorizationServerMetadata(issuer, options);
22
+ return this.fromMetadata(serverMetadata, authMethod, dpopKey);
23
+ }
24
+ /**
25
+ * @throws see {@link OAuthServerAgent}
26
+ */
27
+ async fromMetadata(serverMetadata, authMethod, dpopKey) {
28
+ return new OAuthServerAgent(authMethod, dpopKey, serverMetadata, this.clientMetadata, this.dpopNonceCache, this.resolver, this.runtime, this.keyset, this.fetch);
29
+ }
30
+ }
31
+ //# sourceMappingURL=oauth-server-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-server-factory.js","sourceRoot":"","sources":["../src/oauth-server-factory.ts"],"names":[],"mappings":"AAMA,OAAO,EAAuB,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAI/E,MAAM,OAAO,kBAAkB;IAC7B,YACW,cAA8B,EAC9B,OAAgB,EAChB,QAAuB,EACvB,KAAY,EACZ,MAA0B,EAC1B,cAA8B;8BAL9B,cAAc;uBACd,OAAO;wBACP,QAAQ;qBACR,KAAK;sBACL,MAAM;8BACN,cAAc;IACtB,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 type { Key, Keyset } from '@atproto/jwk'\nimport type { OAuthAuthorizationServerMetadata } from '@atproto/oauth-types'\nimport type { Fetch } from '@atproto-labs/fetch'\nimport type { GetCachedOptions } from './oauth-authorization-server-metadata-resolver.js'\nimport type { ClientAuthMethod } from './oauth-client-auth.js'\nimport type { OAuthResolver } from './oauth-resolver.js'\nimport { type DpopNonceCache, OAuthServerAgent } from './oauth-server-agent.js'\nimport type { Runtime } from './runtime.js'\nimport type { 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"]}
@@ -0,0 +1,34 @@
1
+ import type { AtprotoDid } from '@atproto/did';
2
+ import type { AtprotoOAuthScope, OAuthAuthorizationServerMetadata } from '@atproto/oauth-types';
3
+ import { type Fetch } from '@atproto-labs/fetch';
4
+ import type { OAuthServerAgent, TokenSet } from './oauth-server-agent.js';
5
+ import type { SessionGetter } from './session-getter.js';
6
+ export type { AtprotoDid, AtprotoOAuthScope };
7
+ export type TokenInfo = {
8
+ expiresAt?: Date;
9
+ expired?: boolean;
10
+ scope: AtprotoOAuthScope;
11
+ iss: string;
12
+ aud: string;
13
+ sub: AtprotoDid;
14
+ };
15
+ export declare class OAuthSession {
16
+ readonly server: OAuthServerAgent;
17
+ readonly sub: AtprotoDid;
18
+ private readonly sessionGetter;
19
+ protected dpopFetch: Fetch<unknown>;
20
+ constructor(server: OAuthServerAgent, sub: AtprotoDid, sessionGetter: SessionGetter, fetch?: Fetch);
21
+ get did(): AtprotoDid;
22
+ get serverMetadata(): Readonly<OAuthAuthorizationServerMetadata>;
23
+ /**
24
+ * @param refresh When `true`, the credentials will be refreshed even if they
25
+ * are not expired. When `false`, the credentials will not be refreshed even
26
+ * if they are expired. When `undefined`, the credentials will be refreshed
27
+ * if, and only if, they are (about to be) expired. Defaults to `undefined`.
28
+ */
29
+ protected getTokenSet(refresh: boolean | 'auto'): Promise<TokenSet>;
30
+ getTokenInfo(refresh?: boolean | 'auto'): Promise<TokenInfo>;
31
+ signOut(): Promise<void>;
32
+ fetchHandler(pathname: string, init?: RequestInit): Promise<Response>;
33
+ }
34
+ //# sourceMappingURL=oauth-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-session.d.ts","sourceRoot":"","sources":["../src/oauth-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,KAAK,EACV,iBAAiB,EACjB,gCAAgC,EACjC,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,KAAK,KAAK,EAAa,MAAM,qBAAqB,CAAA;AAI3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAKxD,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,CAAA;AAC7C,MAAM,MAAM,SAAS,GAAG;IACtB,SAAS,CAAC,EAAE,IAAI,CAAA;IAChB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,iBAAiB,CAAA;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,UAAU,CAAA;CAChB,CAAA;AAED,qBAAa,YAAY;aAIL,MAAM,EAAE,gBAAgB;aACxB,GAAG,EAAE,UAAU;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa;IALhC,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;IAEnC,YACkB,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE,UAAU,EACd,aAAa,EAAE,aAAa,EAC7C,KAAK,GAAE,KAAwB,EAUhC;IAED,IAAI,GAAG,IAAI,UAAU,CAEpB;IAED,IAAI,cAAc,IAAI,QAAQ,CAAC,gCAAgC,CAAC,CAE/D;IAED;;;;;OAKG;IACH,UAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAIxE;IAEK,YAAY,CAAC,OAAO,GAAE,OAAO,GAAG,MAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAiBzE;IAEK,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAU7B;IAEK,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CA0D1E;CACF"}