@julr/sesame 0.5.0 → 0.6.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 (74) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +405 -62
  3. package/build/authorize_controller-BiycO4be.js +251 -0
  4. package/build/chunk-DF48asd8.js +9 -0
  5. package/build/{client_info_controller-BucHGx4u.js → client_info_controller-AcOG8lWu.js} +11 -3
  6. package/build/commands/sesame_client.d.ts +20 -0
  7. package/build/commands/sesame_key.d.ts +12 -0
  8. package/build/commands/sesame_purge.d.ts +0 -2
  9. package/build/commands/sesame_purge.js +15 -3
  10. package/build/configure-DkDkIlt8.js +27 -0
  11. package/build/configure.js +2 -24
  12. package/build/consent_controller-Dsdhv6-f.js +108 -0
  13. package/build/id_token_service-CpTzOUDe.js +54 -0
  14. package/build/index.d.ts +1 -1
  15. package/build/index.js +30 -10
  16. package/build/{introspect_controller-un95fs4y.js → introspect_controller-DvOp9scr.js} +21 -7
  17. package/build/issue_authorization_code-B9ERu1uO.js +40 -0
  18. package/build/jwks_controller-keo4kBZc.js +26 -0
  19. package/build/{main-B3M6ihoS.js → main-DGBJhq3E.js} +34 -4
  20. package/build/metadata_controller-BVsTo0Gp.js +158 -0
  21. package/build/{oauth_access_token-bsoM5KeU.js → oauth_access_token-Cz_5gNBx.js} +12 -1
  22. package/build/oauth_client-BSanvSql.js +63 -0
  23. package/build/oauth_error-C7UhDb2q.js +189 -0
  24. package/build/providers/sesame_provider.js +14 -3
  25. package/build/{register_controller-Dch4ecyD.js → register_controller-gbq7p8a5.js} +46 -7
  26. package/build/{revoke_controller-DnPmzYMd.js → revoke_controller-z_ghrEB7.js} +21 -8
  27. package/build/services/main.js +7 -3
  28. package/build/sesame_manager-B1Jgq1v2.js +6 -0
  29. package/build/sesame_manager-DYUSZ0NC.js +693 -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 +13 -12
  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 +9 -2
  39. package/build/src/controllers/token_controller.d.ts +8 -5
  40. package/build/src/controllers/userinfo_controller.d.ts +14 -0
  41. package/build/src/guard/main.js +5 -5
  42. package/build/src/middleware/any_scope_middleware.js +11 -1
  43. package/build/src/middleware/scope_middleware.js +11 -1
  44. package/build/src/models/oauth_authorization_code.d.ts +1 -0
  45. package/build/src/models/oauth_pending_authorization_request.d.ts +1 -0
  46. package/build/src/oauth_error.d.ts +1 -1
  47. package/build/src/routes.d.ts +3 -1
  48. package/build/src/services/id_token_service.d.ts +30 -0
  49. package/build/src/services/key_service.d.ts +20 -0
  50. package/build/src/sesame_manager.d.ts +54 -3
  51. package/build/src/types.d.ts +124 -3
  52. package/build/stubs/main.ts +5 -0
  53. package/build/stubs/migrations/create_oauth_authorization_codes_table.stub +1 -0
  54. package/build/stubs/migrations/create_oauth_pending_authorization_requests_table.stub +1 -0
  55. package/build/stubs/migrations/create_oauth_refresh_tokens_table.stub +1 -1
  56. package/build/token_controller-DyI7oy-U.js +481 -0
  57. package/build/token_service-DwnfAR9F.js +59 -0
  58. package/build/userinfo_controller-RLk8cN_o.js +40 -0
  59. package/build/vite.config.d.ts +2 -0
  60. package/package.json +26 -41
  61. package/build/authorize_controller-BGzxPvYU.js +0 -138
  62. package/build/client_service-C3rfXGk_.js +0 -65
  63. package/build/consent_controller-BHoB9mip.js +0 -85
  64. package/build/decorate-BKZEjPRg.js +0 -15
  65. package/build/metadata_controller-CJeZG93_.js +0 -81
  66. package/build/oauth_client-BIoY5jBR.js +0 -24
  67. package/build/oauth_error-CnJ3L8tf.js +0 -94
  68. package/build/sesame_manager-BQIW2mqt.js +0 -4
  69. package/build/sesame_manager-C-eEFFHM.js +0 -167
  70. package/build/src/grants/authorization_code_grant.d.ts +0 -27
  71. package/build/src/grants/client_credentials_grant.d.ts +0 -23
  72. package/build/src/grants/refresh_token_grant.d.ts +0 -27
  73. package/build/token_controller-hGDAYuBS.js +0 -194
  74. package/build/token_service-fhoA4slP.js +0 -31
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@julr/sesame",
3
3
  "description": "OAuth 2.1 + OIDC server for AdonisJS",
4
- "version": "0.5.0",
4
+ "version": "0.6.0",
5
5
  "engines": {
6
6
  "node": ">=24.0.0"
7
7
  },
@@ -15,6 +15,7 @@
15
15
  "main": "build/index.js",
16
16
  "exports": {
17
17
  ".": "./build/index.js",
18
+ "./exceptions": "./build/src/oauth_error.js",
18
19
  "./types": "./build/src/types.js",
19
20
  "./sesame_provider": "./build/providers/sesame_provider.js",
20
21
  "./guard": "./build/src/guard/main.js",
@@ -25,21 +26,15 @@
25
26
  "./commands": "./build/commands/main.js"
26
27
  },
27
28
  "scripts": {
28
- "copy:templates": "copyfiles \"stubs/**/*.stub\" build",
29
- "typecheck": "tsc --noEmit",
30
- "lint": "oxlint .",
31
- "format": "oxfmt --write .",
29
+ "check": "vp check",
30
+ "format": "vp fmt --write .",
32
31
  "quick:test": "node --import=@poppinss/ts-exec --enable-source-maps bin/test.ts",
33
- "pretest": "npm run lint",
34
32
  "test": "c8 npm run quick:test",
35
- "precompile": "npm run lint",
36
- "index:commands": "adonis-kit index build/commands",
37
- "compile": "tsdown && tsc --emitDeclarationOnly --declaration",
38
- "postcompile": "npm run copy:templates && npm run index:commands",
39
- "build": "npm run compile",
33
+ "build": "vp pack && tsc --emitDeclarationOnly --declaration && adonis-kit index build/commands",
40
34
  "release": "release-it",
41
35
  "version": "npm run build",
42
- "prepublishOnly": "npm run build"
36
+ "prepublishOnly": "npm run build",
37
+ "prepare": "vp config"
43
38
  },
44
39
  "repository": {
45
40
  "type": "git",
@@ -49,7 +44,17 @@
49
44
  "url": "https://github.com/Julien-R44/sesame/issues"
50
45
  },
51
46
  "homepage": "https://github.com/Julien-R44/sesame#readme",
52
- "keywords": [],
47
+ "keywords": [
48
+ "adonisjs",
49
+ "adonis",
50
+ "oauth",
51
+ "oauth2.1",
52
+ "oidc",
53
+ "openid-connect",
54
+ "authorization-server",
55
+ "mcp",
56
+ "pkce"
57
+ ],
53
58
  "author": "Julien Ripouteau",
54
59
  "license": "MIT",
55
60
  "devDependencies": {
@@ -57,8 +62,8 @@
57
62
  "@adonisjs/auth": "^10.0.0",
58
63
  "@adonisjs/core": "^7.0.0",
59
64
  "@adonisjs/lucid": "^22.0.0",
60
- "@adonisjs/prettier-config": "^1.4.5",
61
65
  "@adonisjs/tsconfig": "^2.0.0",
66
+ "@japa/api-client": "^3.2.1",
62
67
  "@japa/assert": "^4.2.0",
63
68
  "@japa/expect-type": "^2.0.4",
64
69
  "@japa/runner": "^5.3.0",
@@ -69,50 +74,29 @@
69
74
  "@vinejs/vine": "^4.3.0",
70
75
  "better-sqlite3": "^12.6.2",
71
76
  "c8": "^11.0.0",
72
- "copyfiles": "^2.4.1",
73
77
  "cross-env": "^10.1.0",
74
- "oxfmt": "^0.36.0",
75
- "oxlint": "^1.51.0",
76
78
  "release-it": "^19.2.4",
77
- "tsdown": "^0.20.3",
78
- "typescript": "^5.9.3"
79
+ "typescript": "^5.9.3",
80
+ "vite-plus": "catalog:"
79
81
  },
80
82
  "peerDependencies": {
81
83
  "@adonisjs/assembler": "^8.0.0",
82
84
  "@adonisjs/auth": "^10.0.0",
83
85
  "@adonisjs/core": "^7.0.0",
84
- "@adonisjs/lucid": "^21.0.0",
86
+ "@adonisjs/lucid": "^21.0.0 || ^22.0.0",
85
87
  "@vinejs/vine": "^4.0.0"
86
88
  },
87
89
  "peerDependenciesMeta": {
88
90
  "@adonisjs/assembler": {
89
91
  "optional": true
92
+ },
93
+ "@adonisjs/auth": {
94
+ "optional": true
90
95
  }
91
96
  },
92
97
  "publishConfig": {
93
98
  "access": "public"
94
99
  },
95
- "tsdown": {
96
- "entry": [
97
- "./index.ts",
98
- "./configure.ts",
99
- "./providers/sesame_provider.ts",
100
- "./services/main.ts",
101
- "./src/guard/main.ts",
102
- "./commands/sesame_purge.ts",
103
- "./src/middleware/scope_middleware.ts",
104
- "./src/middleware/any_scope_middleware.ts"
105
- ],
106
- "outDir": "./build",
107
- "clean": true,
108
- "format": "esm",
109
- "minify": "dce-only",
110
- "fixedExtension": false,
111
- "dts": false,
112
- "treeshake": false,
113
- "sourcemaps": false,
114
- "target": "esnext"
115
- },
116
100
  "release-it": {
117
101
  "git": {
118
102
  "requireCleanWorkingDir": true,
@@ -147,6 +131,7 @@
147
131
  ]
148
132
  },
149
133
  "dependencies": {
134
+ "jose": "^6.2.1",
150
135
  "luxon": "^3.7.2"
151
136
  }
152
137
  }
@@ -1,138 +0,0 @@
1
- import { i as OAuthAuthorizationCode, n as OAuthPendingAuthorizationRequest, r as OAuthConsent, t as SesameManager } from "./sesame_manager-C-eEFFHM.js";
2
- import "./decorate-BKZEjPRg.js";
3
- import "./oauth_access_token-bsoM5KeU.js";
4
- import { d as E_UNSUPPORTED_RESPONSE_TYPE, o as E_INVALID_REQUEST, r as E_INVALID_CLIENT } from "./oauth_error-CnJ3L8tf.js";
5
- import { t as OAuthClient } from "./oauth_client-BIoY5jBR.js";
6
- import { t as TokenService } from "./token_service-fhoA4slP.js";
7
- import { t as ClientService } from "./client_service-C3rfXGk_.js";
8
- import { DateTime } from "luxon";
9
- import string from "@adonisjs/core/helpers/string";
10
- import vine from "@vinejs/vine";
11
- var AuthorizeController = class AuthorizeController {
12
- static validator = vine.create({
13
- client_id: vine.string(),
14
- response_type: vine.string(),
15
- redirect_uri: vine.string(),
16
- scope: vine.string().optional(),
17
- state: vine.string().optional(),
18
- code_challenge: vine.string().optional(),
19
- code_challenge_method: vine.string().optional()
20
- });
21
- #redirectWithError(ctx, manager, redirectUri, error, description, state) {
22
- const url = new URL(redirectUri);
23
- url.searchParams.set("error", error);
24
- url.searchParams.set("error_description", description);
25
- if (state) url.searchParams.set("state", state);
26
- url.searchParams.set("iss", manager.config.issuer);
27
- return ctx.response.redirect().toPath(url.toString());
28
- }
29
- async #buildAuthorizationRequestParams(manager, options) {
30
- const tokenService = new TokenService(manager);
31
- const rawRequestToken = tokenService.generateOpaqueToken();
32
- const ttl = string.seconds.parse(manager.config.authorizationRequestTtl);
33
- await OAuthPendingAuthorizationRequest.create({
34
- id: crypto.randomUUID(),
35
- token: tokenService.hashToken(rawRequestToken),
36
- userId: options.userId,
37
- clientId: options.clientId,
38
- redirectUri: options.redirectUri,
39
- scopes: options.scopes,
40
- state: options.state ?? null,
41
- codeChallenge: options.codeChallenge ?? null,
42
- codeChallengeMethod: options.codeChallengeMethod ?? null,
43
- expiresAt: DateTime.now().plus({ seconds: ttl })
44
- });
45
- const params = new URLSearchParams();
46
- params.set("auth_token", rawRequestToken);
47
- return params;
48
- }
49
- #copyAuthorizeDisplayParams(params, query) {
50
- for (const [key, value] of Object.entries(query)) {
51
- if (key === "auth_token") continue;
52
- if (value != null) params.set(key, String(value));
53
- }
54
- }
55
- #resolvePageUrl(page, ctx, params) {
56
- if (typeof page === "function") return page(ctx, params);
57
- return `${page}?${params.toString()}`;
58
- }
59
- async #issueAuthorizationCode(ctx, manager, options) {
60
- const tokenService = new TokenService(manager);
61
- const raw = tokenService.generateOpaqueToken();
62
- const hashed = tokenService.hashToken(raw);
63
- const ttl = string.seconds.parse(manager.config.authorizationCodeTtl);
64
- await OAuthAuthorizationCode.create({
65
- id: crypto.randomUUID(),
66
- code: hashed,
67
- clientId: options.client.clientId,
68
- userId: options.userId,
69
- scopes: options.scopes,
70
- redirectUri: options.redirectUri,
71
- codeChallenge: options.codeChallenge ?? null,
72
- codeChallengeMethod: options.codeChallengeMethod ?? null,
73
- expiresAt: DateTime.now().plus({ seconds: ttl })
74
- });
75
- const url = new URL(options.redirectUri);
76
- url.searchParams.set("code", raw);
77
- if (options.state) url.searchParams.set("state", options.state);
78
- url.searchParams.set("iss", manager.config.issuer);
79
- return ctx.response.redirect().toPath(url.toString());
80
- }
81
- async handle(ctx) {
82
- const manager = await ctx.containerResolver.make(SesameManager);
83
- const clientService = new ClientService();
84
- const [error, query] = await AuthorizeController.validator.tryValidate(ctx.request.qs());
85
- if (error) throw new E_INVALID_REQUEST("Invalid authorization request parameters");
86
- if (query.response_type !== "code") throw new E_UNSUPPORTED_RESPONSE_TYPE("Only \"code\" is supported");
87
- const client = await OAuthClient.query().where("clientId", query.client_id).first();
88
- if (!client) throw new E_INVALID_CLIENT("Client not found");
89
- if (client.isDisabled) throw new E_INVALID_CLIENT("Client is disabled");
90
- if (!client.redirectUris.includes(query.redirect_uri)) throw new E_INVALID_REQUEST("Invalid redirect_uri");
91
- if (!client.grantTypes.includes("authorization_code")) return this.#redirectWithError(ctx, manager, query.redirect_uri, "unauthorized_client", "Client is not allowed to use the authorization_code grant", query.state);
92
- const requestedScopes = query.scope ? query.scope.split(" ") : manager.config.defaultScopes;
93
- const invalidScopes = manager.validateScopes(requestedScopes);
94
- if (invalidScopes.length > 0) return this.#redirectWithError(ctx, manager, query.redirect_uri, "invalid_scope", `Invalid scopes: ${invalidScopes.join(", ")}`, query.state);
95
- try {
96
- clientService.validateClientScopes(requestedScopes, client.scopes);
97
- } catch (error) {
98
- return this.#redirectWithError(ctx, manager, query.redirect_uri, "invalid_scope", error.message, query.state);
99
- }
100
- if (!query.code_challenge) return this.#redirectWithError(ctx, manager, query.redirect_uri, "invalid_request", "PKCE code_challenge is required", query.state);
101
- if (query.code_challenge_method !== "S256") return this.#redirectWithError(ctx, manager, query.redirect_uri, "invalid_request", "Only S256 code_challenge_method is supported", query.state);
102
- await ctx.auth.check();
103
- const user = ctx.auth.user;
104
- if (!user) {
105
- const params = new URLSearchParams();
106
- this.#copyAuthorizeDisplayParams(params, ctx.request.qs());
107
- const loginPage = this.#resolvePageUrl(manager.config.loginPage, ctx, params);
108
- return ctx.response.redirect().toPath(loginPage);
109
- }
110
- const existingConsent = await OAuthConsent.query().where("clientId", client.clientId).where("userId", String(user.id)).first();
111
- if (existingConsent) {
112
- const consentedSet = new Set(existingConsent.scopes);
113
- if (requestedScopes.every((s) => consentedSet.has(s))) return this.#issueAuthorizationCode(ctx, manager, {
114
- client,
115
- userId: String(user.id),
116
- scopes: requestedScopes,
117
- redirectUri: query.redirect_uri,
118
- codeChallenge: query.code_challenge,
119
- codeChallengeMethod: query.code_challenge_method,
120
- state: query.state
121
- });
122
- }
123
- const params = await this.#buildAuthorizationRequestParams(manager, {
124
- userId: String(user.id),
125
- clientId: client.clientId,
126
- redirectUri: query.redirect_uri,
127
- scopes: requestedScopes,
128
- state: query.state,
129
- codeChallenge: query.code_challenge,
130
- codeChallengeMethod: query.code_challenge_method
131
- });
132
- this.#copyAuthorizeDisplayParams(params, ctx.request.qs());
133
- params.set("scope", requestedScopes.join(" "));
134
- const consentPage = this.#resolvePageUrl(manager.config.consentPage, ctx, params);
135
- return ctx.response.redirect().toPath(consentPage);
136
- }
137
- };
138
- export { AuthorizeController as default };
@@ -1,65 +0,0 @@
1
- import { o as BUILTIN_SCOPES } from "./sesame_manager-C-eEFFHM.js";
2
- import { o as E_INVALID_REQUEST, r as E_INVALID_CLIENT, s as E_INVALID_SCOPE } from "./oauth_error-CnJ3L8tf.js";
3
- import { t as OAuthClient } from "./oauth_client-BIoY5jBR.js";
4
- import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
5
- var ClientService = class {
6
- parseBasicAuth(header) {
7
- if (!header.startsWith("Basic ")) return null;
8
- try {
9
- const decoded = Buffer.from(header.slice(6), "base64").toString("utf-8");
10
- const colonIndex = decoded.indexOf(":");
11
- if (colonIndex === -1) return null;
12
- return {
13
- clientId: decodeURIComponent(decoded.slice(0, colonIndex)),
14
- clientSecret: decodeURIComponent(decoded.slice(colonIndex + 1))
15
- };
16
- } catch {
17
- return null;
18
- }
19
- }
20
- extractCredentials(options) {
21
- const basic = options.authorizationHeader ? this.parseBasicAuth(options.authorizationHeader) : null;
22
- if (basic && options.bodyClientId) throw new E_INVALID_REQUEST("Multiple client authentication methods are not allowed");
23
- if (basic) return basic;
24
- if (options.bodyClientId) return {
25
- clientId: options.bodyClientId,
26
- clientSecret: options.bodyClientSecret
27
- };
28
- return null;
29
- }
30
- async authenticateClient(options) {
31
- const credentials = this.extractCredentials(options);
32
- if (!credentials) throw new E_INVALID_CLIENT("Client authentication failed");
33
- const client = await OAuthClient.query().where("clientId", credentials.clientId).first();
34
- if (!client || client.isDisabled) throw new E_INVALID_CLIENT("Client authentication failed");
35
- if (!client.isPublic) {
36
- if (!credentials.clientSecret || !this.verifySecret(credentials.clientSecret, client.clientSecret)) throw new E_INVALID_CLIENT("Client authentication failed");
37
- }
38
- return client;
39
- }
40
- validateClientScopes(requestedScopes, clientScopes) {
41
- const nonBuiltinScopes = requestedScopes.filter((s) => !BUILTIN_SCOPES.has(s));
42
- if (clientScopes.length === 0 && nonBuiltinScopes.length > 0) throw new E_INVALID_SCOPE(`Scope not allowed: ${nonBuiltinScopes.join(", ")}`);
43
- const allowedSet = new Set(clientScopes);
44
- const invalid = nonBuiltinScopes.filter((s) => !allowedSet.has(s));
45
- if (invalid.length > 0) throw new E_INVALID_SCOPE(`Scope not allowed: ${invalid.join(", ")}`);
46
- }
47
- hashSecret(secret) {
48
- return createHash("sha256").update(secret).digest("base64url");
49
- }
50
- verifySecret(secret, storedHash) {
51
- const hash = this.hashSecret(secret);
52
- try {
53
- return timingSafeEqual(Buffer.from(hash), Buffer.from(storedHash));
54
- } catch {
55
- return false;
56
- }
57
- }
58
- generateClientId() {
59
- return randomBytes(16).toString("hex");
60
- }
61
- generateClientSecret() {
62
- return randomBytes(32).toString("base64url");
63
- }
64
- };
65
- export { ClientService as t };
@@ -1,85 +0,0 @@
1
- import { i as OAuthAuthorizationCode, n as OAuthPendingAuthorizationRequest, r as OAuthConsent, t as SesameManager } from "./sesame_manager-C-eEFFHM.js";
2
- import "./decorate-BKZEjPRg.js";
3
- import "./oauth_access_token-bsoM5KeU.js";
4
- import { a as E_INVALID_GRANT, o as E_INVALID_REQUEST, r as E_INVALID_CLIENT } from "./oauth_error-CnJ3L8tf.js";
5
- import { t as OAuthClient } from "./oauth_client-BIoY5jBR.js";
6
- import { t as TokenService } from "./token_service-fhoA4slP.js";
7
- import { DateTime } from "luxon";
8
- import string from "@adonisjs/core/helpers/string";
9
- import vine from "@vinejs/vine";
10
- var ConsentController = class ConsentController {
11
- static validator = vine.create({ auth_token: vine.string() });
12
- async #consumePendingRequest(hashedToken, userId) {
13
- const row = await OAuthPendingAuthorizationRequest.query().where("token", hashedToken).where("userId", userId).where("expiresAt", ">", DateTime.now().toSQL()).first();
14
- if (!row) return null;
15
- const deleted = await OAuthPendingAuthorizationRequest.query().where("id", row.id).delete();
16
- if ((Array.isArray(deleted) ? Number(deleted[0]) : Number(deleted)) === 0) return null;
17
- return row;
18
- }
19
- async #issueAuthorizationCode(ctx, manager, options) {
20
- const tokenService = new TokenService(manager);
21
- const raw = tokenService.generateOpaqueToken();
22
- const hashed = tokenService.hashToken(raw);
23
- const ttl = string.seconds.parse(manager.config.authorizationCodeTtl);
24
- await OAuthAuthorizationCode.create({
25
- id: crypto.randomUUID(),
26
- code: hashed,
27
- clientId: options.client.clientId,
28
- userId: options.userId,
29
- scopes: options.scopes,
30
- redirectUri: options.redirectUri,
31
- codeChallenge: options.codeChallenge ?? null,
32
- codeChallengeMethod: options.codeChallengeMethod ?? null,
33
- expiresAt: DateTime.now().plus({ seconds: ttl })
34
- });
35
- const url = new URL(options.redirectUri);
36
- url.searchParams.set("code", raw);
37
- if (options.state) url.searchParams.set("state", options.state);
38
- url.searchParams.set("iss", manager.config.issuer);
39
- return ctx.response.redirect().toPath(url.toString());
40
- }
41
- async handle(ctx) {
42
- const manager = await ctx.containerResolver.make(SesameManager);
43
- await ctx.auth.check();
44
- const user = ctx.auth.user;
45
- if (!user) throw new E_INVALID_REQUEST("User must be authenticated");
46
- const [error, body] = await ConsentController.validator.tryValidate(ctx.request.body());
47
- if (error) throw new E_INVALID_REQUEST("Missing required parameter: auth_token");
48
- const accept = ctx.request.body().accept;
49
- const hashedToken = new TokenService(manager).hashToken(body.auth_token);
50
- const pendingRequest = await this.#consumePendingRequest(hashedToken, String(user.id));
51
- if (!pendingRequest) throw new E_INVALID_GRANT("Authorization request not found or expired");
52
- const client = await OAuthClient.query().where("clientId", pendingRequest.clientId).first();
53
- if (!client) throw new E_INVALID_CLIENT("Client not found");
54
- if (client.isDisabled) throw new E_INVALID_CLIENT("Client is disabled");
55
- if (!client.redirectUris.includes(pendingRequest.redirectUri)) throw new E_INVALID_REQUEST("Invalid redirect_uri");
56
- if (!accept) {
57
- const url = new URL(pendingRequest.redirectUri);
58
- url.searchParams.set("error", "access_denied");
59
- url.searchParams.set("error_description", "The user denied the authorization request");
60
- if (pendingRequest.state) url.searchParams.set("state", pendingRequest.state);
61
- url.searchParams.set("iss", manager.config.issuer);
62
- return ctx.response.redirect().toPath(url.toString());
63
- }
64
- const existingConsent = await OAuthConsent.query().where("clientId", client.clientId).where("userId", String(user.id)).first();
65
- if (existingConsent) {
66
- existingConsent.scopes = [...new Set([...existingConsent.scopes, ...pendingRequest.scopes])];
67
- await existingConsent.save();
68
- } else await OAuthConsent.create({
69
- id: crypto.randomUUID(),
70
- clientId: client.clientId,
71
- userId: String(user.id),
72
- scopes: pendingRequest.scopes
73
- });
74
- return this.#issueAuthorizationCode(ctx, manager, {
75
- client,
76
- userId: String(user.id),
77
- scopes: pendingRequest.scopes,
78
- redirectUri: pendingRequest.redirectUri,
79
- codeChallenge: pendingRequest.codeChallenge ?? void 0,
80
- codeChallengeMethod: pendingRequest.codeChallengeMethod ?? void 0,
81
- state: pendingRequest.state ?? void 0
82
- });
83
- }
84
- };
85
- export { ConsentController as default };
@@ -1,15 +0,0 @@
1
- import { column } from "@adonisjs/lucid/orm";
2
- function json(options) {
3
- return column({
4
- ...options,
5
- prepare: (value) => value == null ? null : JSON.stringify(value),
6
- consume: (value) => value == null ? null : typeof value === "string" ? JSON.parse(value) : value
7
- });
8
- }
9
- function __decorate(decorators, target, key, desc) {
10
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
11
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
12
- 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;
13
- return c > 3 && r && Object.defineProperty(target, key, r), r;
14
- }
15
- export { json as n, __decorate as t };
@@ -1,81 +0,0 @@
1
- import { t as SesameManager } from "./sesame_manager-C-eEFFHM.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";
5
- const DISCOVERY_ROUTE_NAMES = {
6
- authorization_endpoint: "sesame.authorize",
7
- token_endpoint: "sesame.token",
8
- registration_endpoint: "sesame.register",
9
- introspection_endpoint: "sesame.introspect",
10
- revocation_endpoint: "sesame.revoke"
11
- };
12
- var MetadataController = class {
13
- #assertDiscoveryRoutes(router, options) {
14
- const requiredRoutes = [
15
- DISCOVERY_ROUTE_NAMES.authorization_endpoint,
16
- DISCOVERY_ROUTE_NAMES.token_endpoint,
17
- DISCOVERY_ROUTE_NAMES.introspection_endpoint,
18
- DISCOVERY_ROUTE_NAMES.revocation_endpoint
19
- ];
20
- if (options.includeRegistration) requiredRoutes.push(DISCOVERY_ROUTE_NAMES.registration_endpoint);
21
- const missingRoutes = requiredRoutes.filter((routeName) => !router.has(routeName));
22
- 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
- }
24
- async authServer(ctx) {
25
- const manager = await ctx.containerResolver.make(SesameManager);
26
- const router = await ctx.containerResolver.make("router");
27
- const issuer = manager.config.issuer;
28
- const prefixUrl = issuer;
29
- this.#assertDiscoveryRoutes(router, { includeRegistration: manager.config.allowDynamicRegistration });
30
- ctx.response.header("Cache-Control", "public, max-age=15, stale-while-revalidate=15, stale-if-error=86400");
31
- return {
32
- issuer,
33
- authorization_endpoint: router.makeUrl("sesame.authorize", {}, { prefixUrl }),
34
- token_endpoint: router.makeUrl("sesame.token", {}, { prefixUrl }),
35
- registration_endpoint: manager.config.allowDynamicRegistration ? router.makeUrl("sesame.register", {}, { prefixUrl }) : void 0,
36
- introspection_endpoint: router.makeUrl("sesame.introspect", {}, { prefixUrl }),
37
- revocation_endpoint: router.makeUrl("sesame.revoke", {}, { prefixUrl }),
38
- response_types_supported: ["code"],
39
- response_modes_supported: ["query"],
40
- grant_types_supported: manager.config.grantTypes,
41
- token_endpoint_auth_methods_supported: [
42
- "none",
43
- "client_secret_basic",
44
- "client_secret_post"
45
- ],
46
- introspection_endpoint_auth_methods_supported: [
47
- "none",
48
- "client_secret_basic",
49
- "client_secret_post"
50
- ],
51
- revocation_endpoint_auth_methods_supported: [
52
- "none",
53
- "client_secret_basic",
54
- "client_secret_post"
55
- ],
56
- code_challenge_methods_supported: ["S256"],
57
- authorization_response_iss_parameter_supported: true
58
- };
59
- }
60
- async oidc(ctx) {
61
- const base = await this.authServer(ctx);
62
- const manager = await ctx.containerResolver.make(SesameManager);
63
- return {
64
- ...base,
65
- subject_types_supported: ["public"],
66
- scopes_supported: Object.keys(manager.config.scopes)
67
- };
68
- }
69
- async protectedResource(ctx) {
70
- const manager = await ctx.containerResolver.make(SesameManager);
71
- const issuer = manager.config.issuer;
72
- ctx.response.header("Cache-Control", "public, max-age=15, stale-while-revalidate=15, stale-if-error=86400");
73
- return {
74
- resource: issuer,
75
- authorization_servers: [issuer],
76
- scopes_supported: Object.keys(manager.config.scopes),
77
- bearer_methods_supported: ["header"]
78
- };
79
- }
80
- };
81
- export { MetadataController as default };
@@ -1,24 +0,0 @@
1
- import { n as json, t as __decorate } from "./decorate-BKZEjPRg.js";
2
- import { BaseModel, column } from "@adonisjs/lucid/orm";
3
- var OAuthClient = class extends BaseModel {
4
- static table = "oauth_clients";
5
- };
6
- __decorate([column({ isPrimary: true })], OAuthClient.prototype, "id", void 0);
7
- __decorate([column()], OAuthClient.prototype, "clientId", void 0);
8
- __decorate([column({ serializeAs: null })], OAuthClient.prototype, "clientSecret", void 0);
9
- __decorate([column()], OAuthClient.prototype, "name", void 0);
10
- __decorate([json()], OAuthClient.prototype, "redirectUris", void 0);
11
- __decorate([json()], OAuthClient.prototype, "scopes", void 0);
12
- __decorate([json()], OAuthClient.prototype, "grantTypes", void 0);
13
- __decorate([column()], OAuthClient.prototype, "isPublic", void 0);
14
- __decorate([column()], OAuthClient.prototype, "isDisabled", void 0);
15
- __decorate([column()], OAuthClient.prototype, "requirePkce", void 0);
16
- __decorate([column()], OAuthClient.prototype, "type", void 0);
17
- __decorate([json()], OAuthClient.prototype, "metadata", void 0);
18
- __decorate([column()], OAuthClient.prototype, "userId", void 0);
19
- __decorate([column.dateTime({ autoCreate: true })], OAuthClient.prototype, "createdAt", void 0);
20
- __decorate([column.dateTime({
21
- autoCreate: true,
22
- autoUpdate: true
23
- })], OAuthClient.prototype, "updatedAt", void 0);
24
- export { OAuthClient as t };
@@ -1,94 +0,0 @@
1
- import { Exception } from "@adonisjs/core/exceptions";
2
- var OAuthError = class extends Exception {
3
- static oauthCode;
4
- get oauthCode() {
5
- return this.constructor.oauthCode;
6
- }
7
- handle(error, ctx) {
8
- ctx.response.status(error.status).json({
9
- error: error.oauthCode,
10
- error_description: error.message
11
- });
12
- }
13
- };
14
- const E_INVALID_REQUEST = class extends OAuthError {
15
- static status = 400;
16
- static code = "E_INVALID_REQUEST";
17
- static message = "Invalid request";
18
- static oauthCode = "invalid_request";
19
- };
20
- const E_INVALID_CLIENT = class extends OAuthError {
21
- static status = 401;
22
- static code = "E_INVALID_CLIENT";
23
- static message = "Invalid client";
24
- static oauthCode = "invalid_client";
25
- handle(error, ctx) {
26
- if (ctx.request.header("authorization")?.startsWith("Basic ")) ctx.response.header("WWW-Authenticate", "Basic");
27
- super.handle(error, ctx);
28
- }
29
- };
30
- const E_INVALID_GRANT = class extends OAuthError {
31
- static status = 400;
32
- static code = "E_INVALID_GRANT";
33
- static message = "Invalid grant";
34
- static oauthCode = "invalid_grant";
35
- };
36
- const E_INVALID_SCOPE = class extends OAuthError {
37
- static status = 400;
38
- static code = "E_INVALID_SCOPE";
39
- static message = "Invalid scope";
40
- static oauthCode = "invalid_scope";
41
- };
42
- const E_INVALID_TOKEN = class extends OAuthError {
43
- static status = 401;
44
- static code = "E_INVALID_TOKEN";
45
- static message = "Invalid token";
46
- static oauthCode = "invalid_token";
47
- };
48
- const E_UNSUPPORTED_GRANT_TYPE = class extends OAuthError {
49
- static status = 400;
50
- static code = "E_UNSUPPORTED_GRANT_TYPE";
51
- static message = "Unsupported grant type";
52
- static oauthCode = "unsupported_grant_type";
53
- };
54
- const E_UNSUPPORTED_RESPONSE_TYPE = class extends OAuthError {
55
- static status = 400;
56
- static code = "E_UNSUPPORTED_RESPONSE_TYPE";
57
- static message = "Unsupported response type";
58
- static oauthCode = "unsupported_response_type";
59
- };
60
- const E_ACCESS_DENIED = class extends OAuthError {
61
- static status = 403;
62
- static code = "E_ACCESS_DENIED";
63
- static message = "Access denied";
64
- static oauthCode = "access_denied";
65
- };
66
- const E_INVALID_CLIENT_METADATA = class extends OAuthError {
67
- static status = 400;
68
- static code = "E_INVALID_CLIENT_METADATA";
69
- static message = "Invalid client metadata";
70
- static oauthCode = "invalid_client_metadata";
71
- };
72
- const E_SERVER_ERROR = class extends OAuthError {
73
- static status = 500;
74
- static code = "E_SERVER_ERROR";
75
- static message = "Server error";
76
- static oauthCode = "server_error";
77
- };
78
- const E_INSUFFICIENT_SCOPE = class extends OAuthError {
79
- static status = 403;
80
- static code = "E_INSUFFICIENT_SCOPE";
81
- static message = "Insufficient scope";
82
- static oauthCode = "insufficient_scope";
83
- missingScopes;
84
- constructor(missingScopes, message) {
85
- super(message ?? "The token does not have the required scope(s)");
86
- this.missingScopes = missingScopes;
87
- }
88
- handle(error, ctx) {
89
- const scope = error.missingScopes.join(" ");
90
- ctx.response.header("WWW-Authenticate", `Bearer error="insufficient_scope", error_description="${error.message}", scope="${scope}"`);
91
- super.handle(error, ctx);
92
- }
93
- };
94
- export { E_INVALID_GRANT as a, E_INVALID_TOKEN as c, E_UNSUPPORTED_RESPONSE_TYPE as d, OAuthError as f, 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 };
@@ -1,4 +0,0 @@
1
- import { t as SesameManager } from "./sesame_manager-C-eEFFHM.js";
2
- import "./decorate-BKZEjPRg.js";
3
- import "./oauth_access_token-bsoM5KeU.js";
4
- export { SesameManager };