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