@atcute/oauth-browser-client 1.0.27 → 2.0.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.
Files changed (50) hide show
  1. package/README.md +122 -218
  2. package/dist/agents/exchange.d.ts +18 -6
  3. package/dist/agents/exchange.d.ts.map +1 -1
  4. package/dist/agents/exchange.js +35 -17
  5. package/dist/agents/exchange.js.map +1 -1
  6. package/dist/agents/server-agent.d.ts.map +1 -1
  7. package/dist/agents/server-agent.js +22 -5
  8. package/dist/agents/server-agent.js.map +1 -1
  9. package/dist/dpop.d.ts.map +1 -1
  10. package/dist/dpop.js +3 -0
  11. package/dist/dpop.js.map +1 -1
  12. package/dist/environment.d.ts +12 -2
  13. package/dist/environment.d.ts.map +1 -1
  14. package/dist/environment.js +3 -0
  15. package/dist/environment.js.map +1 -1
  16. package/dist/index.d.ts +2 -1
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +2 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/resolvers.d.ts +5 -47
  21. package/dist/resolvers.d.ts.map +1 -1
  22. package/dist/resolvers.js +22 -122
  23. package/dist/resolvers.js.map +1 -1
  24. package/dist/store/db.d.ts +1 -0
  25. package/dist/store/db.d.ts.map +1 -1
  26. package/dist/store/db.js.map +1 -1
  27. package/dist/types/client-assertion.d.ts +21 -0
  28. package/dist/types/client-assertion.d.ts.map +1 -0
  29. package/dist/types/client-assertion.js +3 -0
  30. package/dist/types/client-assertion.js.map +1 -0
  31. package/dist/types/dpop.d.ts +2 -0
  32. package/dist/types/dpop.d.ts.map +1 -1
  33. package/dist/types/identity.d.ts +12 -5
  34. package/dist/types/identity.d.ts.map +1 -1
  35. package/dist/utils/identity-resolver.d.ts +8 -0
  36. package/dist/utils/identity-resolver.d.ts.map +1 -0
  37. package/dist/utils/identity-resolver.js +44 -0
  38. package/dist/utils/identity-resolver.js.map +1 -0
  39. package/lib/agents/exchange.ts +52 -25
  40. package/lib/agents/server-agent.ts +25 -5
  41. package/lib/dpop.ts +4 -0
  42. package/lib/environment.ts +19 -2
  43. package/lib/index.ts +3 -1
  44. package/lib/resolvers.ts +27 -142
  45. package/lib/store/db.ts +1 -0
  46. package/lib/types/client-assertion.ts +25 -0
  47. package/lib/types/dpop.ts +2 -0
  48. package/lib/types/identity.ts +14 -5
  49. package/lib/utils/identity-resolver.ts +59 -0
  50. package/package.json +5 -4
package/README.md CHANGED
@@ -18,60 +18,57 @@ minimal OAuth browser client implementation for AT Protocol.
18
18
 
19
19
  ### setup
20
20
 
21
- initialize the client by importing and calling `configureOAuth` with the client ID and redirect URL.
22
- this call should be placed before any other calls you make with this library.
21
+ initialize the client by importing and calling `configureOAuth` with the client ID and redirect URL,
22
+ along with the resolvers that will be used to resolve and verify account details. this call should
23
+ be placed before any other calls you make with this library.
23
24
 
24
25
  ```ts
25
- import { configureOAuth } from '@atcute/oauth-browser-client';
26
+ import { configureOAuth, defaultIdentityResolver } from '@atcute/oauth-browser-client';
27
+
28
+ import {
29
+ CompositeDidDocumentResolver,
30
+ PlcDidDocumentResolver,
31
+ WebDidDocumentResolver,
32
+ XrpcHandleResolver,
33
+ } from '@atcute/identity-resolver';
26
34
 
27
35
  configureOAuth({
28
36
  metadata: {
29
37
  client_id: 'https://example.com/oauth-client-metadata.json',
30
38
  redirect_uri: 'https://example.com/oauth/callback',
31
39
  },
40
+ identityResolver: defaultIdentityResolver({
41
+ // AT Protocol handles resolve via DNS TXT record or HTTP well-known endpoints.
42
+ // since web apps lack direct DNS access and face CORS restrictions, we're using
43
+ // Bluesky's AppView for this example.
44
+ //
45
+ // NOTE: Bluesky may log handle resolutions and requester info per their privacy
46
+ // policy. consider the privacy implications of this arrangement and change this
47
+ // setup if unsuitable for your use case.
48
+ handleResolver: new XrpcHandleResolver({ serviceUrl: 'https://public.api.bsky.app' }),
49
+
50
+ didDocumentResolver: new CompositeDidDocumentResolver({
51
+ methods: {
52
+ plc: new PlcDidDocumentResolver(),
53
+ web: new WebDidDocumentResolver(),
54
+ },
55
+ }),
56
+ }),
32
57
  });
33
58
  ```
34
59
 
35
60
  ### starting an authorization flow
36
61
 
37
- > [!CAUTION]
38
- > the built-in handle resolution makes use of Bluesky-hosted services to return the intended DID,
39
- > and this can mean sharing the user's IP address and the handle identifiers to Bluesky.
40
- >
41
- > while Bluesky has a declared privacy policy, both developers and users need to be informed and
42
- > aware of the privacy implications of this arrangement. read [this guide](#doing-handle-resolution)
43
- > on how you can implement your own resolution code.
44
-
45
- if your application involves asking for the user's handle or DID, you can use `resolveFromIdentity`
46
- which resolves the user's identity to get its PDS, and the metadata of its authorization server.
47
-
48
- ```ts
49
- import { resolveFromIdentity } from '@atcute/oauth-browser-client';
50
-
51
- const { identity, metadata } = await resolveFromIdentity('mary.my.id');
52
- ```
53
-
54
- alternatively, if it involves asking for the user's PDS, then you can use `resolveFromService` which
55
- just grabs the authorization server metadata.
56
-
57
- ```ts
58
- import { resolveFromService } from '@atcute/oauth-browser-client';
59
-
60
- const { metadata } = await resolveFromService('bsky.social');
61
- ```
62
-
63
- we can then proceed with authorization by calling `createAuthorizationUrl` with the resolved
64
- `metadata` (and `identity`, if using `resolveFromIdentity`) along with the scope of the
65
- authorization, which should either match the one in your client metadata, or a reduced set of it.
62
+ we can start authorization by calling `createAuthorizationUrl` with the intended account's
63
+ identifier or service along with the scope of the authorization, which should either match the one
64
+ in your client metadata, or a reduced set of it.
66
65
 
67
66
  ```ts
68
67
  import { createAuthorizationUrl } from '@atcute/oauth-browser-client';
69
68
 
70
- // passing `identity` is optional,
71
- // it allows for the login form to be autofilled with the user's handle or DID
72
69
  const authUrl = await createAuthorizationUrl({
73
- metadata: metadata,
74
- identity: identity,
70
+ target: { type: 'account', identifier: 'mary.my.id' },
71
+ // or { type: 'pds', serviceUrl: 'https://bsky.social' }
75
72
  scope: 'atproto transition:generic transition:chat.bsky',
76
73
  });
77
74
 
@@ -175,6 +172,93 @@ try {
175
172
  }
176
173
  ```
177
174
 
175
+ ## confidential client mode (optional)
176
+
177
+ by default, `@atcute/oauth-browser-client` operates as a **public client**, resulting in shorter
178
+ session lifetimes by authorization servers as it's deemed to be unable to securely store
179
+ credentials.
180
+
181
+ if you want longer-lived sessions and better security controls, you can enable **confidential client
182
+ mode** by setting up a [client assertion backend](client-assertion-backend).
183
+
184
+ [client-assertion-backend]:
185
+ https://github.com/bluesky-social/proposals/tree/main/0010-client-assertion-backend
186
+
187
+ ### setup
188
+
189
+ configure the client with a function to fetch client assertions from your backend:
190
+
191
+ ```ts
192
+ import { configureOAuth } from '@atcute/oauth-browser-client';
193
+
194
+ configureOAuth({
195
+ // ... existing config
196
+
197
+ async fetchClientAssertion({ jkt, aud, createDpopProof }) {
198
+ const dpop = await createDpopProof('https://example.com/api/client-assertion');
199
+
200
+ const response = await fetch('https://example.com/api/client-assertion', {
201
+ method: 'POST',
202
+ headers: {
203
+ dpop: dpop,
204
+ 'content-type': 'application/json',
205
+ },
206
+ body: JSON.stringify({ jkt, aud }),
207
+ });
208
+
209
+ const data = await response.json();
210
+
211
+ return {
212
+ client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
213
+ client_assertion: data.assertion,
214
+ };
215
+ },
216
+ });
217
+ ```
218
+
219
+ the backend API is completely up to you—there's no standardized spec. design it however works best
220
+ for your infrastructure (authentication, request format, error handling, etc.)
221
+
222
+ your backend needs to validate the incoming DPoP proof and sign a client assertion JWT with the
223
+ following interface:
224
+
225
+ ```ts
226
+ interface ClientAssertionJwt {
227
+ /** your client ID */
228
+ iss: string;
229
+ /** also your client ID */
230
+ sub: string;
231
+ /** the authorization server receiving this token */
232
+ aud: string;
233
+ /** when this token expires */
234
+ exp: number;
235
+ /** unique nonce */
236
+ jti: string;
237
+ /** asserts that this jkt is allowed */
238
+ cnf: { jkt: string };
239
+ }
240
+ ```
241
+
242
+ you're able to use the `jkt` to refuse assertions when necessary (suspicious activity, compromised
243
+ code, etc.)
244
+
245
+ ### client metadata updates
246
+
247
+ your OAuth client metadata document must also be updated for confidential clients:
248
+
249
+ ```json
250
+ {
251
+ "client_id": "https://example.com/oauth-client-metadata.json",
252
+ "client_name": "My App",
253
+ "redirect_uris": ["https://example.com/oauth/callback"],
254
+ "scope": "atproto transition:generic",
255
+ "token_endpoint_auth_method": "private_key_jwt",
256
+ "jwks_uri": "https://example.com/oauth-jwks.json"
257
+ }
258
+ ```
259
+
260
+ the `jwks_uri` should expose the public keys used to sign client assertions.
261
+
178
262
  ## additional guide
179
263
 
180
264
  ### configuring your Vite project
@@ -265,195 +349,15 @@ configureOAuth({
265
349
  client_id: import.meta.env.VITE_OAUTH_CLIENT_ID,
266
350
  redirect_uri: import.meta.env.VITE_OAUTH_REDIRECT_URI,
267
351
  },
352
+ // ...
268
353
  });
269
354
 
270
355
  // ... later during sign-in process
271
356
  const authUrl = await createAuthorizationUrl({
272
- metadata: metadata,
273
- identity: identity,
357
+ // ...
274
358
  scope: import.meta.env.VITE_OAUTH_SCOPE,
275
359
  });
276
360
  ```
277
361
 
278
362
  adjust the code here as necessary, the plugin adds more environment variables than what is actually
279
363
  needed, you can remove them if you don't think you'd need it.
280
-
281
- ### doing handle resolution
282
-
283
- there are two ways that a handle can be verified:
284
-
285
- 1. HTTP verification: there is a file at `/.well-known/atproto-did` containing your account's DID
286
- 2. DNS verification: there is an `_atproto` TXT record containing your account's DID
287
-
288
- you'd want to resolve both of these. if both methods return a response but does not match each other
289
- then it should ideally be thrown.
290
-
291
- verify that the DID matches the intended format
292
-
293
- ```ts
294
- const isDid = (did: string): did is At.DID => {
295
- return /^did:([a-z]+):([a-zA-Z0-9._:%-]*[a-zA-Z0-9._-])$/.test(did);
296
- };
297
- ```
298
-
299
- pass this resolved DID to `resolveFromIdentity`, and carry on as per usual.
300
-
301
- #### HTTP handle resolution
302
-
303
- this is very straightforward, make a request to `https://<handle>/.well-known/atproto-did` without
304
- following redirects. check if the response status is 200 and trim off any excess whitespaces.
305
-
306
- some web servers might not set a permissible CORS header to access this resource, in which case
307
- there is nothing that can be done, unless you'd want to proxy the requests.
308
-
309
- ```ts
310
- const resolveHandleViaHttp = async (handle: string): Promise<At.DID> => {
311
- const url = new URL('/.well-known/atproto-did', `https://${handle}`);
312
-
313
- const response = await fetch(url, { redirect: 'error' });
314
- if (!response.ok) {
315
- throw new ResolverError(`domain is unreachable`);
316
- }
317
-
318
- const text = await response.text();
319
-
320
- const did = text.split('\n')[0]!.trim();
321
- if (isDid(did)) {
322
- return did;
323
- }
324
-
325
- throw new ResolverError(`failed to resolve ${handle}`);
326
- };
327
- ```
328
-
329
- #### DNS handle resolution
330
-
331
- as websites can't do DNS resolution on their own, we'd have to rely on DNS-over-HTTPS (DoH)
332
- services. it should be noted that this _can_ have privacy implications of its own, please read
333
- through the privacy policy of whichever DoH service you end up using and make the user aware of it
334
- as well.
335
-
336
- for this example, we'll be using Cloudflare's DoH resolver for Firefox ([privacy
337
- policy][cf-resolver-firefox-privacy]) as it has support for `application/dns-json` format which
338
- allows us to query and see the responses in JSON.
339
-
340
- ```ts
341
- const SUBDOMAIN = '_atproto';
342
- const PREFIX = 'did=';
343
-
344
- const resolveHandleViaDoH = async (handle: string): Promise<At.DID> => {
345
- const url = new URL('https://mozilla.cloudflare-dns.com/dns-query');
346
- url.searchParams.set('type', 'TXT');
347
- url.searchParams.set('name', `${SUBDOMAIN}.${handle}`);
348
-
349
- const response = await fetch(url, {
350
- method: 'GET',
351
- headers: { accept: 'application/dns-json' },
352
- redirect: 'follow',
353
- });
354
-
355
- const type = response.headers.get('content-type')?.trim();
356
- if (!response.ok) {
357
- const message = type?.startsWith('text/plain') ? await response.text() : `failed to resolve ${handle}`;
358
-
359
- throw new ResolverError(message);
360
- }
361
-
362
- if (type !== 'application/dns-json') {
363
- throw new ResolverError(`unexpected response from DoH server`);
364
- }
365
-
366
- const result = asResult(await response.json());
367
- const answers = result.Answer?.filter(isAnswerTxt).map(extractTxtData) ?? [];
368
-
369
- for (let i = 0; i < answers.length; i++) {
370
- // skip if the line does not start with "did="
371
- if (!answers[i].startsWith(PREFIX)) {
372
- continue;
373
- }
374
-
375
- // ensure there is no other entry starting with "did="
376
- for (let j = i + 1; j < answers.length; j++) {
377
- if (answers[j].startsWith(PREFIX)) {
378
- throw new ResolverError(`handle returned multiple did values`);
379
- }
380
- }
381
-
382
- const did = answers[i].slice(PREFIX.length);
383
- if (isDid(did)) {
384
- return did;
385
- }
386
-
387
- break;
388
- }
389
-
390
- throw new ResolverError(`failed to resolve ${handle}`);
391
- };
392
-
393
- type Result = { Status: number; Answer?: Answer[] };
394
- const isResult = (result: unknown): result is Result => {
395
- if (result === null || typeof result !== 'object') {
396
- return false;
397
- }
398
-
399
- return (
400
- 'Status' in result &&
401
- typeof result.Status === 'number' &&
402
- (!('Answer' in result) || (Array.isArray(result.Answer) && result.Answer.every(isAnswer)))
403
- );
404
- };
405
- const asResult = (result: unknown): Result => {
406
- if (!isResult(result)) {
407
- throw new TypeError(`unexpected DoH response`);
408
- }
409
-
410
- return result;
411
- };
412
-
413
- type Answer = { name: string; type: number; data: string; TTL: number };
414
- const isAnswer = (answer: unknown): answer is Answer => {
415
- if (answer === null || typeof answer !== 'object') {
416
- return false;
417
- }
418
-
419
- return (
420
- 'name' in answer &&
421
- typeof answer.name === 'string' &&
422
- 'type' in answer &&
423
- typeof answer.type === 'number' &&
424
- 'data' in answer &&
425
- typeof answer.data === 'string' &&
426
- 'TTL' in answer &&
427
- typeof answer.TTL === 'number'
428
- );
429
- };
430
-
431
- type AnswerTxt = Answer & { type: 16 };
432
- const isAnswerTxt = (answer: Answer): answer is AnswerTxt => {
433
- return answer.type === 16;
434
- };
435
-
436
- const extractTxtData = (answer: AnswerTxt): string => {
437
- return answer.data.replace(/^"|"$/g, '').replace(/\\"/g, '"');
438
- };
439
- ```
440
-
441
- [cf-resolver-firefox-privacy]: https://developers.cloudflare.com/1.1.1.1/privacy/cloudflare-resolver-firefox/
442
-
443
- #### using your PDS for handle resolution
444
-
445
- alternatively, if you operate your own PDS, you can make use of it as a handle resolver.
446
-
447
- ```ts
448
- const resolveHandleViaPds = async (handle: string): Promise<At.DID> => {
449
- const rpc = new XRPC({ handler: simpleFetchHandler({ service: `https://my-pds.example.com` }) });
450
-
451
- const { data } = await rpc.get('com.atproto.identity.resolveHandle', {
452
- params: {
453
- handle: handle,
454
- },
455
- });
456
-
457
- return data.did;
458
- };
459
- ```
@@ -1,21 +1,33 @@
1
- import type { IdentityMetadata } from '../types/identity.js';
2
- import type { AuthorizationServerMetadata } from '../types/server.js';
1
+ import type { ActorIdentifier } from '@atcute/lexicons';
3
2
  import type { Session } from '../types/token.js';
3
+ export type AuthorizeTargetOptions = {
4
+ type: 'account';
5
+ identifier: ActorIdentifier;
6
+ } | {
7
+ type: 'pds';
8
+ serviceUrl: string;
9
+ };
4
10
  export interface AuthorizeOptions {
5
- metadata: AuthorizationServerMetadata;
6
- identity?: IdentityMetadata;
11
+ target: AuthorizeTargetOptions;
7
12
  scope: string;
13
+ state?: unknown;
14
+ prompt?: 'none' | 'login' | 'consent' | 'select_account';
15
+ display?: 'page' | 'popup' | 'touch' | 'wap';
16
+ locale?: string;
8
17
  }
9
18
  /**
10
19
  * Create authentication URL for authorization
11
20
  * @param options
12
21
  * @returns URL to redirect the user for authorization
13
22
  */
14
- export declare const createAuthorizationUrl: ({ metadata, identity, scope, }: AuthorizeOptions) => Promise<URL>;
23
+ export declare const createAuthorizationUrl: (options: AuthorizeOptions) => Promise<URL>;
15
24
  /**
16
25
  * Finalize authorization
17
26
  * @param params Search params
18
27
  * @returns Session object, which you can use to instantiate user agents
19
28
  */
20
- export declare const finalizeAuthorization: (params: URLSearchParams) => Promise<Session>;
29
+ export declare const finalizeAuthorization: (params: URLSearchParams) => Promise<{
30
+ session: Session;
31
+ state: {} | null;
32
+ }>;
21
33
  //# sourceMappingURL=exchange.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"exchange.d.ts","sourceRoot":"","sources":["../../lib/agents/exchange.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAMjD,MAAM,WAAW,gBAAgB;IAChC,QAAQ,EAAE,2BAA2B,CAAC;IACtC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAU,gCAI1C,gBAAgB,KAAG,OAAO,CAAC,GAAG,CAoChC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAAU,QAAQ,eAAe,qBA6ClE,CAAC"}
1
+ {"version":3,"file":"exchange.d.ts","sourceRoot":"","sources":["../../lib/agents/exchange.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAOxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAOjD,MAAM,MAAM,sBAAsB,GAC/B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,eAAe,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvC,MAAM,WAAW,gBAAgB;IAChC,MAAM,EAAE,sBAAsB,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,gBAAgB,CAAC;IACzD,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAU,SAAS,gBAAgB,KAAG,OAAO,CAAC,GAAG,CAwDnF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAAU,QAAQ,eAAe;;;EA8ClE,CAAC"}
@@ -3,6 +3,7 @@ import { createES256Key } from '../dpop.js';
3
3
  import { CLIENT_ID, database, REDIRECT_URI } from '../environment.js';
4
4
  import { AuthorizationError, LoginError } from '../errors.js';
5
5
  import { generatePKCE } from '../utils/runtime.js';
6
+ import { resolveFromIdentifier, resolveFromService } from '../resolvers.js';
6
7
  import { OAuthServerAgent } from './server-agent.js';
7
8
  import { storeSession } from './sessions.js';
8
9
  /**
@@ -10,29 +11,45 @@ import { storeSession } from './sessions.js';
10
11
  * @param options
11
12
  * @returns URL to redirect the user for authorization
12
13
  */
13
- export const createAuthorizationUrl = async ({ metadata, identity, scope, }) => {
14
- const state = nanoid(24);
14
+ export const createAuthorizationUrl = async (options) => {
15
+ const { target, scope, state = null, ...reqs } = options;
16
+ let resolved;
17
+ switch (target.type) {
18
+ case 'account': {
19
+ resolved = await resolveFromIdentifier(target.identifier);
20
+ break;
21
+ }
22
+ case 'pds': {
23
+ resolved = await resolveFromService(target.serviceUrl);
24
+ }
25
+ }
26
+ const { identity, metadata } = resolved;
27
+ const loginHint = identity
28
+ ? identity.handle !== 'handle.invalid'
29
+ ? identity.handle
30
+ : identity.did
31
+ : undefined;
32
+ const sid = nanoid(24);
15
33
  const pkce = await generatePKCE();
16
34
  const dpopKey = await createES256Key();
17
35
  const params = {
36
+ display: reqs.display,
37
+ ui_locales: reqs.locale,
38
+ prompt: reqs.prompt,
18
39
  redirect_uri: REDIRECT_URI,
19
40
  code_challenge: pkce.challenge,
20
41
  code_challenge_method: pkce.method,
21
- state: state,
22
- login_hint: identity?.raw,
42
+ state: sid,
43
+ login_hint: loginHint,
23
44
  response_mode: 'fragment',
24
45
  response_type: 'code',
25
- display: 'page',
26
- // id_token_hint: undefined,
27
- // max_age: undefined,
28
- // prompt: undefined,
29
46
  scope: scope,
30
- // ui_locales: undefined,
31
47
  };
32
- database.states.set(state, {
48
+ database.states.set(sid, {
33
49
  dpopKey: dpopKey,
34
50
  metadata: metadata,
35
51
  verifier: pkce.verifier,
52
+ state: state,
36
53
  });
37
54
  const server = new OAuthServerAgent(metadata, dpopKey);
38
55
  const response = await server.request('pushed_authorization_request', params);
@@ -48,28 +65,29 @@ export const createAuthorizationUrl = async ({ metadata, identity, scope, }) =>
48
65
  */
49
66
  export const finalizeAuthorization = async (params) => {
50
67
  const issuer = params.get('iss');
51
- const state = params.get('state');
68
+ const sid = params.get('state');
52
69
  const code = params.get('code');
53
70
  const error = params.get('error');
54
- if (!state || !(code || error)) {
71
+ if (!sid || !(code || error)) {
55
72
  throw new LoginError(`missing parameters`);
56
73
  }
57
- const stored = database.states.get(state);
74
+ const stored = database.states.get(sid);
58
75
  if (stored) {
59
76
  // Delete now that we've caught it
60
- database.states.delete(state);
77
+ database.states.delete(sid);
61
78
  }
62
79
  else {
63
80
  throw new LoginError(`unknown state provided`);
64
81
  }
65
- const dpopKey = stored.dpopKey;
66
- const metadata = stored.metadata;
67
82
  if (error) {
68
83
  throw new AuthorizationError(params.get('error_description') || error);
69
84
  }
70
85
  if (!code) {
71
86
  throw new LoginError(`missing code parameter`);
72
87
  }
88
+ const dpopKey = stored.dpopKey;
89
+ const metadata = stored.metadata;
90
+ const state = stored.state ?? null;
73
91
  if (issuer === null) {
74
92
  throw new LoginError(`missing issuer parameter`);
75
93
  }
@@ -83,6 +101,6 @@ export const finalizeAuthorization = async (params) => {
83
101
  const sub = info.sub;
84
102
  const session = { dpopKey, info, token };
85
103
  await storeSession(sub, session);
86
- return session;
104
+ return { session, state };
87
105
  };
88
106
  //# sourceMappingURL=exchange.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"exchange.js","sourceRoot":"","sources":["../../lib/agents/exchange.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"exchange.js","sourceRoot":"","sources":["../../lib/agents/exchange.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"server-agent.d.ts","sourceRoot":"","sources":["../../lib/agents/server-agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAM5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAIrF,qBAAa,gBAAgB;;gBAIhB,QAAQ,EAAE,oCAAoC,EAAE,OAAO,EAAE,OAAO;IAKtE,OAAO,CACZ,QAAQ,EAAE,8BAA8B,EACxC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,gBAAgB,CAAC;IACtB,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC;IACzF,OAAO,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC;IAC/E,OAAO,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC;IA0BlF,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpC,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,YAAY,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAC;IAgBhG,OAAO,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;QAAE,GAAG,EAAE,GAAG,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,GAAG,OAAO,CAAC,SAAS,CAAC;CAwEjF"}
1
+ {"version":3,"file":"server-agent.d.ts","sourceRoot":"","sources":["../../lib/agents/server-agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAM5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAIrF,qBAAa,gBAAgB;;gBAKhB,QAAQ,EAAE,oCAAoC,EAAE,OAAO,EAAE,OAAO;IAMtE,OAAO,CACZ,QAAQ,EAAE,8BAA8B,EACxC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,gBAAgB,CAAC;IACtB,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC;IACzF,OAAO,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC;IAC/E,OAAO,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC;IA4ClF,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpC,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,YAAY,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAC;IAgBhG,OAAO,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;QAAE,GAAG,EAAE,GAAG,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,GAAG,OAAO,CAAC,SAAS,CAAC;CAwEjF"}
@@ -1,14 +1,16 @@
1
- import { createDPoPFetch } from '../dpop.js';
2
- import { CLIENT_ID, REDIRECT_URI } from '../environment.js';
1
+ import { createDPoPFetch, createDPoPSignage } from '../dpop.js';
2
+ import { CLIENT_ID, fetchClientAssertion, REDIRECT_URI } from '../environment.js';
3
3
  import { FetchResponseError, OAuthResponseError, TokenRefreshError } from '../errors.js';
4
- import { resolveFromIdentity } from '../resolvers.js';
4
+ import { resolveFromIdentifier } from '../resolvers.js';
5
5
  import { pick } from '../utils/misc.js';
6
6
  import { extractContentType } from '../utils/response.js';
7
7
  export class OAuthServerAgent {
8
8
  #fetch;
9
9
  #metadata;
10
+ #dpopKey;
10
11
  constructor(metadata, dpopKey) {
11
12
  this.#metadata = metadata;
13
+ this.#dpopKey = dpopKey;
12
14
  this.#fetch = createDPoPFetch(dpopKey, true);
13
15
  }
14
16
  async request(endpoint, payload) {
@@ -16,6 +18,21 @@ export class OAuthServerAgent {
16
18
  if (!url) {
17
19
  throw new Error(`no endpoint for ${endpoint}`);
18
20
  }
21
+ if (endpoint === 'token' && fetchClientAssertion !== undefined) {
22
+ const jkt = this.#dpopKey.jkt;
23
+ if (jkt === undefined) {
24
+ throw new Error(`DPoP key missing jkt field`);
25
+ }
26
+ const clientAssertionCredentials = await fetchClientAssertion({
27
+ jkt: jkt,
28
+ aud: this.#metadata.issuer,
29
+ createDpopProof: async (url) => {
30
+ const sign = createDPoPSignage(this.#dpopKey);
31
+ return await sign('POST', url, undefined, undefined);
32
+ },
33
+ });
34
+ payload = { ...payload, ...clientAssertionCredentials };
35
+ }
19
36
  const response = await this.#fetch(url, {
20
37
  method: 'post',
21
38
  headers: { 'content-type': 'application/json' },
@@ -96,7 +113,7 @@ export class OAuthServerAgent {
96
113
  throw new TypeError(`missing sub field in token response`);
97
114
  }
98
115
  const token = this.#processTokenResponse(res);
99
- const resolved = await resolveFromIdentity(sub);
116
+ const resolved = await resolveFromIdentifier(sub);
100
117
  if (resolved.metadata.issuer !== this.#metadata.issuer) {
101
118
  throw new TypeError(`issuer mismatch; got ${resolved.metadata.issuer}`);
102
119
  }
@@ -104,7 +121,7 @@ export class OAuthServerAgent {
104
121
  token: token,
105
122
  info: {
106
123
  sub: sub,
107
- aud: resolved.identity.pds.href,
124
+ aud: resolved.identity.pds,
108
125
  server: pick(resolved.metadata, [
109
126
  'issuer',
110
127
  'authorization_endpoint',
@@ -1 +1 @@
1
- {"version":3,"file":"server-agent.js","sourceRoot":"","sources":["../../lib/agents/server-agent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server-agent.js","sourceRoot":"","sources":["../../lib/agents/server-agent.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"dpop.d.ts","sourceRoot":"","sources":["../lib/dpop.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAM/C,eAAO,MAAM,cAAc,QAAa,OAAO,CAAC,OAAO,CAWtD,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,SAAS,OAAO,MAuBnC,QAAQ,MAAM,EAAE,KAAK,MAAM,EAAE,OAAO,MAAM,GAAG,SAAS,EAAE,KAAK,MAAM,GAAG,SAAS,oBAa7F,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,SAAS,OAAO,EAAE,eAAe,OAAO,KAAG,OAAO,KA+HjF,CAAC"}
1
+ {"version":3,"file":"dpop.d.ts","sourceRoot":"","sources":["../lib/dpop.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAM/C,eAAO,MAAM,cAAc,QAAa,OAAO,CAAC,OAAO,CAetD,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,SAAS,OAAO,MAuBnC,QAAQ,MAAM,EAAE,KAAK,MAAM,EAAE,OAAO,MAAM,GAAG,SAAS,EAAE,KAAK,MAAM,GAAG,SAAS,oBAa7F,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,SAAS,OAAO,EAAE,eAAe,OAAO,KAAG,OAAO,KA+HjF,CAAC"}
package/dist/dpop.js CHANGED
@@ -9,10 +9,13 @@ export const createES256Key = async () => {
9
9
  const pair = await crypto.subtle.generateKey(ES256_ALG, true, ['sign', 'verify']);
10
10
  const key = await crypto.subtle.exportKey('pkcs8', pair.privateKey);
11
11
  const { ext: _ext, key_ops: _key_opts, ...jwk } = await crypto.subtle.exportKey('jwk', pair.publicKey);
12
+ const canonicalJwk = JSON.stringify({ crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y });
13
+ const jkt = await stringToSha256(canonicalJwk);
12
14
  return {
13
15
  typ: 'ES256',
14
16
  key: toBase64Url(new Uint8Array(key)),
15
17
  jwt: toBase64Url(encodeUtf8(JSON.stringify({ typ: 'dpop+jwt', alg: 'ES256', jwk: jwk }))),
18
+ jkt: jkt,
16
19
  };
17
20
  };
18
21
  export const createDPoPSignage = (dpopKey) => {