@julr/sesame 0.6.0 → 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.
Files changed (70) hide show
  1. package/README.md +69 -6
  2. package/build/{authorize_controller-BiycO4be.js → authorize_controller-sCZIjnc-.js} +10 -10
  3. package/build/{client_info_controller-AcOG8lWu.js → client_info_controller-B5A3tpbk.js} +3 -4
  4. package/build/commands/sesame_key.d.ts +1 -1
  5. package/build/commands/sesame_purge.js +2 -5
  6. package/build/{configure-DkDkIlt8.js → configure-t9q3KonY.js} +7 -3
  7. package/build/configure.js +1 -2
  8. package/build/{consent_controller-Dsdhv6-f.js → consent_controller-ChBboaSd.js} +17 -29
  9. package/build/consent_retry-DMkfhmk9.js +41 -0
  10. package/build/decorate-RQD1h28J.js +9 -0
  11. package/build/{id_token_service-CpTzOUDe.js → id_token_service-BtBUiL_n.js} +1 -1
  12. package/build/index.d.ts +2 -8
  13. package/build/index.js +38 -9
  14. package/build/{introspect_controller-DvOp9scr.js → introspect_controller-LVYGWhMj.js} +13 -9
  15. package/build/{issue_authorization_code-B9ERu1uO.js → issue_authorization_code-Dr4-bbMp.js} +2 -3
  16. package/build/{jwks_controller-keo4kBZc.js → jwks_controller-4HUVS_HC.js} +1 -5
  17. package/build/{main-DGBJhq3E.js → main-Dx9kZv07.js} +14 -52
  18. package/build/{metadata_controller-BVsTo0Gp.js → metadata_controller-hwfmxlF7.js} +4 -6
  19. package/build/providers/sesame_provider.d.ts +1 -1
  20. package/build/providers/sesame_provider.js +8 -8
  21. package/build/{register_controller-gbq7p8a5.js → register_controller-Cdk3c32j.js} +3 -6
  22. package/build/{revoke_controller-z_ghrEB7.js → revoke_controller-Daz4Yqfs.js} +15 -21
  23. package/build/services/main.js +1 -5
  24. package/build/{sesame_manager-DYUSZ0NC.js → sesame_manager-CeEmlzba.js} +78 -256
  25. package/build/sesame_manager-DQxvsTEq.js +2 -0
  26. package/build/src/actions/exchange_authorization_code.d.ts +5 -5
  27. package/build/src/actions/exchange_client_credentials.d.ts +3 -3
  28. package/build/src/actions/exchange_refresh_token.d.ts +4 -4
  29. package/build/src/actions/issue_authorization_code.d.ts +2 -2
  30. package/build/src/controllers/authorize_controller.d.ts +11 -11
  31. package/build/src/controllers/metadata_controller.d.ts +7 -7
  32. package/build/src/controllers/register_controller.d.ts +30 -30
  33. package/build/src/guard/guard.d.ts +1 -1
  34. package/build/src/guard/lucid.d.ts +8 -0
  35. package/build/src/guard/lucid.js +46 -0
  36. package/build/src/guard/main.d.ts +2 -9
  37. package/build/src/guard/main.js +2 -6
  38. package/build/src/guard/types.d.ts +0 -9
  39. package/build/src/guard/user_provider.d.ts +9 -1
  40. package/build/src/middleware/any_scope_middleware.js +1 -2
  41. package/build/src/middleware/scope_middleware.js +1 -2
  42. package/build/src/oauth_error.d.ts +74 -74
  43. package/build/src/services/client_service.d.ts +5 -2
  44. package/build/src/sesame_manager.d.ts +6 -5
  45. package/build/src/storage/consent_retry.d.ts +4 -0
  46. package/build/src/storage/drivers/kysely.d.ts +166 -0
  47. package/build/src/storage/drivers/kysely.js +480 -0
  48. package/build/src/storage/drivers/lucid.d.ts +162 -0
  49. package/build/src/storage/drivers/lucid.js +549 -0
  50. package/build/src/storage/migrations/kysely.d.ts +6 -0
  51. package/build/src/storage/types.d.ts +197 -0
  52. package/build/src/storage/types.js +1 -0
  53. package/build/src/stores.d.ts +21 -0
  54. package/build/src/types.d.ts +9 -1
  55. package/build/src/types.js +2 -0
  56. package/build/stubs/config/sesame.stub +4 -1
  57. package/build/stubs/config/sesame_kysely.stub +35 -0
  58. package/build/stubs/migrations/kysely/create_oauth_tables.stub +151 -0
  59. package/build/taze.config.d.ts +2 -0
  60. package/build/{token_controller-DyI7oy-U.js → token_controller-DRatCQNT.js} +74 -63
  61. package/build/types-DEtn2Zdb.js +75 -0
  62. package/build/{userinfo_controller-RLk8cN_o.js → userinfo_controller-YwN26wK-.js} +5 -8
  63. package/build/vite.config.d.ts +1 -1
  64. package/package.json +33 -17
  65. package/build/chunk-DF48asd8.js +0 -9
  66. package/build/oauth_access_token-Cz_5gNBx.js +0 -29
  67. package/build/oauth_client-BSanvSql.js +0 -63
  68. package/build/sesame_manager-B1Jgq1v2.js +0 -6
  69. /package/build/{oauth_error-C7UhDb2q.js → oauth_error-CvxtZvjp.js} +0 -0
  70. /package/build/{token_service-DwnfAR9F.js → token_service-DDQ3Dxaj.js} +0 -0
@@ -1,10 +1,8 @@
1
- import "./chunk-DF48asd8.js";
2
- import { a as OAuthRefreshToken, c as OIDC_SCOPES, i as OAuthAuthorizationCode, o as ClientService, s as BUILTIN_SCOPES, t as SesameManager } from "./sesame_manager-DYUSZ0NC.js";
3
- import "./oauth_client-BSanvSql.js";
4
- import { a as E_INVALID_GRANT, o as E_INVALID_REQUEST, r as E_INVALID_CLIENT, s as E_INVALID_SCOPE, u as E_UNSUPPORTED_GRANT_TYPE } from "./oauth_error-C7UhDb2q.js";
5
- import { t as OAuthAccessToken } from "./oauth_access_token-Cz_5gNBx.js";
6
- import { t as TokenService } from "./token_service-DwnfAR9F.js";
7
- import { t as IdTokenService } from "./id_token_service-CpTzOUDe.js";
1
+ import { n as OIDC_SCOPES, t as BUILTIN_SCOPES } from "./types-DEtn2Zdb.js";
2
+ import { a as E_INVALID_GRANT, o as E_INVALID_REQUEST, r as E_INVALID_CLIENT, s as E_INVALID_SCOPE, u as E_UNSUPPORTED_GRANT_TYPE } from "./oauth_error-CvxtZvjp.js";
3
+ import { n as ClientService, t as SesameManager } from "./sesame_manager-CeEmlzba.js";
4
+ import { t as TokenService } from "./token_service-DDQ3Dxaj.js";
5
+ import { t as IdTokenService } from "./id_token_service-BtBUiL_n.js";
8
6
  import { DateTime } from "luxon";
9
7
  import { createHash } from "node:crypto";
10
8
  import string from "@adonisjs/core/helpers/string";
@@ -35,13 +33,13 @@ var ExchangeAuthorizationCodeAction = class {
35
33
  const tokenService = new TokenService(manager);
36
34
  const clientService = new ClientService();
37
35
  if (!input.client.grantTypes.includes("authorization_code")) throw new E_INVALID_CLIENT("Client is not allowed to use the authorization_code grant");
38
- const authCode = await this.#validateAuthorizationCode(tokenService, input);
39
- await this.#verifyPkce(input.codeVerifier, authCode);
36
+ const authCode = await this.#validateAuthorizationCode(manager, tokenService, input);
37
+ await this.#verifyPkce(manager, input.codeVerifier, authCode);
40
38
  clientService.validateClientScopes(authCode.scopes, input.client.scopes);
41
39
  const accessToken = tokenService.createAccessToken();
42
40
  const refreshToken = this.#prepareRefreshToken(manager, tokenService);
43
41
  const idToken = await this.#prepareIdToken(manager, authCode, input.client, accessToken.raw);
44
- await this.#atomicExchange(input, authCode, accessToken, refreshToken);
42
+ await this.#atomicExchange(manager, input, authCode, accessToken, refreshToken);
45
43
  const ttlSeconds = string.seconds.parse(manager.config.accessTokenTtl);
46
44
  return {
47
45
  access_token: accessToken.raw,
@@ -56,14 +54,18 @@ var ExchangeAuthorizationCodeAction = class {
56
54
  * Lookup the authorization code by hash and validate
57
55
  * it has not expired and matches the redirect_uri.
58
56
  */
59
- async #validateAuthorizationCode(tokenService, input) {
57
+ async #validateAuthorizationCode(manager, tokenService, input) {
60
58
  if (!input.code) throw new E_INVALID_REQUEST("Missing required parameter: code");
61
59
  if (!input.redirectUri) throw new E_INVALID_REQUEST("Missing required parameter: redirect_uri");
62
60
  const hashedCode = tokenService.hashToken(input.code);
63
- const authCode = await OAuthAuthorizationCode.query().where("code", hashedCode).where("clientId", input.client.clientId).first();
61
+ const store = manager.store;
62
+ const authCode = await store.findAuthorizationCode({
63
+ code: hashedCode,
64
+ clientId: input.client.clientId
65
+ });
64
66
  if (!authCode) throw new E_INVALID_GRANT("Authorization code not found");
65
67
  if (authCode.expiresAt < DateTime.now()) {
66
- await OAuthAuthorizationCode.query().where("id", authCode.id).delete();
68
+ await store.deleteAuthorizationCode(authCode.id);
67
69
  throw new E_INVALID_GRANT("Authorization code has expired");
68
70
  }
69
71
  if (authCode.redirectUri !== input.redirectUri) throw new E_INVALID_GRANT("Redirect URI mismatch");
@@ -73,18 +75,19 @@ var ExchangeAuthorizationCodeAction = class {
73
75
  * Verify the PKCE code_verifier against the stored
74
76
  * code_challenge using S256 (mandatory per OAuth 2.1).
75
77
  */
76
- async #verifyPkce(codeVerifier, authCode) {
78
+ async #verifyPkce(manager, codeVerifier, authCode) {
79
+ const store = manager.store;
77
80
  const [verifierError] = await codeVerifierValidator.tryValidate({ code_verifier: codeVerifier });
78
81
  if (verifierError) {
79
- await OAuthAuthorizationCode.query().where("id", authCode.id).delete();
82
+ await store.deleteAuthorizationCode(authCode.id);
80
83
  throw new E_INVALID_REQUEST("code_verifier must be 43-128 characters using only [A-Za-z0-9-._~] (RFC 7636 §4.1)");
81
84
  }
82
85
  if (!authCode.codeChallenge) {
83
- await OAuthAuthorizationCode.query().where("id", authCode.id).delete();
86
+ await store.deleteAuthorizationCode(authCode.id);
84
87
  throw new E_INVALID_GRANT("Authorization code is missing PKCE challenge");
85
88
  }
86
89
  if (createHash("sha256").update(codeVerifier).digest("base64url") !== authCode.codeChallenge) {
87
- await OAuthAuthorizationCode.query().where("id", authCode.id).delete();
90
+ await store.deleteAuthorizationCode(authCode.id);
88
91
  throw new E_INVALID_GRANT("PKCE verification failed");
89
92
  }
90
93
  }
@@ -125,20 +128,20 @@ var ExchangeAuthorizationCodeAction = class {
125
128
  * the new access token (and optionally refresh token)
126
129
  * inside a single transaction.
127
130
  */
128
- async #atomicExchange(input, authCode, accessToken, refreshToken) {
129
- await OAuthAuthorizationCode.transaction(async (trx) => {
130
- const deleteResult = await OAuthAuthorizationCode.query({ client: trx }).where("id", authCode.id).delete();
131
- if ((Array.isArray(deleteResult) ? Number(deleteResult[0] ?? 0) : Number(deleteResult)) !== 1) throw new E_INVALID_GRANT("Authorization code has already been consumed");
132
- const accessTokenId = crypto.randomUUID();
133
- await OAuthAccessToken.create({
131
+ async #atomicExchange(manager, input, authCode, accessToken, refreshToken) {
132
+ const store = manager.store;
133
+ const accessTokenId = crypto.randomUUID();
134
+ if (!await store.exchangeAuthorizationCode({
135
+ codeId: authCode.id,
136
+ accessToken: {
134
137
  id: accessTokenId,
135
138
  tokenHash: accessToken.hash,
136
139
  clientId: input.client.clientId,
137
140
  userId: authCode.userId,
138
141
  scopes: authCode.scopes,
139
142
  expiresAt: DateTime.fromJSDate(accessToken.expiresAt)
140
- }, { client: trx });
141
- if (refreshToken) await OAuthRefreshToken.create({
143
+ },
144
+ refreshToken: refreshToken ? {
142
145
  id: crypto.randomUUID(),
143
146
  token: refreshToken.hash,
144
147
  accessTokenId,
@@ -146,8 +149,8 @@ var ExchangeAuthorizationCodeAction = class {
146
149
  userId: authCode.userId,
147
150
  scopes: authCode.scopes,
148
151
  expiresAt: refreshToken.expiresAt
149
- }, { client: trx });
150
- });
152
+ } : null
153
+ })) throw new E_INVALID_GRANT("Authorization code has already been consumed");
151
154
  }
152
155
  };
153
156
  //#endregion
@@ -200,13 +203,16 @@ var ExchangeRefreshTokenAction = class {
200
203
  if (!input.refreshToken) throw new E_INVALID_REQUEST("Missing required parameter: refresh_token");
201
204
  if (!input.client.grantTypes.includes("refresh_token")) throw new E_INVALID_CLIENT("Client is not allowed to use the refresh_token grant");
202
205
  const hashedToken = tokenService.hashToken(input.refreshToken);
203
- const refreshToken = await OAuthRefreshToken.query().where("token", hashedToken).where("clientId", input.client.clientId).first();
206
+ const refreshToken = await manager.store.findRefreshToken({
207
+ hash: hashedToken,
208
+ clientId: input.client.clientId
209
+ });
204
210
  if (!refreshToken) throw new E_INVALID_GRANT("Refresh token not found");
205
211
  if (refreshToken.revokedAt) {
206
212
  const gracePeriodSeconds = manager.config.refreshTokenRotationGracePeriod;
207
213
  const revokedSecondsAgo = DateTime.now().diff(refreshToken.revokedAt, "seconds").seconds;
208
214
  if (gracePeriodSeconds <= 0 || revokedSecondsAgo > gracePeriodSeconds) {
209
- await this.#nukeTokensForReplay(input.client.clientId, refreshToken.userId);
215
+ await this.#nukeTokensForReplay(manager, input.client.clientId, refreshToken.userId);
210
216
  throw new E_INVALID_GRANT("Refresh token has been revoked (possible replay attack)");
211
217
  }
212
218
  /**
@@ -223,7 +229,7 @@ var ExchangeRefreshTokenAction = class {
223
229
  const accessToken = tokenService.createAccessToken();
224
230
  const newRefreshToken = this.#prepareRefreshToken(manager, tokenService);
225
231
  const idToken = await this.#prepareIdToken(manager, scopes, refreshToken, input.client, accessToken.raw);
226
- await this.#atomicRotation(input, refreshToken, accessToken, newRefreshToken, scopes);
232
+ await this.#atomicRotation(manager, input, refreshToken, accessToken, newRefreshToken, scopes);
227
233
  const ttlSeconds = string.seconds.parse(manager.config.accessTokenTtl);
228
234
  return {
229
235
  access_token: accessToken.raw,
@@ -248,22 +254,24 @@ var ExchangeRefreshTokenAction = class {
248
254
  const newRefreshToken = this.#prepareRefreshToken(manager, tokenService);
249
255
  const idToken = await this.#prepareIdToken(manager, scopes, revokedRefreshToken, input.client, accessToken.raw);
250
256
  const accessTokenId = crypto.randomUUID();
251
- await OAuthAccessToken.create({
252
- id: accessTokenId,
253
- tokenHash: accessToken.hash,
254
- clientId: input.client.clientId,
255
- userId: revokedRefreshToken.userId,
256
- scopes,
257
- expiresAt: DateTime.fromJSDate(accessToken.expiresAt)
258
- });
259
- await OAuthRefreshToken.create({
260
- id: crypto.randomUUID(),
261
- token: newRefreshToken.hash,
262
- accessTokenId,
263
- clientId: input.client.clientId,
264
- userId: revokedRefreshToken.userId,
265
- scopes,
266
- expiresAt: newRefreshToken.expiresAt
257
+ await manager.store.issueTokenPair({
258
+ accessToken: {
259
+ id: accessTokenId,
260
+ tokenHash: accessToken.hash,
261
+ clientId: input.client.clientId,
262
+ userId: revokedRefreshToken.userId,
263
+ scopes,
264
+ expiresAt: DateTime.fromJSDate(accessToken.expiresAt)
265
+ },
266
+ refreshToken: {
267
+ id: crypto.randomUUID(),
268
+ token: newRefreshToken.hash,
269
+ accessTokenId,
270
+ clientId: input.client.clientId,
271
+ userId: revokedRefreshToken.userId,
272
+ scopes,
273
+ expiresAt: newRefreshToken.expiresAt
274
+ }
267
275
  });
268
276
  const ttlSeconds = string.seconds.parse(manager.config.accessTokenTtl);
269
277
  return {
@@ -279,9 +287,12 @@ var ExchangeRefreshTokenAction = class {
279
287
  * Replay detection: nuke all tokens for this client+user
280
288
  * pair when a revoked token is reused.
281
289
  */
282
- async #nukeTokensForReplay(clientId, userId) {
283
- await OAuthRefreshToken.query().where("clientId", clientId).where("userId", userId).delete();
284
- await OAuthAccessToken.query().where("clientId", clientId).where("userId", userId).whereNull("revokedAt").update({ revokedAt: DateTime.now().toSQL() });
290
+ async #nukeTokensForReplay(manager, clientId, userId) {
291
+ await manager.store.revokeTokenFamily({
292
+ clientId,
293
+ userId,
294
+ now: DateTime.now()
295
+ });
285
296
  }
286
297
  /**
287
298
  * Resolve the effective scopes for the new token. Supports
@@ -335,22 +346,22 @@ var ExchangeRefreshTokenAction = class {
335
346
  * Atomically revoke the old token pair and persist the
336
347
  * new access + refresh tokens inside a single transaction.
337
348
  */
338
- async #atomicRotation(input, oldRefreshToken, accessToken, newRefreshToken, scopes) {
339
- await OAuthRefreshToken.transaction(async (trx) => {
340
- const revokedAt = DateTime.now();
341
- const updateResult = await OAuthRefreshToken.query({ client: trx }).where("id", oldRefreshToken.id).whereNull("revokedAt").update({ revokedAt: revokedAt.toSQL() });
342
- if ((Array.isArray(updateResult) ? Number(updateResult[0] ?? 0) : Number(updateResult)) !== 1) throw new E_INVALID_GRANT("Refresh token has already been consumed");
343
- await OAuthAccessToken.query({ client: trx }).where("id", oldRefreshToken.accessTokenId).whereNull("revokedAt").update({ revokedAt: revokedAt.toSQL() });
344
- const accessTokenId = crypto.randomUUID();
345
- await OAuthAccessToken.create({
349
+ async #atomicRotation(manager, input, oldRefreshToken, accessToken, newRefreshToken, scopes) {
350
+ const store = manager.store;
351
+ const accessTokenId = crypto.randomUUID();
352
+ if (!await store.rotateRefreshToken({
353
+ oldRefreshTokenId: oldRefreshToken.id,
354
+ oldAccessTokenId: oldRefreshToken.accessTokenId,
355
+ revokedAt: DateTime.now(),
356
+ accessToken: {
346
357
  id: accessTokenId,
347
358
  tokenHash: accessToken.hash,
348
359
  clientId: input.client.clientId,
349
360
  userId: oldRefreshToken.userId,
350
361
  scopes,
351
362
  expiresAt: DateTime.fromJSDate(accessToken.expiresAt)
352
- }, { client: trx });
353
- await OAuthRefreshToken.create({
363
+ },
364
+ refreshToken: {
354
365
  id: crypto.randomUUID(),
355
366
  token: newRefreshToken.hash,
356
367
  accessTokenId,
@@ -358,8 +369,8 @@ var ExchangeRefreshTokenAction = class {
358
369
  userId: oldRefreshToken.userId,
359
370
  scopes,
360
371
  expiresAt: newRefreshToken.expiresAt
361
- }, { client: trx });
362
- });
372
+ }
373
+ })) throw new E_INVALID_GRANT("Refresh token has already been consumed");
363
374
  }
364
375
  };
365
376
  //#endregion
@@ -388,7 +399,7 @@ var ExchangeClientCredentialsAction = class {
388
399
  const ttlSeconds = string.seconds.parse(manager.config.clientCredentialsAccessTokenTtl);
389
400
  const { raw: accessTokenRaw, hash: tokenHash } = tokenService.createAccessToken();
390
401
  const expiresAt = new Date(Date.now() + ttlSeconds * 1e3);
391
- await OAuthAccessToken.create({
402
+ await manager.store.createAccessToken({
392
403
  id: crypto.randomUUID(),
393
404
  tokenHash,
394
405
  clientId: input.client.clientId,
@@ -441,7 +452,7 @@ var TokenController = class TokenController {
441
452
  const [error, validated] = await TokenController.validator.tryValidate(body);
442
453
  if (error) throw new E_UNSUPPORTED_GRANT_TYPE("Missing required parameter: grant_type");
443
454
  if (!manager.isGrantTypeEnabled(validated.grant_type)) throw new E_UNSUPPORTED_GRANT_TYPE(`Grant type "${validated.grant_type}" is not enabled`);
444
- const client = await new ClientService().authenticateClient({
455
+ const client = await new ClientService(manager).authenticateClient({
445
456
  authorizationHeader: ctx.request.header("authorization"),
446
457
  bodyClientId: body.client_id,
447
458
  bodyClientSecret: body.client_secret
@@ -0,0 +1,75 @@
1
+ //#region src/types.ts
2
+ /**
3
+ * Standard OAuth/OIDC scopes that are always valid regardless
4
+ * of server or client scope configuration.
5
+ *
6
+ * These scopes are:
7
+ * - Accepted during scope validation (client and server level)
8
+ * - Advertised in `scopes_supported` of all metadata endpoints
9
+ * (protected resource, OIDC discovery) so MCP clients know
10
+ * they can request them
11
+ *
12
+ * - `offline_access`: signals that the client needs a refresh token
13
+ * (OIDC Core §11). Note: Sesame issues refresh tokens by default
14
+ * when the `refresh_token` grant is enabled, regardless of whether
15
+ * the client requests this scope (per RFC 6749 §5.1). This scope
16
+ * is still advertised for clients that check metadata before
17
+ * building their authorization request.
18
+ *
19
+ * @see https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
20
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.1
21
+ */
22
+ const BUILTIN_SCOPES = /* @__PURE__ */ new Set(["offline_access"]);
23
+ /**
24
+ * OIDC-recognized scopes that are accepted by server-level validation
25
+ * without needing to be declared in the config `scopes` map.
26
+ *
27
+ * Unlike `BUILTIN_SCOPES`, these still require explicit client authorization
28
+ * via `ClientService.validateClientScopes()`.
29
+ */
30
+ const OIDC_SCOPES = /* @__PURE__ */ new Set([
31
+ "openid",
32
+ "profile",
33
+ "email"
34
+ ]);
35
+ /**
36
+ * Protocol-managed claims that must never be overridden by `getOidcClaims()`.
37
+ */
38
+ const RESERVED_OIDC_CLAIMS = /* @__PURE__ */ new Set([
39
+ "sub",
40
+ "iss",
41
+ "aud",
42
+ "exp",
43
+ "iat",
44
+ "nbf",
45
+ "jti",
46
+ "nonce",
47
+ "at_hash",
48
+ "auth_time",
49
+ "acr",
50
+ "azp",
51
+ "sid"
52
+ ]);
53
+ /**
54
+ * Collect OIDC claims based on the granted scopes.
55
+ *
56
+ * Maps each scope to its corresponding claims object and merges
57
+ * only the claims for scopes present in the granted set.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * getOidcClaims(scopes: Scope[]) {
62
+ * return collectOidcClaims(scopes, {
63
+ * profile: { name: this.fullName },
64
+ * email: { email: this.email },
65
+ * })
66
+ * }
67
+ * ```
68
+ */
69
+ function collectOidcClaims(scopes, claimsMap) {
70
+ const result = {};
71
+ for (const [scope, claims] of Object.entries(claimsMap)) if (scopes.includes(scope) && claims) Object.assign(result, claims);
72
+ return result;
73
+ }
74
+ //#endregion
75
+ export { collectOidcClaims as i, OIDC_SCOPES as n, RESERVED_OIDC_CLAIMS as r, BUILTIN_SCOPES as t };
@@ -1,10 +1,7 @@
1
- import "./chunk-DF48asd8.js";
2
- import { t as SesameManager } from "./sesame_manager-DYUSZ0NC.js";
3
- import "./oauth_client-BSanvSql.js";
4
- import { c as E_INVALID_TOKEN, n as E_INSUFFICIENT_SCOPE, o as E_INVALID_REQUEST } from "./oauth_error-C7UhDb2q.js";
5
- import { t as OAuthAccessToken } from "./oauth_access_token-Cz_5gNBx.js";
6
- import { t as TokenService } from "./token_service-DwnfAR9F.js";
7
- import { t as IdTokenService } from "./id_token_service-CpTzOUDe.js";
1
+ import { c as E_INVALID_TOKEN, n as E_INSUFFICIENT_SCOPE, o as E_INVALID_REQUEST } from "./oauth_error-CvxtZvjp.js";
2
+ import { t as SesameManager } from "./sesame_manager-CeEmlzba.js";
3
+ import { t as TokenService } from "./token_service-DDQ3Dxaj.js";
4
+ import { t as IdTokenService } from "./id_token_service-BtBUiL_n.js";
8
5
  //#region src/controllers/userinfo_controller.ts
9
6
  /**
10
7
  * OpenID Connect UserInfo endpoint (OIDC Core §5.3).
@@ -21,7 +18,7 @@ var UserinfoController = class {
21
18
  const rawToken = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : ctx.request.body().access_token;
22
19
  if (!rawToken) throw new E_INVALID_REQUEST("Missing Bearer token");
23
20
  const hashed = new TokenService(manager).hashToken(rawToken);
24
- const token = await OAuthAccessToken.query().where("tokenHash", hashed).first();
21
+ const token = await manager.store.findAccessToken({ hash: hashed });
25
22
  if (!token) throw new E_INVALID_TOKEN("Invalid access token");
26
23
  if (token.revokedAt) throw new E_INVALID_TOKEN("Access token has been revoked");
27
24
  if (token.expiresAt.toJSDate() < /* @__PURE__ */ new Date()) throw new E_INVALID_TOKEN("Access token has expired");
@@ -1,2 +1,2 @@
1
- declare const _default: UserConfig;
1
+ declare const _default: import("vite").UserConfig;
2
2
  export default _default;
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@julr/sesame",
3
3
  "description": "OAuth 2.1 + OIDC server for AdonisJS",
4
- "version": "0.6.0",
4
+ "version": "0.7.0",
5
5
  "engines": {
6
6
  "node": ">=24.0.0"
7
7
  },
8
- "packageManager": "pnpm@10.26.2",
8
+ "packageManager": "pnpm@10.34.5",
9
9
  "type": "module",
10
10
  "files": [
11
11
  "build",
@@ -17,8 +17,12 @@
17
17
  ".": "./build/index.js",
18
18
  "./exceptions": "./build/src/oauth_error.js",
19
19
  "./types": "./build/src/types.js",
20
+ "./storage/types": "./build/src/storage/types.js",
21
+ "./drivers/lucid": "./build/src/storage/drivers/lucid.js",
22
+ "./drivers/kysely": "./build/src/storage/drivers/kysely.js",
20
23
  "./sesame_provider": "./build/providers/sesame_provider.js",
21
24
  "./guard": "./build/src/guard/main.js",
25
+ "./guard/lucid": "./build/src/guard/lucid.js",
22
26
  "./scope_middleware": "./build/src/middleware/scope_middleware.js",
23
27
  "./any_scope_middleware": "./build/src/middleware/any_scope_middleware.js",
24
28
  "./services/main": "./build/services/main.js",
@@ -29,6 +33,7 @@
29
33
  "check": "vp check",
30
34
  "format": "vp fmt --write .",
31
35
  "quick:test": "node --import=@poppinss/ts-exec --enable-source-maps bin/test.ts",
36
+ "test:sql": "bash bin/test_sql_dialects.sh",
32
37
  "test": "c8 npm run quick:test",
33
38
  "build": "vp pack && tsc --emitDeclarationOnly --declaration && adonis-kit index build/commands",
34
39
  "release": "release-it",
@@ -58,25 +63,32 @@
58
63
  "author": "Julien Ripouteau",
59
64
  "license": "MIT",
60
65
  "devDependencies": {
61
- "@adonisjs/assembler": "^8.0.0",
62
- "@adonisjs/auth": "^10.0.0",
63
- "@adonisjs/core": "^7.0.0",
64
- "@adonisjs/lucid": "^22.0.0",
66
+ "@adonisjs/assembler": "^8.5.2",
67
+ "@adonisjs/auth": "^10.1.0",
68
+ "@adonisjs/core": "^7.5.2",
69
+ "@adonisjs/lucid": "^22.4.2",
65
70
  "@adonisjs/tsconfig": "^2.0.0",
66
71
  "@japa/api-client": "^3.2.1",
67
72
  "@japa/assert": "^4.2.0",
68
73
  "@japa/expect-type": "^2.0.4",
69
74
  "@japa/runner": "^5.3.0",
70
75
  "@poppinss/ts-exec": "^1.4.4",
71
- "@release-it/conventional-changelog": "^10.0.5",
72
- "@types/luxon": "^3.7.1",
73
- "@types/node": "^25.3.3",
74
- "@vinejs/vine": "^4.3.0",
75
- "better-sqlite3": "^12.6.2",
76
- "c8": "^11.0.0",
76
+ "@release-it/conventional-changelog": "^12.0.2",
77
+ "@types/better-sqlite3": "^9.6.0",
78
+ "@types/luxon": "^3.7.5",
79
+ "@types/node": "^26.6.2",
80
+ "@types/pg": "^8.23.1",
81
+ "@vinejs/vine": "^4.4.0",
82
+ "better-sqlite3": "^13.0.3",
83
+ "c8": "^12.0.0",
77
84
  "cross-env": "^10.1.0",
78
- "release-it": "^19.2.4",
79
- "typescript": "^5.9.3",
85
+ "kysely": "^0.29.6",
86
+ "mysql2": "^3.24.4",
87
+ "node-gyp": "^13.0.2",
88
+ "pg": "^8.23.0",
89
+ "release-it": "^21.1.0",
90
+ "taze": "^21.1.0",
91
+ "typescript": "^7.0.2",
80
92
  "vite-plus": "catalog:"
81
93
  },
82
94
  "peerDependencies": {
@@ -84,13 +96,17 @@
84
96
  "@adonisjs/auth": "^10.0.0",
85
97
  "@adonisjs/core": "^7.0.0",
86
98
  "@adonisjs/lucid": "^21.0.0 || ^22.0.0",
87
- "@vinejs/vine": "^4.0.0"
99
+ "@vinejs/vine": "^4.0.0",
100
+ "kysely": "^0.29.0"
88
101
  },
89
102
  "peerDependenciesMeta": {
90
103
  "@adonisjs/assembler": {
91
104
  "optional": true
92
105
  },
93
- "@adonisjs/auth": {
106
+ "@adonisjs/lucid": {
107
+ "optional": true
108
+ },
109
+ "kysely": {
94
110
  "optional": true
95
111
  }
96
112
  },
@@ -131,7 +147,7 @@
131
147
  ]
132
148
  },
133
149
  "dependencies": {
134
- "jose": "^6.2.1",
150
+ "jose": "^6.2.12",
135
151
  "luxon": "^3.7.2"
136
152
  }
137
153
  }
@@ -1,9 +0,0 @@
1
- import "node:module";
2
- Object.create;
3
- Object.defineProperty;
4
- Object.getOwnPropertyDescriptor;
5
- Object.getOwnPropertyNames;
6
- Object.getPrototypeOf;
7
- Object.prototype.hasOwnProperty;
8
- //#endregion
9
- export {};
@@ -1,29 +0,0 @@
1
- import { n as __decorate, r as json } from "./oauth_client-BSanvSql.js";
2
- import { BaseModel, column } from "@adonisjs/lucid/orm";
3
- //#region src/models/oauth_access_token.ts
4
- /**
5
- * Database record for an issued OAuth 2.0 access token.
6
- *
7
- * Access tokens are opaque random values. Only the SHA-256 hash
8
- * is stored so raw tokens cannot be reconstructed from a DB leak.
9
- * A token is considered revoked when `revokedAt` is set.
10
- *
11
- * @see https://datatracker.ietf.org/doc/html/rfc7662
12
- */
13
- var OAuthAccessToken = class extends BaseModel {
14
- static table = "oauth_access_tokens";
15
- };
16
- __decorate([column({ isPrimary: true })], OAuthAccessToken.prototype, "id", void 0);
17
- __decorate([column()], OAuthAccessToken.prototype, "tokenHash", void 0);
18
- __decorate([column()], OAuthAccessToken.prototype, "clientId", void 0);
19
- __decorate([column()], OAuthAccessToken.prototype, "userId", void 0);
20
- __decorate([json()], OAuthAccessToken.prototype, "scopes", void 0);
21
- __decorate([column.dateTime()], OAuthAccessToken.prototype, "expiresAt", void 0);
22
- __decorate([column.dateTime()], OAuthAccessToken.prototype, "revokedAt", void 0);
23
- __decorate([column.dateTime({ autoCreate: true })], OAuthAccessToken.prototype, "createdAt", void 0);
24
- __decorate([column.dateTime({
25
- autoCreate: true,
26
- autoUpdate: true
27
- })], OAuthAccessToken.prototype, "updatedAt", void 0);
28
- //#endregion
29
- export { OAuthAccessToken as t };
@@ -1,63 +0,0 @@
1
- import { BaseModel, column } from "@adonisjs/lucid/orm";
2
- //#region src/decorators.ts
3
- /**
4
- * Column decorator for JSON values (arrays, objects).
5
- *
6
- * Lucid ORM doesn't handle JSON serialization automatically —
7
- * `prepare` stringifies before INSERT/UPDATE, `consume` parses
8
- * after SELECT. The typeof check in consume handles both drivers
9
- * that return strings (SQLite, MySQL) and parsed objects (PostgreSQL JSONB).
10
- */
11
- function json(options) {
12
- return column({
13
- ...options,
14
- prepare: (value) => value == null ? null : JSON.stringify(value),
15
- consume: (value) => value == null ? null : typeof value === "string" ? JSON.parse(value) : value
16
- });
17
- }
18
- //#endregion
19
- //#region \0@oxc-project+runtime@0.115.0/helpers/decorate.js
20
- function __decorate(decorators, target, key, desc) {
21
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
22
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
23
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
24
- return c > 3 && r && Object.defineProperty(target, key, r), r;
25
- }
26
- //#endregion
27
- //#region src/models/oauth_client.ts
28
- /**
29
- * Represents a registered OAuth 2.0 client (RFC 6749 §2).
30
- *
31
- * Clients can be either confidential (with a hashed secret) or
32
- * public (no secret, e.g. SPAs, native apps). Public clients must
33
- * use PKCE (RFC 7636) for authorization code exchanges.
34
- *
35
- * Clients may be created manually or via dynamic client registration
36
- * (RFC 7591) through the `/oauth/register` endpoint.
37
- *
38
- * @see https://datatracker.ietf.org/doc/html/rfc6749#section-2
39
- * @see https://datatracker.ietf.org/doc/html/rfc7591
40
- */
41
- var OAuthClient = class extends BaseModel {
42
- static table = "oauth_clients";
43
- };
44
- __decorate([column({ isPrimary: true })], OAuthClient.prototype, "id", void 0);
45
- __decorate([column()], OAuthClient.prototype, "clientId", void 0);
46
- __decorate([column({ serializeAs: null })], OAuthClient.prototype, "clientSecret", void 0);
47
- __decorate([column()], OAuthClient.prototype, "name", void 0);
48
- __decorate([json()], OAuthClient.prototype, "redirectUris", void 0);
49
- __decorate([json()], OAuthClient.prototype, "scopes", void 0);
50
- __decorate([json()], OAuthClient.prototype, "grantTypes", void 0);
51
- __decorate([column()], OAuthClient.prototype, "isPublic", void 0);
52
- __decorate([column()], OAuthClient.prototype, "isDisabled", void 0);
53
- __decorate([column()], OAuthClient.prototype, "requirePkce", void 0);
54
- __decorate([column()], OAuthClient.prototype, "type", void 0);
55
- __decorate([json()], OAuthClient.prototype, "metadata", void 0);
56
- __decorate([column()], OAuthClient.prototype, "userId", void 0);
57
- __decorate([column.dateTime({ autoCreate: true })], OAuthClient.prototype, "createdAt", void 0);
58
- __decorate([column.dateTime({
59
- autoCreate: true,
60
- autoUpdate: true
61
- })], OAuthClient.prototype, "updatedAt", void 0);
62
- //#endregion
63
- export { __decorate as n, json as r, OAuthClient as t };
@@ -1,6 +0,0 @@
1
- import "./chunk-DF48asd8.js";
2
- import { t as SesameManager } from "./sesame_manager-DYUSZ0NC.js";
3
- import "./oauth_client-BSanvSql.js";
4
- import "./oauth_error-C7UhDb2q.js";
5
- import "./oauth_access_token-Cz_5gNBx.js";
6
- export { SesameManager };