@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/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@julr/sesame",
3
3
  "description": "OAuth 2.1 + OIDC server for AdonisJS",
4
- "version": "0.5.1",
4
+ "version": "0.7.0",
5
5
  "engines": {
6
6
  "node": ">=24.0.0"
7
7
  },
8
- "packageManager": "pnpm@10.26.2",
8
+ "packageManager": "pnpm@10.34.5",
9
9
  "type": "module",
10
10
  "files": [
11
11
  "build",
@@ -15,9 +15,14 @@
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",
20
+ "./storage/types": "./build/src/storage/types.js",
21
+ "./drivers/lucid": "./build/src/storage/drivers/lucid.js",
22
+ "./drivers/kysely": "./build/src/storage/drivers/kysely.js",
19
23
  "./sesame_provider": "./build/providers/sesame_provider.js",
20
24
  "./guard": "./build/src/guard/main.js",
25
+ "./guard/lucid": "./build/src/guard/lucid.js",
21
26
  "./scope_middleware": "./build/src/middleware/scope_middleware.js",
22
27
  "./any_scope_middleware": "./build/src/middleware/any_scope_middleware.js",
23
28
  "./services/main": "./build/services/main.js",
@@ -25,21 +30,16 @@
25
30
  "./commands": "./build/commands/main.js"
26
31
  },
27
32
  "scripts": {
28
- "copy:templates": "copyfiles \"stubs/**/*.stub\" build",
29
- "typecheck": "tsc --noEmit",
30
- "lint": "oxlint .",
31
- "format": "oxfmt --write .",
33
+ "check": "vp check",
34
+ "format": "vp fmt --write .",
32
35
  "quick:test": "node --import=@poppinss/ts-exec --enable-source-maps bin/test.ts",
33
- "pretest": "npm run lint",
36
+ "test:sql": "bash bin/test_sql_dialects.sh",
34
37
  "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",
38
+ "build": "vp pack && tsc --emitDeclarationOnly --declaration && adonis-kit index build/commands",
40
39
  "release": "release-it",
41
40
  "version": "npm run build",
42
- "prepublishOnly": "npm run build"
41
+ "prepublishOnly": "npm run build",
42
+ "prepare": "vp config"
43
43
  },
44
44
  "repository": {
45
45
  "type": "git",
@@ -49,70 +49,70 @@
49
49
  "url": "https://github.com/Julien-R44/sesame/issues"
50
50
  },
51
51
  "homepage": "https://github.com/Julien-R44/sesame#readme",
52
- "keywords": [],
52
+ "keywords": [
53
+ "adonisjs",
54
+ "adonis",
55
+ "oauth",
56
+ "oauth2.1",
57
+ "oidc",
58
+ "openid-connect",
59
+ "authorization-server",
60
+ "mcp",
61
+ "pkce"
62
+ ],
53
63
  "author": "Julien Ripouteau",
54
64
  "license": "MIT",
55
65
  "devDependencies": {
56
- "@adonisjs/assembler": "^8.0.0",
57
- "@adonisjs/auth": "^10.0.0",
58
- "@adonisjs/core": "^7.0.0",
59
- "@adonisjs/lucid": "^22.0.0",
60
- "@adonisjs/prettier-config": "^1.4.5",
66
+ "@adonisjs/assembler": "^8.5.2",
67
+ "@adonisjs/auth": "^10.1.0",
68
+ "@adonisjs/core": "^7.5.2",
69
+ "@adonisjs/lucid": "^22.4.2",
61
70
  "@adonisjs/tsconfig": "^2.0.0",
71
+ "@japa/api-client": "^3.2.1",
62
72
  "@japa/assert": "^4.2.0",
63
73
  "@japa/expect-type": "^2.0.4",
64
74
  "@japa/runner": "^5.3.0",
65
75
  "@poppinss/ts-exec": "^1.4.4",
66
- "@release-it/conventional-changelog": "^10.0.5",
67
- "@types/luxon": "^3.7.1",
68
- "@types/node": "^25.3.3",
69
- "@vinejs/vine": "^4.3.0",
70
- "better-sqlite3": "^12.6.2",
71
- "c8": "^11.0.0",
72
- "copyfiles": "^2.4.1",
76
+ "@release-it/conventional-changelog": "^12.0.2",
77
+ "@types/better-sqlite3": "^9.6.0",
78
+ "@types/luxon": "^3.7.5",
79
+ "@types/node": "^26.6.2",
80
+ "@types/pg": "^8.23.1",
81
+ "@vinejs/vine": "^4.4.0",
82
+ "better-sqlite3": "^13.0.3",
83
+ "c8": "^12.0.0",
73
84
  "cross-env": "^10.1.0",
74
- "oxfmt": "^0.36.0",
75
- "oxlint": "^1.51.0",
76
- "release-it": "^19.2.4",
77
- "tsdown": "^0.20.3",
78
- "typescript": "^5.9.3"
85
+ "kysely": "^0.29.6",
86
+ "mysql2": "^3.24.4",
87
+ "node-gyp": "^13.0.2",
88
+ "pg": "^8.23.0",
89
+ "release-it": "^21.1.0",
90
+ "taze": "^21.1.0",
91
+ "typescript": "^7.0.2",
92
+ "vite-plus": "catalog:"
79
93
  },
80
94
  "peerDependencies": {
81
95
  "@adonisjs/assembler": "^8.0.0",
82
96
  "@adonisjs/auth": "^10.0.0",
83
97
  "@adonisjs/core": "^7.0.0",
84
- "@adonisjs/lucid": "^21.0.0",
85
- "@vinejs/vine": "^4.0.0"
98
+ "@adonisjs/lucid": "^21.0.0 || ^22.0.0",
99
+ "@vinejs/vine": "^4.0.0",
100
+ "kysely": "^0.29.0"
86
101
  },
87
102
  "peerDependenciesMeta": {
88
103
  "@adonisjs/assembler": {
89
104
  "optional": true
105
+ },
106
+ "@adonisjs/lucid": {
107
+ "optional": true
108
+ },
109
+ "kysely": {
110
+ "optional": true
90
111
  }
91
112
  },
92
113
  "publishConfig": {
93
114
  "access": "public"
94
115
  },
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
116
  "release-it": {
117
117
  "git": {
118
118
  "requireCleanWorkingDir": true,
@@ -147,6 +147,7 @@
147
147
  ]
148
148
  },
149
149
  "dependencies": {
150
+ "jose": "^6.2.12",
150
151
  "luxon": "^3.7.2"
151
152
  }
152
153
  }
@@ -1,138 +0,0 @@
1
- import { i as OAuthAuthorizationCode, n as OAuthPendingAuthorizationRequest, r as OAuthConsent, t as SesameManager } from "./sesame_manager-DwDZy5Vy.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-WTNMqWzY.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-DwDZy5Vy.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-DwDZy5Vy.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,18 +0,0 @@
1
- import { n as json, t as __decorate } from "./decorate-BKZEjPRg.js";
2
- import { BaseModel, column } from "@adonisjs/lucid/orm";
3
- var OAuthAccessToken = class extends BaseModel {
4
- static table = "oauth_access_tokens";
5
- };
6
- __decorate([column({ isPrimary: true })], OAuthAccessToken.prototype, "id", void 0);
7
- __decorate([column()], OAuthAccessToken.prototype, "tokenHash", void 0);
8
- __decorate([column()], OAuthAccessToken.prototype, "clientId", void 0);
9
- __decorate([column()], OAuthAccessToken.prototype, "userId", void 0);
10
- __decorate([json()], OAuthAccessToken.prototype, "scopes", void 0);
11
- __decorate([column.dateTime()], OAuthAccessToken.prototype, "expiresAt", void 0);
12
- __decorate([column.dateTime()], OAuthAccessToken.prototype, "revokedAt", void 0);
13
- __decorate([column.dateTime({ autoCreate: true })], OAuthAccessToken.prototype, "createdAt", void 0);
14
- __decorate([column.dateTime({
15
- autoCreate: true,
16
- autoUpdate: true
17
- })], OAuthAccessToken.prototype, "updatedAt", void 0);
18
- export { OAuthAccessToken as t };
@@ -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,41 +0,0 @@
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";
8
- import { DateTime } from "luxon";
9
- var RevokeController = class {
10
- async handle(ctx) {
11
- const manager = await ctx.containerResolver.make(SesameManager);
12
- const client = await new ClientService().authenticateClient({
13
- authorizationHeader: ctx.request.header("authorization"),
14
- bodyClientId: ctx.request.body().client_id,
15
- bodyClientSecret: ctx.request.body().client_secret
16
- });
17
- const token = ctx.request.body().token;
18
- if (!token) return ctx.response.ok({});
19
- const tokenTypeHint = ctx.request.body().token_type_hint;
20
- const hashed = new TokenService(manager).hashToken(token);
21
- if (!tokenTypeHint || tokenTypeHint === "access_token") {
22
- const record = await OAuthAccessToken.query().where("tokenHash", hashed).where("clientId", client.clientId).first();
23
- if (record && !record.revokedAt) {
24
- record.revokedAt = DateTime.now();
25
- await record.save();
26
- return ctx.response.ok({});
27
- }
28
- if (tokenTypeHint === "access_token") return ctx.response.ok({});
29
- }
30
- if (!tokenTypeHint || tokenTypeHint === "refresh_token") {
31
- const refreshToken = await OAuthRefreshToken.query().where("token", hashed).where("clientId", client.clientId).first();
32
- if (refreshToken && !refreshToken.revokedAt) {
33
- refreshToken.revokedAt = DateTime.now();
34
- await refreshToken.save();
35
- await OAuthAccessToken.query().where("tokenHash", refreshToken.accessTokenId).whereNull("revokedAt").update({ revokedAt: DateTime.now().toSQL() });
36
- }
37
- }
38
- return ctx.response.ok({});
39
- }
40
- };
41
- export { RevokeController as default };
@@ -1,4 +0,0 @@
1
- import { t as SesameManager } from "./sesame_manager-DwDZy5Vy.js";
2
- import "./decorate-BKZEjPRg.js";
3
- import "./oauth_access_token-bsoM5KeU.js";
4
- export { SesameManager };