@julr/sesame 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +470 -64
  3. package/build/authorize_controller-sCZIjnc-.js +251 -0
  4. package/build/{client_info_controller-BucHGx4u.js → client_info_controller-B5A3tpbk.js} +11 -4
  5. package/build/commands/sesame_client.d.ts +20 -0
  6. package/build/commands/sesame_key.d.ts +12 -0
  7. package/build/commands/sesame_purge.d.ts +0 -2
  8. package/build/commands/sesame_purge.js +12 -3
  9. package/build/configure-t9q3KonY.js +31 -0
  10. package/build/configure.js +1 -24
  11. package/build/consent_controller-ChBboaSd.js +96 -0
  12. package/build/consent_retry-DMkfhmk9.js +41 -0
  13. package/build/{decorate-BKZEjPRg.js → decorate-RQD1h28J.js} +3 -9
  14. package/build/id_token_service-BtBUiL_n.js +54 -0
  15. package/build/index.d.ts +2 -8
  16. package/build/index.js +59 -10
  17. package/build/{introspect_controller-6bRt9sZt.js → introspect_controller-LVYGWhMj.js} +28 -10
  18. package/build/issue_authorization_code-Dr4-bbMp.js +39 -0
  19. package/build/jwks_controller-4HUVS_HC.js +22 -0
  20. package/build/{main-EbeMS5S9.js → main-Dx9kZv07.js} +35 -43
  21. package/build/{metadata_controller-DeaMRnUr.js → metadata_controller-hwfmxlF7.js} +81 -6
  22. package/build/oauth_error-CvxtZvjp.js +189 -0
  23. package/build/providers/sesame_provider.d.ts +1 -1
  24. package/build/providers/sesame_provider.js +17 -6
  25. package/build/{register_controller-sIJ1rxdM.js → register_controller-Cdk3c32j.js} +44 -8
  26. package/build/revoke_controller-Daz4Yqfs.js +48 -0
  27. package/build/services/main.js +3 -3
  28. package/build/sesame_manager-CeEmlzba.js +515 -0
  29. package/build/sesame_manager-DQxvsTEq.js +2 -0
  30. package/build/src/actions/authorize.d.ts +46 -0
  31. package/build/src/actions/exchange_authorization_code.d.ts +34 -0
  32. package/build/src/actions/exchange_client_credentials.d.ts +28 -0
  33. package/build/src/actions/exchange_refresh_token.d.ts +59 -0
  34. package/build/src/actions/issue_authorization_code.d.ts +26 -0
  35. package/build/src/controllers/authorize_controller.d.ts +18 -17
  36. package/build/src/controllers/consent_controller.d.ts +5 -0
  37. package/build/src/controllers/jwks_controller.d.ts +14 -0
  38. package/build/src/controllers/metadata_controller.d.ts +10 -3
  39. package/build/src/controllers/register_controller.d.ts +30 -30
  40. package/build/src/controllers/token_controller.d.ts +8 -5
  41. package/build/src/controllers/userinfo_controller.d.ts +14 -0
  42. package/build/src/guard/guard.d.ts +1 -1
  43. package/build/src/guard/lucid.d.ts +8 -0
  44. package/build/src/guard/lucid.js +46 -0
  45. package/build/src/guard/main.d.ts +2 -9
  46. package/build/src/guard/main.js +2 -6
  47. package/build/src/guard/types.d.ts +0 -9
  48. package/build/src/guard/user_provider.d.ts +9 -1
  49. package/build/src/middleware/any_scope_middleware.js +10 -1
  50. package/build/src/middleware/scope_middleware.js +10 -1
  51. package/build/src/models/oauth_authorization_code.d.ts +1 -0
  52. package/build/src/models/oauth_pending_authorization_request.d.ts +1 -0
  53. package/build/src/oauth_error.d.ts +74 -74
  54. package/build/src/routes.d.ts +3 -1
  55. package/build/src/services/client_service.d.ts +5 -2
  56. package/build/src/services/id_token_service.d.ts +30 -0
  57. package/build/src/services/key_service.d.ts +20 -0
  58. package/build/src/sesame_manager.d.ts +56 -4
  59. package/build/src/storage/consent_retry.d.ts +4 -0
  60. package/build/src/storage/drivers/kysely.d.ts +166 -0
  61. package/build/src/storage/drivers/kysely.js +480 -0
  62. package/build/src/storage/drivers/lucid.d.ts +162 -0
  63. package/build/src/storage/drivers/lucid.js +549 -0
  64. package/build/src/storage/migrations/kysely.d.ts +6 -0
  65. package/build/src/storage/types.d.ts +197 -0
  66. package/build/src/storage/types.js +1 -0
  67. package/build/src/stores.d.ts +21 -0
  68. package/build/src/types.d.ts +120 -0
  69. package/build/src/types.js +2 -0
  70. package/build/stubs/config/sesame.stub +4 -1
  71. package/build/stubs/config/sesame_kysely.stub +35 -0
  72. package/build/stubs/main.ts +5 -0
  73. package/build/stubs/migrations/create_oauth_authorization_codes_table.stub +1 -0
  74. package/build/stubs/migrations/create_oauth_pending_authorization_requests_table.stub +1 -0
  75. package/build/stubs/migrations/create_oauth_refresh_tokens_table.stub +1 -1
  76. package/build/stubs/migrations/kysely/create_oauth_tables.stub +151 -0
  77. package/build/taze.config.d.ts +2 -0
  78. package/build/token_controller-DRatCQNT.js +492 -0
  79. package/build/token_service-DDQ3Dxaj.js +59 -0
  80. package/build/types-DEtn2Zdb.js +75 -0
  81. package/build/userinfo_controller-YwN26wK-.js +37 -0
  82. package/build/vite.config.d.ts +2 -0
  83. package/package.json +55 -54
  84. package/build/authorize_controller-YUfAy-R2.js +0 -138
  85. package/build/client_service-WTNMqWzY.js +0 -65
  86. package/build/consent_controller-Dprwd1ed.js +0 -85
  87. package/build/oauth_access_token-bsoM5KeU.js +0 -18
  88. package/build/oauth_client-BIoY5jBR.js +0 -24
  89. package/build/oauth_error-CnJ3L8tf.js +0 -94
  90. package/build/revoke_controller-D6isoQCi.js +0 -41
  91. package/build/sesame_manager-Bu4MHqZV.js +0 -4
  92. package/build/sesame_manager-DwDZy5Vy.js +0 -167
  93. package/build/src/grants/authorization_code_grant.d.ts +0 -23
  94. package/build/src/grants/client_credentials_grant.d.ts +0 -23
  95. package/build/src/grants/refresh_token_grant.d.ts +0 -27
  96. package/build/token_controller-DzcrLMyS.js +0 -194
  97. package/build/token_service-fhoA4slP.js +0 -31
package/build/index.js CHANGED
@@ -1,13 +1,27 @@
1
- import { configure } from "./configure.js";
2
- import { a as OAuthRefreshToken, i as OAuthAuthorizationCode, r as OAuthConsent, t as SesameManager } from "./sesame_manager-DwDZy5Vy.js";
3
- import "./decorate-BKZEjPRg.js";
4
- import { t as OAuthAccessToken } from "./oauth_access_token-bsoM5KeU.js";
5
- import { a as E_INVALID_GRANT, c as E_INVALID_TOKEN, d as E_UNSUPPORTED_RESPONSE_TYPE, f as OAuthError, i as E_INVALID_CLIENT_METADATA, l as E_SERVER_ERROR, n as E_INSUFFICIENT_SCOPE, o as E_INVALID_REQUEST, r as E_INVALID_CLIENT, s as E_INVALID_SCOPE, t as E_ACCESS_DENIED, u as E_UNSUPPORTED_GRANT_TYPE } from "./oauth_error-CnJ3L8tf.js";
6
- import { t as OAuthClient } from "./oauth_client-BIoY5jBR.js";
7
- import "./token_service-fhoA4slP.js";
8
- import { i as OAuthGuard, n as oauthUserProvider, r as OAuthLucidUserProvider, t as oauthGuard } from "./main-EbeMS5S9.js";
1
+ import { t as configure } from "./configure-t9q3KonY.js";
2
+ import { t as SesameManager } from "./sesame_manager-CeEmlzba.js";
3
+ import { n as OAuthGuard, t as oauthGuard } from "./main-Dx9kZv07.js";
4
+ import { InvalidArgumentsException } from "@adonisjs/core/exceptions";
5
+ import { configProvider } from "@adonisjs/core";
6
+ //#region src/define_config.ts
7
+ /**
8
+ * Resolve user-supplied `SesameConfig` into a `ResolvedSesameConfig`
9
+ * by applying sensible defaults for all optional fields.
10
+ *
11
+ * Defaults:
12
+ * - `scopes`: `{}`
13
+ * - `defaultScopes`: `[]`
14
+ * - `grantTypes`: `['authorization_code', 'refresh_token']`
15
+ * - `accessTokenTtl`: `'1h'`
16
+ * - `refreshTokenTtl`: `'30d'`
17
+ * - `authorizationCodeTtl`: `'10m'`
18
+ * - `allowDynamicRegistration`: `false`
19
+ * - `allowPublicRegistration`: `false`
20
+ */
9
21
  function defineConfig(config) {
22
+ if (!config.store) throw new InvalidArgumentsException("Missing \"store\" in Sesame config");
10
23
  return {
24
+ store: config.store,
11
25
  issuer: config.issuer,
12
26
  scopes: config.scopes ?? {},
13
27
  defaultScopes: config.defaultScopes ?? [],
@@ -15,12 +29,47 @@ function defineConfig(config) {
15
29
  accessTokenTtl: config.accessTokenTtl ?? "1h",
16
30
  clientCredentialsAccessTokenTtl: config.clientCredentialsAccessTokenTtl ?? config.accessTokenTtl ?? "1h",
17
31
  refreshTokenTtl: config.refreshTokenTtl ?? "30d",
32
+ refreshTokenRotationGracePeriod: config.refreshTokenRotationGracePeriod ?? 120,
18
33
  authorizationCodeTtl: config.authorizationCodeTtl ?? "10m",
19
34
  authorizationRequestTtl: config.authorizationRequestTtl ?? config.authorizationCodeTtl ?? "10m",
20
35
  loginPage: config.loginPage,
21
36
  consentPage: config.consentPage,
22
37
  allowDynamicRegistration: config.allowDynamicRegistration ?? false,
23
- allowPublicRegistration: config.allowPublicRegistration ?? false
38
+ allowPublicRegistration: config.allowPublicRegistration ?? false,
39
+ jwk: config.jwk,
40
+ oidcProvider: config.oidcProvider,
41
+ idTokenTtl: config.idTokenTtl ?? "1h"
24
42
  };
25
43
  }
26
- export { E_ACCESS_DENIED, E_INSUFFICIENT_SCOPE, E_INVALID_CLIENT, E_INVALID_CLIENT_METADATA, E_INVALID_GRANT, E_INVALID_REQUEST, E_INVALID_SCOPE, E_INVALID_TOKEN, E_SERVER_ERROR, E_UNSUPPORTED_GRANT_TYPE, E_UNSUPPORTED_RESPONSE_TYPE, OAuthAccessToken, OAuthAuthorizationCode, OAuthClient, OAuthConsent, OAuthError, OAuthGuard, OAuthLucidUserProvider, OAuthRefreshToken, SesameManager, configure, defineConfig, oauthGuard, oauthUserProvider };
44
+ //#endregion
45
+ //#region src/stores.ts
46
+ /**
47
+ * Store driver factories resolved by the AdonisJS service provider.
48
+ */
49
+ const stores = {
50
+ /**
51
+ * Use the application's Lucid connection for OAuth persistence.
52
+ */
53
+ lucid() {
54
+ return configProvider.create(async (app) => {
55
+ await app.container.make("lucid.db");
56
+ const { lucidStore } = await import("./src/storage/drivers/lucid.js");
57
+ return lucidStore();
58
+ });
59
+ },
60
+ /**
61
+ * Use an existing Kysely connection for OAuth persistence.
62
+ */
63
+ kysely(options) {
64
+ return configProvider.create(async (app) => {
65
+ const connection = typeof options.connection === "function" ? await options.connection(app) : options.connection;
66
+ const { kyselyStore } = await import("./src/storage/drivers/kysely.js");
67
+ return kyselyStore({
68
+ db: connection,
69
+ dialect: options.dialect
70
+ });
71
+ });
72
+ }
73
+ };
74
+ //#endregion
75
+ export { OAuthGuard, SesameManager, configure, defineConfig, oauthGuard, stores };
@@ -1,15 +1,26 @@
1
- import { a as OAuthRefreshToken, t as SesameManager } from "./sesame_manager-DwDZy5Vy.js";
2
- import "./decorate-BKZEjPRg.js";
3
- import { t as OAuthAccessToken } from "./oauth_access_token-bsoM5KeU.js";
4
- import "./oauth_error-CnJ3L8tf.js";
5
- import "./oauth_client-BIoY5jBR.js";
6
- import { t as TokenService } from "./token_service-fhoA4slP.js";
7
- import { t as ClientService } from "./client_service-WTNMqWzY.js";
1
+ import { n as ClientService, t as SesameManager } from "./sesame_manager-CeEmlzba.js";
2
+ import { t as TokenService } from "./token_service-DDQ3Dxaj.js";
3
+ //#region src/controllers/introspect_controller.ts
8
4
  const INACTIVE = { active: false };
5
+ /**
6
+ * Handles the OAuth 2.0 Token Introspection Endpoint (RFC 7662).
7
+ *
8
+ * Allows an authenticated client to determine the active state and
9
+ * metadata of a token (access token or refresh token). The response
10
+ * always includes at least `{ active: boolean }`.
11
+ *
12
+ * Supports the `token_type_hint` parameter to optimize lookup order.
13
+ * Both access tokens and refresh tokens are opaque values looked up
14
+ * by their SHA-256 hash in the database.
15
+ *
16
+ * @see https://datatracker.ietf.org/doc/html/rfc7662
17
+ */
9
18
  var IntrospectController = class {
10
19
  async handle(ctx) {
11
20
  const manager = await ctx.containerResolver.make(SesameManager);
12
- const client = await new ClientService().authenticateClient({
21
+ const clientService = new ClientService(manager);
22
+ const store = manager.store;
23
+ const client = await clientService.authenticateClient({
13
24
  authorizationHeader: ctx.request.header("authorization"),
14
25
  bodyClientId: ctx.request.body().client_id,
15
26
  bodyClientSecret: ctx.request.body().client_secret
@@ -19,7 +30,10 @@ var IntrospectController = class {
19
30
  const tokenTypeHint = ctx.request.body().token_type_hint;
20
31
  const hashed = new TokenService(manager).hashToken(token);
21
32
  if (!tokenTypeHint || tokenTypeHint === "access_token") {
22
- const record = await OAuthAccessToken.query().where("tokenHash", hashed).where("clientId", client.clientId).first();
33
+ const record = await store.findAccessToken({
34
+ hash: hashed,
35
+ clientId: client.clientId
36
+ });
23
37
  if (record && !record.revokedAt && record.expiresAt.toJSDate() >= /* @__PURE__ */ new Date()) return {
24
38
  active: true,
25
39
  token_type: "Bearer",
@@ -33,7 +47,10 @@ var IntrospectController = class {
33
47
  if (tokenTypeHint === "access_token") return INACTIVE;
34
48
  }
35
49
  if (!tokenTypeHint || tokenTypeHint === "refresh_token") {
36
- const refreshToken = await OAuthRefreshToken.query().where("token", hashed).where("clientId", client.clientId).first();
50
+ const refreshToken = await store.findRefreshToken({
51
+ hash: hashed,
52
+ clientId: client.clientId
53
+ });
37
54
  if (!refreshToken) return INACTIVE;
38
55
  if (refreshToken.revokedAt) return INACTIVE;
39
56
  if (refreshToken.expiresAt.toJSDate() < /* @__PURE__ */ new Date()) return INACTIVE;
@@ -51,4 +68,5 @@ var IntrospectController = class {
51
68
  return INACTIVE;
52
69
  }
53
70
  };
71
+ //#endregion
54
72
  export { IntrospectController as default };
@@ -0,0 +1,39 @@
1
+ import { t as TokenService } from "./token_service-DDQ3Dxaj.js";
2
+ import { DateTime } from "luxon";
3
+ import string from "@adonisjs/core/helpers/string";
4
+ //#region src/actions/issue_authorization_code.ts
5
+ /**
6
+ * Create and persist an authorization code in the database.
7
+ * Returns the raw (unhashed) code to be sent to the client.
8
+ *
9
+ * Shared between the authorize and consent flows to avoid
10
+ * duplicating the code-issuance logic.
11
+ */
12
+ var IssueAuthorizationCodeAction = class {
13
+ /**
14
+ * Generate an opaque authorization code, store its SHA-256
15
+ * hash in the database, and return the raw value for the
16
+ * client redirect.
17
+ */
18
+ async execute(manager, input) {
19
+ const tokenService = new TokenService(manager);
20
+ const raw = tokenService.generateOpaqueToken();
21
+ const hashed = tokenService.hashToken(raw);
22
+ const ttl = string.seconds.parse(manager.config.authorizationCodeTtl);
23
+ await manager.store.createAuthorizationCode({
24
+ id: crypto.randomUUID(),
25
+ code: hashed,
26
+ clientId: input.client.clientId,
27
+ userId: input.userId,
28
+ scopes: input.scopes,
29
+ redirectUri: input.redirectUri,
30
+ codeChallenge: input.codeChallenge ?? null,
31
+ codeChallengeMethod: input.codeChallengeMethod ?? null,
32
+ nonce: input.nonce ?? null,
33
+ expiresAt: DateTime.now().plus({ seconds: ttl })
34
+ });
35
+ return raw;
36
+ }
37
+ };
38
+ //#endregion
39
+ export { IssueAuthorizationCodeAction as t };
@@ -0,0 +1,22 @@
1
+ import { t as SesameManager } from "./sesame_manager-CeEmlzba.js";
2
+ //#region src/controllers/jwks_controller.ts
3
+ /**
4
+ * Serves the JSON Web Key Set (JWKS) containing public keys
5
+ * used to verify ID token signatures.
6
+ *
7
+ * @see https://datatracker.ietf.org/doc/html/rfc7517
8
+ */
9
+ var JwksController = class {
10
+ async handle(ctx) {
11
+ const manager = await ctx.containerResolver.make(SesameManager);
12
+ if (!manager.isOidcEnabled) {
13
+ ctx.response.status(404);
14
+ return { error: "OIDC is not configured" };
15
+ }
16
+ ctx.response.header("Cache-Control", "public, max-age=900");
17
+ ctx.response.header("Content-Type", "application/jwk-set+json");
18
+ return manager.keyService.getPublicJwks();
19
+ }
20
+ };
21
+ //#endregion
22
+ export { JwksController as default };
@@ -1,9 +1,14 @@
1
- import { t as OAuthAccessToken } from "./oauth_access_token-bsoM5KeU.js";
2
- import { t as OAuthClient } from "./oauth_client-BIoY5jBR.js";
3
- import { t as TokenService } from "./token_service-fhoA4slP.js";
1
+ import { t as TokenService } from "./token_service-DDQ3Dxaj.js";
4
2
  import { DateTime } from "luxon";
5
- import { RuntimeException } from "@adonisjs/core/exceptions";
6
3
  import { errors } from "@adonisjs/auth";
4
+ //#region src/guard/guard.ts
5
+ /**
6
+ * OAuth 2.0 guard for `@adonisjs/auth`.
7
+ *
8
+ * Verifies opaque Bearer tokens against the database,
9
+ * checks revocation and expiry, loads the real User model
10
+ * via the provider, and exposes OAuth-specific data (scopes, clientId).
11
+ */
7
12
  var OAuthGuard = class {
8
13
  driverName = "oauth";
9
14
  authenticationAttempted = false;
@@ -58,7 +63,7 @@ var OAuthGuard = class {
58
63
  const tokenService = new TokenService(this.#manager);
59
64
  const includeError = { includeError: true };
60
65
  const hashed = tokenService.hashToken(rawToken);
61
- const record = await OAuthAccessToken.query().where("tokenHash", hashed).first();
66
+ const record = await this.#manager.store.findAccessToken({ hash: hashed });
62
67
  if (!record) throw this.#authenticationFailed("Invalid or expired token", includeError);
63
68
  if (record.revokedAt) throw this.#authenticationFailed("Invalid or expired token", includeError);
64
69
  if (record.expiresAt.toJSDate() < /* @__PURE__ */ new Date()) throw this.#authenticationFailed("Invalid or expired token", includeError);
@@ -91,22 +96,36 @@ var OAuthGuard = class {
91
96
  hasAnyScope(...scopes) {
92
97
  return scopes.some((s) => this.scopes.includes(s));
93
98
  }
99
+ /**
100
+ * Used internally by Japa's `loginAs` helper during testing.
101
+ * Creates a test client and access token in DB, then returns
102
+ * the authorization headers for the test HTTP client to use.
103
+ *
104
+ * @see https://docs.adonisjs.com/guides/auth/custom-auth-guard#implementing-the-guard
105
+ */
94
106
  async authenticateAsClient(user) {
95
107
  const tokenService = new TokenService(this.#manager);
96
108
  const defaultScopes = this.#manager.config.defaultScopes;
97
- const testClient = await OAuthClient.firstOrCreate({ clientId: "__test_client__" }, {
109
+ const store = this.#manager.store;
110
+ let testClient = await store.findClient("__test_client__");
111
+ if (!testClient) testClient = await store.createClient({
98
112
  id: crypto.randomUUID(),
99
113
  clientId: "__test_client__",
114
+ clientSecret: null,
100
115
  name: "Test Client",
101
116
  redirectUris: ["http://localhost/callback"],
102
117
  grantTypes: ["authorization_code"],
103
118
  scopes: defaultScopes,
104
119
  isPublic: true,
105
- requirePkce: false
120
+ isDisabled: false,
121
+ requirePkce: false,
122
+ type: "public",
123
+ metadata: null,
124
+ userId: null
106
125
  });
107
126
  const userId = String(user.id ?? user.getId?.() ?? "test-user");
108
127
  const { raw, hash, expiresAt } = tokenService.createAccessToken();
109
- await OAuthAccessToken.create({
128
+ await store.createAccessToken({
110
129
  id: crypto.randomUUID(),
111
130
  tokenHash: hash,
112
131
  clientId: testClient.clientId,
@@ -117,45 +136,18 @@ var OAuthGuard = class {
117
136
  return { headers: { authorization: `Bearer ${raw}` } };
118
137
  }
119
138
  };
120
- var OAuthLucidUserProvider = class {
121
- #model;
122
- #options;
123
- constructor(options) {
124
- this.#options = options;
125
- }
126
- async #getModel() {
127
- if (this.#model && !("hot" in import.meta)) return this.#model;
128
- this.#model = (await this.#options.model()).default;
129
- return this.#model;
130
- }
131
- async createUserForGuard(user) {
132
- const model = await this.#getModel();
133
- if (user instanceof model === false) throw new RuntimeException(`Invalid user object. It must be an instance of the "${model.name}" model`);
134
- return {
135
- getId() {
136
- if (!user.$primaryKeyValue) throw new RuntimeException(`Cannot use "${model.name}" model for authentication. The value of column "${model.primaryKey}" is undefined or null`);
137
- return user.$primaryKeyValue;
138
- },
139
- getOriginal() {
140
- return user;
141
- }
142
- };
143
- }
144
- async findById(identifier) {
145
- const user = await (await this.#getModel()).find(identifier);
146
- if (!user) return null;
147
- return this.createUserForGuard(user);
148
- }
149
- };
139
+ //#endregion
140
+ //#region src/guard/main.ts
141
+ /**
142
+ * Configure the OAuth guard for `@adonisjs/auth`.
143
+ */
150
144
  function oauthGuard(config) {
151
145
  return { async resolver(name, app) {
152
146
  const emitter = await app.container.make("emitter");
153
- const { SesameManager } = await import("./sesame_manager-Bu4MHqZV.js");
147
+ const { SesameManager } = await import("./sesame_manager-DQxvsTEq.js");
154
148
  const manager = await app.container.make(SesameManager);
155
149
  return (ctx) => new OAuthGuard(name, ctx, emitter, config.provider, manager, config.resource);
156
150
  } };
157
151
  }
158
- function oauthUserProvider(options) {
159
- return new OAuthLucidUserProvider(options);
160
- }
161
- export { OAuthGuard as i, oauthUserProvider as n, OAuthLucidUserProvider as r, oauthGuard as t };
152
+ //#endregion
153
+ export { OAuthGuard as n, oauthGuard as t };
@@ -1,7 +1,7 @@
1
- import { o as BUILTIN_SCOPES, t as SesameManager } from "./sesame_manager-DwDZy5Vy.js";
2
- import "./decorate-BKZEjPRg.js";
3
- import "./oauth_access_token-bsoM5KeU.js";
4
- import { l as E_SERVER_ERROR } from "./oauth_error-CnJ3L8tf.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";
4
+ //#region src/controllers/metadata_controller.ts
5
5
  const DISCOVERY_ROUTE_NAMES = {
6
6
  authorization_endpoint: "sesame.authorize",
7
7
  token_endpoint: "sesame.token",
@@ -9,6 +9,22 @@ const DISCOVERY_ROUTE_NAMES = {
9
9
  introspection_endpoint: "sesame.introspect",
10
10
  revocation_endpoint: "sesame.revoke"
11
11
  };
12
+ const OIDC_DISCOVERY_ROUTE_NAMES = {
13
+ userinfo_endpoint: "sesame.userinfo",
14
+ jwks_uri: "sesame.jwks"
15
+ };
16
+ /**
17
+ * Serves OAuth 2.0 discovery metadata documents.
18
+ *
19
+ * Exposes three well-known endpoints:
20
+ * - `authServer()` — OAuth Authorization Server Metadata (RFC 8414)
21
+ * - `oidc()` — OpenID Connect Discovery 1.0 (extends authServer metadata)
22
+ * - `protectedResource()` — OAuth Protected Resource Metadata (RFC 9728)
23
+ *
24
+ * @see https://datatracker.ietf.org/doc/html/rfc8414
25
+ * @see https://datatracker.ietf.org/doc/html/rfc9728
26
+ * @see https://openid.net/specs/openid-connect-discovery-1_0.html
27
+ */
12
28
  var MetadataController = class {
13
29
  #assertDiscoveryRoutes(router, options) {
14
30
  const requiredRoutes = [
@@ -21,6 +37,19 @@ var MetadataController = class {
21
37
  const missingRoutes = requiredRoutes.filter((routeName) => !router.has(routeName));
22
38
  if (missingRoutes.length > 0) throw new E_SERVER_ERROR(`OAuth discovery is misconfigured. Missing named route(s): ${missingRoutes.join(", ")}. Register OAuth routes with sesame.registerRoutes(router) before exposing well-known metadata.`);
23
39
  }
40
+ #assertOidcDiscoveryRoutes(router) {
41
+ const missingRoutes = Object.values(OIDC_DISCOVERY_ROUTE_NAMES).filter((routeName) => !router.has(routeName));
42
+ if (missingRoutes.length > 0) throw new E_SERVER_ERROR(`OIDC discovery is misconfigured. Missing named route(s): ${missingRoutes.join(", ")}. Register OAuth routes with sesame.registerRoutes(router) and sesame.registerWellKnownRoutes(router) before exposing OpenID discovery.`);
43
+ }
44
+ /**
45
+ * OAuth 2.0 Authorization Server Metadata (RFC 8414).
46
+ *
47
+ * Returns a JSON document describing the server's endpoints,
48
+ * supported grant types, response types, authentication methods,
49
+ * and PKCE support.
50
+ *
51
+ * @see https://datatracker.ietf.org/doc/html/rfc8414#section-2
52
+ */
24
53
  async authServer(ctx) {
25
54
  const manager = await ctx.containerResolver.make(SesameManager);
26
55
  const router = await ctx.containerResolver.make("router");
@@ -57,15 +86,60 @@ var MetadataController = class {
57
86
  authorization_response_iss_parameter_supported: true
58
87
  };
59
88
  }
89
+ /**
90
+ * OpenID Connect Discovery 1.0 metadata.
91
+ *
92
+ * Extends the authorization server metadata with OIDC-specific
93
+ * fields like `subject_types_supported` and
94
+ * `id_token_signing_alg_values_supported`.
95
+ *
96
+ * @see https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
97
+ */
60
98
  async oidc(ctx) {
61
- const base = await this.authServer(ctx);
62
99
  const manager = await ctx.containerResolver.make(SesameManager);
100
+ if (!manager.isOidcEnabled) {
101
+ ctx.response.status(404);
102
+ return { error: "OIDC is not configured" };
103
+ }
104
+ const base = await this.authServer(ctx);
105
+ const router = await ctx.containerResolver.make("router");
106
+ const prefixUrl = manager.config.issuer;
107
+ this.#assertOidcDiscoveryRoutes(router);
63
108
  return {
64
109
  ...base,
110
+ userinfo_endpoint: router.makeUrl("sesame.userinfo", {}, { prefixUrl }),
111
+ jwks_uri: router.makeUrl("sesame.jwks", {}, { prefixUrl }),
65
112
  subject_types_supported: ["public"],
66
- scopes_supported: [...Object.keys(manager.config.scopes), ...BUILTIN_SCOPES]
113
+ id_token_signing_alg_values_supported: ["RS256"],
114
+ scopes_supported: [.../* @__PURE__ */ new Set([
115
+ "openid",
116
+ "profile",
117
+ "email",
118
+ ...Object.keys(manager.config.scopes),
119
+ ...BUILTIN_SCOPES
120
+ ])],
121
+ claims_supported: [
122
+ "sub",
123
+ "iss",
124
+ "aud",
125
+ "exp",
126
+ "iat",
127
+ "nonce",
128
+ "at_hash"
129
+ ],
130
+ response_types_supported: ["code"]
67
131
  };
68
132
  }
133
+ /**
134
+ * OAuth 2.0 Protected Resource Metadata (RFC 9728).
135
+ *
136
+ * Returns a JSON document that tells clients which authorization
137
+ * servers protect this resource, which scopes are available, and
138
+ * how to present bearer tokens. Used by MCP clients to discover
139
+ * the authorization server.
140
+ *
141
+ * @see https://datatracker.ietf.org/doc/html/rfc9728
142
+ */
69
143
  async protectedResource(ctx) {
70
144
  const manager = await ctx.containerResolver.make(SesameManager);
71
145
  const issuer = manager.config.issuer;
@@ -78,4 +152,5 @@ var MetadataController = class {
78
152
  };
79
153
  }
80
154
  };
155
+ //#endregion
81
156
  export { MetadataController as default };
@@ -0,0 +1,189 @@
1
+ import { Exception } from "@adonisjs/core/exceptions";
2
+ //#region src/oauth_error.ts
3
+ /**
4
+ * OAuth 2.0 error classes following the error codes defined in
5
+ * RFC 6749 §5.2 (https://datatracker.ietf.org/doc/html/rfc6749#section-5.2)
6
+ * and RFC 7591 §3.2.2 (https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.2).
7
+ *
8
+ * Each error class extends AdonisJS Exception and self-renders via
9
+ * the `handle()` method, producing a standard OAuth JSON error response.
10
+ */
11
+ /**
12
+ * Base class for all OAuth errors. Extends AdonisJS Exception
13
+ * and renders the standard OAuth error response format:
14
+ * `{ error: "<oauth_code>", error_description: "<message>" }`.
15
+ *
16
+ * Subclasses declare a static `oauthCode` matching the `error` field
17
+ * defined by the OAuth 2.0 spec (e.g. `invalid_request`, `invalid_client`).
18
+ */
19
+ var OAuthError = class extends Exception {
20
+ static oauthCode;
21
+ get oauthCode() {
22
+ return this.constructor.oauthCode;
23
+ }
24
+ handle(error, ctx) {
25
+ ctx.response.status(error.status).json({
26
+ error: error.oauthCode,
27
+ error_description: error.message
28
+ });
29
+ }
30
+ };
31
+ /**
32
+ * The request is missing a required parameter, includes an unsupported
33
+ * parameter value, repeats a parameter, or is otherwise malformed.
34
+ *
35
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
36
+ */
37
+ const E_INVALID_REQUEST = class extends OAuthError {
38
+ static status = 400;
39
+ static code = "E_INVALID_REQUEST";
40
+ static message = "Invalid request";
41
+ static oauthCode = "invalid_request";
42
+ };
43
+ /**
44
+ * Client authentication failed (e.g. unknown client, no client
45
+ * authentication included, or unsupported authentication method).
46
+ *
47
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
48
+ */
49
+ const E_INVALID_CLIENT = class extends OAuthError {
50
+ static status = 401;
51
+ static code = "E_INVALID_CLIENT";
52
+ static message = "Invalid client";
53
+ static oauthCode = "invalid_client";
54
+ /**
55
+ * If the client attempted to authenticate via the Authorization header
56
+ * (Basic auth), the server MUST include WWW-Authenticate: Basic.
57
+ *
58
+ * @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-12#section-3.2.4
59
+ */
60
+ handle(error, ctx) {
61
+ if (ctx.request.header("authorization")?.startsWith("Basic ")) ctx.response.header("WWW-Authenticate", "Basic");
62
+ super.handle(error, ctx);
63
+ }
64
+ };
65
+ /**
66
+ * The provided authorization grant (authorization code, refresh token,
67
+ * resource owner credentials) is invalid, expired, revoked, or does
68
+ * not match the redirection URI used in the authorization request.
69
+ *
70
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
71
+ */
72
+ const E_INVALID_GRANT = class extends OAuthError {
73
+ static status = 400;
74
+ static code = "E_INVALID_GRANT";
75
+ static message = "Invalid grant";
76
+ static oauthCode = "invalid_grant";
77
+ };
78
+ /**
79
+ * The requested scope is invalid, unknown, malformed, or exceeds
80
+ * the scope granted by the resource owner.
81
+ *
82
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
83
+ */
84
+ const E_INVALID_SCOPE = class extends OAuthError {
85
+ static status = 400;
86
+ static code = "E_INVALID_SCOPE";
87
+ static message = "Invalid scope";
88
+ static oauthCode = "invalid_scope";
89
+ };
90
+ /**
91
+ * The access token provided is expired, revoked, malformed,
92
+ * or invalid for other reasons.
93
+ *
94
+ * @see https://datatracker.ietf.org/doc/html/rfc6750#section-3.1
95
+ */
96
+ const E_INVALID_TOKEN = class extends OAuthError {
97
+ static status = 401;
98
+ static code = "E_INVALID_TOKEN";
99
+ static message = "Invalid token";
100
+ static oauthCode = "invalid_token";
101
+ handle(error, ctx) {
102
+ ctx.response.header("WWW-Authenticate", `Bearer error="invalid_token", error_description="${error.message}"`);
103
+ super.handle(error, ctx);
104
+ }
105
+ };
106
+ /**
107
+ * The authorization grant type is not supported by the
108
+ * authorization server.
109
+ *
110
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
111
+ */
112
+ const E_UNSUPPORTED_GRANT_TYPE = class extends OAuthError {
113
+ static status = 400;
114
+ static code = "E_UNSUPPORTED_GRANT_TYPE";
115
+ static message = "Unsupported grant type";
116
+ static oauthCode = "unsupported_grant_type";
117
+ };
118
+ /**
119
+ * The authorization server does not support obtaining an
120
+ * authorization code using this response type.
121
+ *
122
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1
123
+ */
124
+ const E_UNSUPPORTED_RESPONSE_TYPE = class extends OAuthError {
125
+ static status = 400;
126
+ static code = "E_UNSUPPORTED_RESPONSE_TYPE";
127
+ static message = "Unsupported response type";
128
+ static oauthCode = "unsupported_response_type";
129
+ };
130
+ /**
131
+ * The resource owner or authorization server denied the request.
132
+ *
133
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1
134
+ */
135
+ const E_ACCESS_DENIED = class extends OAuthError {
136
+ static status = 403;
137
+ static code = "E_ACCESS_DENIED";
138
+ static message = "Access denied";
139
+ static oauthCode = "access_denied";
140
+ };
141
+ /**
142
+ * The client metadata value is invalid, as defined in the dynamic
143
+ * client registration protocol.
144
+ *
145
+ * @see https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.2
146
+ */
147
+ const E_INVALID_CLIENT_METADATA = class extends OAuthError {
148
+ static status = 400;
149
+ static code = "E_INVALID_CLIENT_METADATA";
150
+ static message = "Invalid client metadata";
151
+ static oauthCode = "invalid_client_metadata";
152
+ };
153
+ /**
154
+ * The authorization server encountered an unexpected condition
155
+ * that prevented it from fulfilling the request.
156
+ *
157
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1
158
+ */
159
+ const E_SERVER_ERROR = class extends OAuthError {
160
+ static status = 500;
161
+ static code = "E_SERVER_ERROR";
162
+ static message = "Server error";
163
+ static oauthCode = "server_error";
164
+ };
165
+ /**
166
+ * The access token does not have the required scope(s) to access
167
+ * the protected resource. Returns 403 with a WWW-Authenticate header
168
+ * per RFC 6750 §3.1.
169
+ *
170
+ * @see https://datatracker.ietf.org/doc/html/rfc6750#section-3.1
171
+ */
172
+ const E_INSUFFICIENT_SCOPE = class extends OAuthError {
173
+ static status = 403;
174
+ static code = "E_INSUFFICIENT_SCOPE";
175
+ static message = "Insufficient scope";
176
+ static oauthCode = "insufficient_scope";
177
+ missingScopes;
178
+ constructor(missingScopes, message) {
179
+ super(message ?? "The token does not have the required scope(s)");
180
+ this.missingScopes = missingScopes;
181
+ }
182
+ handle(error, ctx) {
183
+ const scope = error.missingScopes.join(" ");
184
+ ctx.response.header("WWW-Authenticate", `Bearer error="insufficient_scope", error_description="${error.message}", scope="${scope}"`);
185
+ super.handle(error, ctx);
186
+ }
187
+ };
188
+ //#endregion
189
+ export { E_INVALID_GRANT as a, E_INVALID_TOKEN as c, E_UNSUPPORTED_RESPONSE_TYPE as d, E_INVALID_CLIENT_METADATA as i, E_SERVER_ERROR as l, E_INSUFFICIENT_SCOPE as n, E_INVALID_REQUEST as o, E_INVALID_CLIENT as r, E_INVALID_SCOPE as s, E_ACCESS_DENIED as t, E_UNSUPPORTED_GRANT_TYPE as u };
@@ -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.