@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,8 +1,6 @@
1
- import "./chunk-DF48asd8.js";
2
- import { s as BUILTIN_SCOPES, t as SesameManager } from "./sesame_manager-DYUSZ0NC.js";
3
- import "./oauth_client-BSanvSql.js";
4
- import { l as E_SERVER_ERROR } from "./oauth_error-C7UhDb2q.js";
5
- import "./oauth_access_token-Cz_5gNBx.js";
1
+ import { t as BUILTIN_SCOPES } from "./types-DEtn2Zdb.js";
2
+ import { l as E_SERVER_ERROR } from "./oauth_error-CvxtZvjp.js";
3
+ import { t as SesameManager } from "./sesame_manager-CeEmlzba.js";
6
4
  //#region src/controllers/metadata_controller.ts
7
5
  const DISCOVERY_ROUTE_NAMES = {
8
6
  authorization_endpoint: "sesame.authorize",
@@ -113,7 +111,7 @@ var MetadataController = class {
113
111
  jwks_uri: router.makeUrl("sesame.jwks", {}, { prefixUrl }),
114
112
  subject_types_supported: ["public"],
115
113
  id_token_signing_alg_values_supported: ["RS256"],
116
- scopes_supported: [...new Set([
114
+ scopes_supported: [.../* @__PURE__ */ new Set([
117
115
  "openid",
118
116
  "profile",
119
117
  "email",
@@ -3,7 +3,7 @@ import type { ApplicationService } from '@adonisjs/core/types';
3
3
  * AdonisJS service provider for the Sésame OAuth 2.1 server.
4
4
  */
5
5
  export default class SesameProvider {
6
- protected app: ApplicationService;
6
+ #private;
7
7
  constructor(app: ApplicationService);
8
8
  /**
9
9
  * Register `SesameManager` as a singleton binding.
@@ -1,23 +1,23 @@
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";
1
+ import { t as SesameManager } from "../sesame_manager-CeEmlzba.js";
6
2
  //#region providers/sesame_provider.ts
7
3
  /**
8
4
  * AdonisJS service provider for the Sésame OAuth 2.1 server.
9
5
  */
10
6
  var SesameProvider = class {
7
+ #app;
11
8
  constructor(app) {
12
- this.app = app;
9
+ this.#app = app;
13
10
  }
14
11
  /**
15
12
  * Register `SesameManager` as a singleton binding.
16
13
  * The manager is resolved from the `sesame` config key.
17
14
  */
18
15
  register() {
19
- this.app.container.singleton(SesameManager, async () => {
20
- return new SesameManager(this.app.config.get("sesame"), await this.app.container.make("router"));
16
+ this.#app.container.singleton(SesameManager, async () => {
17
+ const config = this.#app.config.get("sesame");
18
+ const router = await this.#app.container.make("router");
19
+ const store = await config.store.resolver(this.#app);
20
+ return new SesameManager(config, router, store);
21
21
  });
22
22
  }
23
23
  };
@@ -1,8 +1,5 @@
1
- import "./chunk-DF48asd8.js";
2
- import { o as ClientService, t as SesameManager } from "./sesame_manager-DYUSZ0NC.js";
3
- import { t as OAuthClient } from "./oauth_client-BSanvSql.js";
4
- import { i as E_INVALID_CLIENT_METADATA, o as E_INVALID_REQUEST, s as E_INVALID_SCOPE, t as E_ACCESS_DENIED } from "./oauth_error-C7UhDb2q.js";
5
- import "./oauth_access_token-Cz_5gNBx.js";
1
+ import { i as E_INVALID_CLIENT_METADATA, o as E_INVALID_REQUEST, s as E_INVALID_SCOPE, t as E_ACCESS_DENIED } from "./oauth_error-CvxtZvjp.js";
2
+ import { n as ClientService, t as SesameManager } from "./sesame_manager-CeEmlzba.js";
6
3
  import vine from "@vinejs/vine";
7
4
  //#region src/rules.ts
8
5
  const DANGEROUS_SCHEMES = [
@@ -128,7 +125,7 @@ var RegisterController = class RegisterController {
128
125
  ...body.software_id ? { software_id: body.software_id } : {},
129
126
  ...body.software_version ? { software_version: body.software_version } : {}
130
127
  };
131
- await OAuthClient.create({
128
+ await manager.store.createClient({
132
129
  id: crypto.randomUUID(),
133
130
  clientId,
134
131
  clientSecret: hashedSecret,
@@ -1,9 +1,5 @@
1
- import "./chunk-DF48asd8.js";
2
- import { a as OAuthRefreshToken, o as ClientService, t as SesameManager } from "./sesame_manager-DYUSZ0NC.js";
3
- import "./oauth_client-BSanvSql.js";
4
- import "./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";
1
+ import { n as ClientService, t as SesameManager } from "./sesame_manager-CeEmlzba.js";
2
+ import { t as TokenService } from "./token_service-DDQ3Dxaj.js";
7
3
  import { DateTime } from "luxon";
8
4
  //#region src/controllers/revoke_controller.ts
9
5
  /**
@@ -21,7 +17,9 @@ import { DateTime } from "luxon";
21
17
  var RevokeController = class {
22
18
  async handle(ctx) {
23
19
  const manager = await ctx.containerResolver.make(SesameManager);
24
- const client = await new ClientService().authenticateClient({
20
+ const clientService = new ClientService(manager);
21
+ const store = manager.store;
22
+ const client = await clientService.authenticateClient({
25
23
  authorizationHeader: ctx.request.header("authorization"),
26
24
  bodyClientId: ctx.request.body().client_id,
27
25
  bodyClientSecret: ctx.request.body().client_secret
@@ -31,22 +29,18 @@ var RevokeController = class {
31
29
  const tokenTypeHint = ctx.request.body().token_type_hint;
32
30
  const hashed = new TokenService(manager).hashToken(token);
33
31
  if (!tokenTypeHint || tokenTypeHint === "access_token") {
34
- const record = await OAuthAccessToken.query().where("tokenHash", hashed).where("clientId", client.clientId).first();
35
- if (record && !record.revokedAt) {
36
- record.revokedAt = DateTime.now();
37
- await record.save();
38
- return ctx.response.ok({});
39
- }
32
+ if (await store.revokeAccessToken({
33
+ hash: hashed,
34
+ clientId: client.clientId,
35
+ now: DateTime.now()
36
+ })) return ctx.response.ok({});
40
37
  if (tokenTypeHint === "access_token") return ctx.response.ok({});
41
38
  }
42
- if (!tokenTypeHint || tokenTypeHint === "refresh_token") {
43
- const refreshToken = await OAuthRefreshToken.query().where("token", hashed).where("clientId", client.clientId).first();
44
- if (refreshToken && !refreshToken.revokedAt) {
45
- refreshToken.revokedAt = DateTime.now();
46
- await refreshToken.save();
47
- await OAuthAccessToken.query().where("id", refreshToken.accessTokenId).whereNull("revokedAt").update({ revokedAt: DateTime.now().toSQL() });
48
- }
49
- }
39
+ if (!tokenTypeHint || tokenTypeHint === "refresh_token") await store.revokeRefreshToken({
40
+ hash: hashed,
41
+ clientId: client.clientId,
42
+ now: DateTime.now()
43
+ });
50
44
  return ctx.response.ok({});
51
45
  }
52
46
  };
@@ -1,8 +1,4 @@
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";
1
+ import { t as SesameManager } from "../sesame_manager-CeEmlzba.js";
6
2
  import app from "@adonisjs/core/services/app";
7
3
  //#region services/main.ts
8
4
  let sesame;
@@ -1,63 +1,8 @@
1
- import { n as __decorate, r as json, t as OAuthClient } from "./oauth_client-BSanvSql.js";
2
- import { o as E_INVALID_REQUEST, r as E_INVALID_CLIENT, s as E_INVALID_SCOPE } from "./oauth_error-C7UhDb2q.js";
3
- import { t as OAuthAccessToken } from "./oauth_access_token-Cz_5gNBx.js";
1
+ import { n as OIDC_SCOPES, t as BUILTIN_SCOPES } from "./types-DEtn2Zdb.js";
2
+ import { o as E_INVALID_REQUEST, r as E_INVALID_CLIENT, s as E_INVALID_SCOPE } from "./oauth_error-CvxtZvjp.js";
4
3
  import { DateTime } from "luxon";
5
4
  import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
6
- import { BaseModel, column } from "@adonisjs/lucid/orm";
7
5
  import { SignJWT, importJWK } from "jose";
8
- //#region src/types.ts
9
- /**
10
- * Standard OAuth/OIDC scopes that are always valid regardless
11
- * of server or client scope configuration.
12
- *
13
- * These scopes are:
14
- * - Accepted during scope validation (client and server level)
15
- * - Advertised in `scopes_supported` of all metadata endpoints
16
- * (protected resource, OIDC discovery) so MCP clients know
17
- * they can request them
18
- *
19
- * - `offline_access`: signals that the client needs a refresh token
20
- * (OIDC Core §11). Note: Sesame issues refresh tokens by default
21
- * when the `refresh_token` grant is enabled, regardless of whether
22
- * the client requests this scope (per RFC 6749 §5.1). This scope
23
- * is still advertised for clients that check metadata before
24
- * building their authorization request.
25
- *
26
- * @see https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
27
- * @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.1
28
- */
29
- const BUILTIN_SCOPES = new Set(["offline_access"]);
30
- /**
31
- * OIDC-recognized scopes that are accepted by server-level validation
32
- * without needing to be declared in the config `scopes` map.
33
- *
34
- * Unlike `BUILTIN_SCOPES`, these still require explicit client authorization
35
- * via `ClientService.validateClientScopes()`.
36
- */
37
- const OIDC_SCOPES = new Set([
38
- "openid",
39
- "profile",
40
- "email"
41
- ]);
42
- /**
43
- * Protocol-managed claims that must never be overridden by `getOidcClaims()`.
44
- */
45
- const RESERVED_OIDC_CLAIMS = new Set([
46
- "sub",
47
- "iss",
48
- "aud",
49
- "exp",
50
- "iat",
51
- "nbf",
52
- "jti",
53
- "nonce",
54
- "at_hash",
55
- "auth_time",
56
- "acr",
57
- "azp",
58
- "sid"
59
- ]);
60
- //#endregion
61
6
  //#region src/services/client_service.ts
62
7
  /**
63
8
  * Handles OAuth client authentication and credential management.
@@ -73,6 +18,10 @@ const RESERVED_OIDC_CLAIMS = new Set([
73
18
  * @see https://datatracker.ietf.org/doc/html/rfc6749#section-2.3
74
19
  */
75
20
  var ClientService = class {
21
+ #manager;
22
+ constructor(manager) {
23
+ this.#manager = manager;
24
+ }
76
25
  /**
77
26
  * Parse an HTTP Basic Authorization header into client credentials.
78
27
  * Follows RFC 6749 §2.3.1 — the client_id and client_secret are
@@ -117,7 +66,8 @@ var ClientService = class {
117
66
  async authenticateClient(options) {
118
67
  const credentials = this.extractCredentials(options);
119
68
  if (!credentials) throw new E_INVALID_CLIENT("Client authentication failed");
120
- const client = await OAuthClient.query().where("clientId", credentials.clientId).first();
69
+ if (!this.#manager) throw new Error("ClientService requires SesameManager for authentication");
70
+ const client = await this.#manager.store.findClient(credentials.clientId);
121
71
  if (!client || client.isDisabled) throw new E_INVALID_CLIENT("Client authentication failed");
122
72
  if (!client.isPublic) {
123
73
  if (!credentials.clientSecret || !this.verifySecret(credentials.clientSecret, client.clientSecret)) throw new E_INVALID_CLIENT("Client authentication failed");
@@ -235,16 +185,16 @@ var KeyService = class KeyService {
235
185
  * their routes are hit.
236
186
  */
237
187
  const controllers = {
238
- token: () => import("./token_controller-DyI7oy-U.js"),
239
- authorize: () => import("./authorize_controller-BiycO4be.js"),
240
- consent: () => import("./consent_controller-Dsdhv6-f.js"),
241
- introspect: () => import("./introspect_controller-DvOp9scr.js"),
242
- revoke: () => import("./revoke_controller-z_ghrEB7.js"),
243
- register: () => import("./register_controller-gbq7p8a5.js"),
244
- metadata: () => import("./metadata_controller-BVsTo0Gp.js"),
245
- clientInfo: () => import("./client_info_controller-AcOG8lWu.js"),
246
- jwks: () => import("./jwks_controller-keo4kBZc.js"),
247
- userinfo: () => import("./userinfo_controller-RLk8cN_o.js")
188
+ token: () => import("./token_controller-DRatCQNT.js"),
189
+ authorize: () => import("./authorize_controller-sCZIjnc-.js"),
190
+ consent: () => import("./consent_controller-ChBboaSd.js"),
191
+ introspect: () => import("./introspect_controller-LVYGWhMj.js"),
192
+ revoke: () => import("./revoke_controller-Daz4Yqfs.js"),
193
+ register: () => import("./register_controller-Cdk3c32j.js"),
194
+ metadata: () => import("./metadata_controller-hwfmxlF7.js"),
195
+ clientInfo: () => import("./client_info_controller-B5A3tpbk.js"),
196
+ jwks: () => import("./jwks_controller-4HUVS_HC.js"),
197
+ userinfo: () => import("./userinfo_controller-YwN26wK-.js")
248
198
  };
249
199
  /**
250
200
  * Register OAuth 2.1 endpoint routes on the given router.
@@ -291,127 +241,6 @@ function registerWellKnownRoutes(router, options) {
291
241
  router.get(jwksPath, [controllers.jwks]).as("sesame.jwks");
292
242
  }
293
243
  //#endregion
294
- //#region src/models/oauth_refresh_token.ts
295
- /**
296
- * Database record for an OAuth 2.0 refresh token (RFC 6749 §6).
297
- *
298
- * The `token` column stores the SHA-256 hash of the raw refresh
299
- * token value. Refresh token rotation is enforced: each use
300
- * produces a new token and revokes the old one.
301
- *
302
- * Replay detection is implemented by checking `revokedAt` —
303
- * if a revoked token is presented, all tokens for that
304
- * client+user pair are deleted as a security measure.
305
- *
306
- * @see https://datatracker.ietf.org/doc/html/rfc6749#section-6
307
- * @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.14.2
308
- */
309
- var OAuthRefreshToken = class extends BaseModel {
310
- static table = "oauth_refresh_tokens";
311
- };
312
- __decorate([column({ isPrimary: true })], OAuthRefreshToken.prototype, "id", void 0);
313
- __decorate([column({ serializeAs: null })], OAuthRefreshToken.prototype, "token", void 0);
314
- __decorate([column()], OAuthRefreshToken.prototype, "accessTokenId", void 0);
315
- __decorate([column()], OAuthRefreshToken.prototype, "clientId", void 0);
316
- __decorate([column()], OAuthRefreshToken.prototype, "userId", void 0);
317
- __decorate([json()], OAuthRefreshToken.prototype, "scopes", void 0);
318
- __decorate([column.dateTime()], OAuthRefreshToken.prototype, "expiresAt", void 0);
319
- __decorate([column.dateTime()], OAuthRefreshToken.prototype, "revokedAt", void 0);
320
- __decorate([column.dateTime({ autoCreate: true })], OAuthRefreshToken.prototype, "createdAt", void 0);
321
- __decorate([column.dateTime({
322
- autoCreate: true,
323
- autoUpdate: true
324
- })], OAuthRefreshToken.prototype, "updatedAt", void 0);
325
- //#endregion
326
- //#region src/models/oauth_authorization_code.ts
327
- /**
328
- * Database record for an OAuth 2.0 authorization code (RFC 6749 §4.1.2).
329
- *
330
- * Authorization codes are short-lived, single-use tokens issued
331
- * during the authorization flow. The `code` column stores the
332
- * SHA-256 hash of the raw code value sent to the client.
333
- *
334
- * When PKCE (RFC 7636) is used, the `codeChallenge` and
335
- * `codeChallengeMethod` (always S256) are stored alongside the
336
- * code for verification at the token endpoint.
337
- *
338
- * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2
339
- * @see https://datatracker.ietf.org/doc/html/rfc7636
340
- */
341
- var OAuthAuthorizationCode = class extends BaseModel {
342
- static table = "oauth_authorization_codes";
343
- };
344
- __decorate([column({ isPrimary: true })], OAuthAuthorizationCode.prototype, "id", void 0);
345
- __decorate([column()], OAuthAuthorizationCode.prototype, "code", void 0);
346
- __decorate([column()], OAuthAuthorizationCode.prototype, "clientId", void 0);
347
- __decorate([column()], OAuthAuthorizationCode.prototype, "userId", void 0);
348
- __decorate([json()], OAuthAuthorizationCode.prototype, "scopes", void 0);
349
- __decorate([column()], OAuthAuthorizationCode.prototype, "redirectUri", void 0);
350
- __decorate([column()], OAuthAuthorizationCode.prototype, "codeChallenge", void 0);
351
- __decorate([column()], OAuthAuthorizationCode.prototype, "codeChallengeMethod", void 0);
352
- __decorate([column()], OAuthAuthorizationCode.prototype, "nonce", void 0);
353
- __decorate([column.dateTime()], OAuthAuthorizationCode.prototype, "expiresAt", void 0);
354
- __decorate([column.dateTime({ autoCreate: true })], OAuthAuthorizationCode.prototype, "createdAt", void 0);
355
- __decorate([column.dateTime({
356
- autoCreate: true,
357
- autoUpdate: true
358
- })], OAuthAuthorizationCode.prototype, "updatedAt", void 0);
359
- //#endregion
360
- //#region src/models/oauth_consent.ts
361
- /**
362
- * Tracks which scopes a user has approved for a given client.
363
- *
364
- * When a user grants consent during the authorization flow,
365
- * the approved scopes are persisted here. On subsequent
366
- * authorization requests, if the requested scopes are a subset
367
- * of previously approved scopes, the user is not prompted again.
368
- *
369
- * New scopes are merged into the existing record when the user
370
- * approves additional permissions.
371
- */
372
- var OAuthConsent = class extends BaseModel {
373
- static table = "oauth_consents";
374
- };
375
- __decorate([column({ isPrimary: true })], OAuthConsent.prototype, "id", void 0);
376
- __decorate([column()], OAuthConsent.prototype, "clientId", void 0);
377
- __decorate([column()], OAuthConsent.prototype, "userId", void 0);
378
- __decorate([json()], OAuthConsent.prototype, "scopes", void 0);
379
- __decorate([column.dateTime({ autoCreate: true })], OAuthConsent.prototype, "createdAt", void 0);
380
- __decorate([column.dateTime({
381
- autoCreate: true,
382
- autoUpdate: true
383
- })], OAuthConsent.prototype, "updatedAt", void 0);
384
- //#endregion
385
- //#region src/models/oauth_pending_authorization_request.ts
386
- /**
387
- * Temporary server-side record for an in-flight OAuth authorization
388
- * request, created when the user is redirected to the consent page
389
- * and consumed (deleted) when the user approves or denies.
390
- *
391
- * Stored in the database instead of the HTTP session to avoid
392
- * last-write-wins race conditions when concurrent SPA requests
393
- * overwrite session data.
394
- *
395
- * The `token` column stores a SHA-256 hash of the raw auth_token
396
- * sent to the consent page, consistent with how authorization
397
- * codes are stored.
398
- */
399
- var OAuthPendingAuthorizationRequest = class extends BaseModel {
400
- static table = "oauth_pending_authorization_requests";
401
- };
402
- __decorate([column({ isPrimary: true })], OAuthPendingAuthorizationRequest.prototype, "id", void 0);
403
- __decorate([column()], OAuthPendingAuthorizationRequest.prototype, "token", void 0);
404
- __decorate([column()], OAuthPendingAuthorizationRequest.prototype, "userId", void 0);
405
- __decorate([column()], OAuthPendingAuthorizationRequest.prototype, "clientId", void 0);
406
- __decorate([column()], OAuthPendingAuthorizationRequest.prototype, "redirectUri", void 0);
407
- __decorate([json()], OAuthPendingAuthorizationRequest.prototype, "scopes", void 0);
408
- __decorate([column()], OAuthPendingAuthorizationRequest.prototype, "state", void 0);
409
- __decorate([column()], OAuthPendingAuthorizationRequest.prototype, "codeChallenge", void 0);
410
- __decorate([column()], OAuthPendingAuthorizationRequest.prototype, "codeChallengeMethod", void 0);
411
- __decorate([column()], OAuthPendingAuthorizationRequest.prototype, "nonce", void 0);
412
- __decorate([column.dateTime()], OAuthPendingAuthorizationRequest.prototype, "expiresAt", void 0);
413
- __decorate([column.dateTime({ autoCreate: true })], OAuthPendingAuthorizationRequest.prototype, "createdAt", void 0);
414
- //#endregion
415
244
  //#region src/sesame_manager.ts
416
245
  /**
417
246
  * Central manager for the Sésame OAuth 2.1 server.
@@ -423,14 +252,28 @@ var SesameManager = class {
423
252
  #config;
424
253
  #router;
425
254
  #keyService;
426
- constructor(config, router) {
255
+ #store;
256
+ constructor(config, router, store) {
427
257
  this.#config = config;
428
258
  this.#router = router;
259
+ this.#store = store;
429
260
  this.#keyService = config.jwk ? new KeyService(config.jwk) : null;
430
261
  }
262
+ #publicClient(client) {
263
+ const publicClient = { ...client };
264
+ Object.defineProperty(publicClient, "clientSecret", {
265
+ value: client.clientSecret,
266
+ enumerable: false,
267
+ writable: false
268
+ });
269
+ return publicClient;
270
+ }
431
271
  get config() {
432
272
  return this.#config;
433
273
  }
274
+ get store() {
275
+ return this.#store;
276
+ }
434
277
  get keyService() {
435
278
  if (!this.#keyService) throw new Error("OIDC requires a JWK. Set the `jwk` option in defineConfig().");
436
279
  return this.#keyService;
@@ -496,11 +339,10 @@ var SesameManager = class {
496
339
  */
497
340
  async revokeAllForUser(userId) {
498
341
  const now = DateTime.now();
499
- await OAuthAccessToken.query().where("userId", userId).whereNull("revokedAt").update({ revokedAt: now.toSQL() });
500
- await OAuthRefreshToken.query().where("userId", userId).whereNull("revokedAt").update({ revokedAt: now.toSQL() });
501
- await OAuthAuthorizationCode.query().where("userId", userId).delete();
502
- await OAuthPendingAuthorizationRequest.query().where("userId", userId).delete();
503
- await OAuthConsent.query().where("userId", userId).delete();
342
+ await this.#store.revokeAllForUser({
343
+ userId,
344
+ now
345
+ });
504
346
  }
505
347
  /**
506
348
  * Purge revoked and/or expired tokens and authorization codes.
@@ -516,26 +358,12 @@ var SesameManager = class {
516
358
  const purgeRevoked = revokedOnly || !expiredOnly;
517
359
  const purgeExpired = expiredOnly || !revokedOnly;
518
360
  const cutoff = DateTime.now().minus({ hours: retentionHours });
519
- let accessTokens = 0;
520
- let refreshTokens = 0;
521
- let authorizationCodes = 0;
522
- let pendingRequests = 0;
523
- if (purgeRevoked) {
524
- accessTokens += await this.#deleteCount(OAuthAccessToken.query().whereNotNull("revokedAt").delete());
525
- refreshTokens += await this.#deleteCount(OAuthRefreshToken.query().whereNotNull("revokedAt").delete());
526
- }
527
- if (purgeExpired) {
528
- accessTokens += await this.#deleteCount(OAuthAccessToken.query().where("expiresAt", "<", cutoff.toSQL()).whereNull("revokedAt").delete());
529
- refreshTokens += await this.#deleteCount(OAuthRefreshToken.query().where("expiresAt", "<", cutoff.toSQL()).whereNull("revokedAt").delete());
530
- authorizationCodes += await this.#deleteCount(OAuthAuthorizationCode.query().where("expiresAt", "<", cutoff.toSQL()).delete());
531
- }
532
- pendingRequests += await this.#deleteCount(OAuthPendingAuthorizationRequest.query().where("expiresAt", "<", DateTime.now().toSQL()).delete());
533
- return {
534
- accessTokens,
535
- refreshTokens,
536
- authorizationCodes,
537
- pendingRequests
538
- };
361
+ return this.#store.purgeTokens({
362
+ purgeRevoked,
363
+ purgeExpired,
364
+ cutoff,
365
+ now: DateTime.now()
366
+ });
539
367
  }
540
368
  /**
541
369
  * Create a new OAuth client programmatically.
@@ -549,22 +377,23 @@ var SesameManager = class {
549
377
  const clientId = clientService.generateClientId();
550
378
  const clientSecret = isPublic ? null : clientService.generateClientSecret();
551
379
  const hashedSecret = clientSecret ? clientService.hashSecret(clientSecret) : null;
380
+ const client = await this.#store.createClient({
381
+ id: crypto.randomUUID(),
382
+ clientId,
383
+ clientSecret: hashedSecret,
384
+ name: options.name,
385
+ redirectUris: options.redirectUris,
386
+ scopes,
387
+ grantTypes,
388
+ isPublic,
389
+ isDisabled: false,
390
+ requirePkce: options.requirePkce ?? true,
391
+ type: isPublic ? "public" : "confidential",
392
+ metadata: options.metadata ?? null,
393
+ userId: options.userId ?? null
394
+ });
552
395
  return {
553
- client: await OAuthClient.create({
554
- id: crypto.randomUUID(),
555
- clientId,
556
- clientSecret: hashedSecret,
557
- name: options.name,
558
- redirectUris: options.redirectUris,
559
- scopes,
560
- grantTypes,
561
- isPublic,
562
- isDisabled: false,
563
- requirePkce: options.requirePkce ?? true,
564
- type: isPublic ? "public" : "confidential",
565
- metadata: options.metadata ?? null,
566
- userId: options.userId ?? null
567
- }),
396
+ client: this.#publicClient(client),
568
397
  clientSecret
569
398
  };
570
399
  }
@@ -572,22 +401,22 @@ var SesameManager = class {
572
401
  * Find a client by its public client_id.
573
402
  */
574
403
  async findClient(clientId) {
575
- return OAuthClient.query().where("clientId", clientId).first();
404
+ const client = await this.#store.findClient(clientId);
405
+ return client ? this.#publicClient(client) : null;
576
406
  }
577
407
  /**
578
408
  * List all clients, optionally filtered by userId.
579
409
  */
580
410
  async listClients(options) {
581
- const query = OAuthClient.query().orderBy("createdAt", "desc");
582
- if (options?.userId) query.where("userId", options.userId);
583
- return query;
411
+ return (await this.#store.listClients(options)).map((client) => this.#publicClient(client));
584
412
  }
585
413
  /**
586
414
  * Update an existing client by its public client_id.
587
415
  * Returns the updated client, or null if not found.
588
416
  */
589
417
  async updateClient(clientId, options) {
590
- const client = await OAuthClient.query().where("clientId", clientId).first();
418
+ const store = this.#store;
419
+ const client = await store.findClient(clientId);
591
420
  if (!client) return null;
592
421
  if (options.name !== void 0) client.name = options.name;
593
422
  if (options.redirectUris !== void 0) client.redirectUris = options.redirectUris;
@@ -596,39 +425,35 @@ var SesameManager = class {
596
425
  if (options.isDisabled !== void 0) client.isDisabled = options.isDisabled;
597
426
  if (options.requirePkce !== void 0) client.requirePkce = options.requirePkce;
598
427
  if (options.metadata !== void 0) client.metadata = options.metadata;
599
- await client.save();
600
- return client;
428
+ await store.updateClient({
429
+ id: client.id,
430
+ data: options
431
+ });
432
+ const updated = await store.findClient(clientId);
433
+ return updated ? this.#publicClient(updated) : null;
601
434
  }
602
435
  /**
603
436
  * Delete a client and all its associated tokens, codes, and consents.
604
437
  * Returns true if the client was found and deleted.
605
438
  */
606
439
  async deleteClient(clientId) {
607
- const client = await OAuthClient.query().where("clientId", clientId).first();
608
- if (!client) return false;
609
- await OAuthClient.transaction(async (trx) => {
610
- await Promise.all([
611
- OAuthRefreshToken.query({ client: trx }).where("clientId", clientId).delete(),
612
- OAuthAccessToken.query({ client: trx }).where("clientId", clientId).delete(),
613
- OAuthAuthorizationCode.query({ client: trx }).where("clientId", clientId).delete(),
614
- OAuthPendingAuthorizationRequest.query({ client: trx }).where("clientId", clientId).delete(),
615
- OAuthConsent.query({ client: trx }).where("clientId", clientId).delete()
616
- ]);
617
- await client.useTransaction(trx).delete();
618
- });
619
- return true;
440
+ return this.#store.deleteClient(clientId);
620
441
  }
621
442
  /**
622
443
  * Rotate the secret of a confidential client.
623
444
  * Returns the new raw secret, or null if the client is public or not found.
624
445
  */
625
446
  async rotateClientSecret(clientId) {
626
- const client = await OAuthClient.query().where("clientId", clientId).first();
447
+ const store = this.#store;
448
+ const client = await store.findClient(clientId);
627
449
  if (!client || client.isPublic) return null;
628
450
  const clientService = new ClientService();
629
451
  const newSecret = clientService.generateClientSecret();
630
452
  client.clientSecret = clientService.hashSecret(newSecret);
631
- await client.save();
453
+ await store.updateClientSecret({
454
+ id: client.id,
455
+ secret: client.clientSecret
456
+ });
632
457
  return newSecret;
633
458
  }
634
459
  /**
@@ -685,9 +510,6 @@ var SesameManager = class {
685
510
  };
686
511
  });
687
512
  }
688
- #deleteCount(result) {
689
- return result.then((r) => Array.isArray(r) ? Number(r[0] ?? 0) : Number(r));
690
- }
691
513
  };
692
514
  //#endregion
693
- export { OAuthRefreshToken as a, OIDC_SCOPES as c, OAuthAuthorizationCode as i, RESERVED_OIDC_CLAIMS as l, OAuthPendingAuthorizationRequest as n, ClientService as o, OAuthConsent as r, BUILTIN_SCOPES as s, SesameManager as t };
515
+ export { ClientService as n, SesameManager as t };
@@ -0,0 +1,2 @@
1
+ import { t as SesameManager } from "./sesame_manager-CeEmlzba.js";
2
+ export { SesameManager };
@@ -1,7 +1,7 @@
1
1
  import type { SesameManager } from '../sesame_manager.ts';
2
- import type { OAuthClient } from '../models/oauth_client.ts';
2
+ import type { OAuthClientRecord } from '../storage/types.ts';
3
3
  export interface ExchangeAuthorizationCodeInput {
4
- client: OAuthClient;
4
+ client: OAuthClientRecord;
5
5
  code: string;
6
6
  redirectUri: string;
7
7
  codeVerifier: string;
@@ -24,11 +24,11 @@ export declare class ExchangeAuthorizationCodeAction {
24
24
  * consumed atomically inside a transaction.
25
25
  */
26
26
  execute(manager: SesameManager, input: ExchangeAuthorizationCodeInput): Promise<{
27
- id_token?: string | undefined;
28
- refresh_token?: string | undefined;
29
27
  access_token: string;
30
- token_type: "Bearer";
28
+ token_type: 'Bearer';
31
29
  expires_in: number;
32
30
  scope: string;
31
+ refresh_token?: string | undefined;
32
+ id_token?: string | undefined;
33
33
  }>;
34
34
  }
@@ -1,7 +1,7 @@
1
1
  import type { SesameManager } from '../sesame_manager.ts';
2
- import type { OAuthClient } from '../models/oauth_client.ts';
2
+ import type { OAuthClientRecord } from '../storage/types.ts';
3
3
  export interface ExchangeClientCredentialsInput {
4
- client: OAuthClient;
4
+ client: OAuthClientRecord;
5
5
  scope?: string;
6
6
  }
7
7
  /**
@@ -21,7 +21,7 @@ export declare class ExchangeClientCredentialsAction {
21
21
  */
22
22
  execute(manager: SesameManager, input: ExchangeClientCredentialsInput): Promise<{
23
23
  access_token: string;
24
- token_type: "Bearer";
24
+ token_type: 'Bearer';
25
25
  expires_in: number;
26
26
  scope: string;
27
27
  }>;