@julr/sesame 0.1.0 → 0.2.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 (31) hide show
  1. package/build/{authorize_controller-CUdEDNEi.js → authorize_controller-D3Kau97j.js} +19 -19
  2. package/build/{client_info_controller-DeIVcW8B.js → client_info_controller-CSipLLoV.js} +3 -3
  3. package/build/{client_service-B9fD3ZGe.js → client_service-DrkqOh37.js} +1 -1
  4. package/build/commands/sesame_purge.js +3 -3
  5. package/build/configure.js +4 -2
  6. package/build/{consent_controller-DFfx7qVs.js → consent_controller-DXayMjAi.js} +26 -29
  7. package/build/index.d.ts +0 -1
  8. package/build/index.js +10 -10
  9. package/build/{introspect_controller-BzwfaUUE.js → introspect_controller-lK9gL03t.js} +7 -7
  10. package/build/{metadata_controller-BSRRElQX.js → metadata_controller-DMAahJkn.js} +3 -3
  11. package/build/{oauth_access_token-BpG8sq-c.js → oauth_access_token-bsoM5KeU.js} +1 -1
  12. package/build/{oauth_client-eh0e5ql-.js → oauth_client-Cpppi0_7.js} +1 -1
  13. package/build/providers/sesame_provider.js +4 -4
  14. package/build/{register_controller-BA7uQAgt.js → register_controller-B0saLAuc.js} +6 -6
  15. package/build/{revoke_controller-CNIgNKH3.js → revoke_controller-D0nl2hS5.js} +7 -7
  16. package/build/{routes-D6QCu0Pz.js → routes-qYJLHjeB.js} +9 -9
  17. package/build/{sesame_manager-B4tO2PLO.js → sesame_manager-CA7dOGwg.js} +22 -4
  18. package/build/src/controllers/authorize_controller.d.ts +3 -3
  19. package/build/src/guard/main.js +6 -6
  20. package/build/src/models/oauth_pending_authorization_request.d.ts +29 -0
  21. package/build/src/sesame_manager.d.ts +1 -0
  22. package/build/src/types.d.ts +7 -0
  23. package/build/stubs/config/sesame.stub +1 -1
  24. package/build/stubs/migrations/create_oauth_pending_authorization_requests_table.stub +32 -0
  25. package/build/{token_controller-C9wh813f.js → token_controller-CDFuzY6S.js} +7 -7
  26. package/build/{user_provider-B3rXEUT3.js → user_provider-CQczt8_g.js} +3 -3
  27. package/package.json +1 -1
  28. package/build/main-kn40V-hF.js +0 -2
  29. /package/build/{decorate-2_8Ex77k.js → decorate-BKZEjPRg.js} +0 -0
  30. /package/build/{oauth_error-BQPqV-MV.js → oauth_error-BOn0846g.js} +0 -0
  31. /package/build/{token_service-Czz9v5GI.js → token_service-BLKxKIHI.js} +0 -0
@@ -1,10 +1,10 @@
1
- import "./decorate-2_8Ex77k.js";
2
- import "./oauth_access_token-BpG8sq-c.js";
3
- import { i as OAuthAuthorizationCode, r as OAuthConsent, t as SesameManager } from "./sesame_manager-B4tO2PLO.js";
4
- import { a as E_INVALID_REQUEST, n as E_INVALID_CLIENT, u as E_UNSUPPORTED_RESPONSE_TYPE } from "./oauth_error-BQPqV-MV.js";
5
- import { t as OAuthClient } from "./oauth_client-eh0e5ql-.js";
6
- import { t as TokenService } from "./token_service-Czz9v5GI.js";
7
- import { t as ClientService } from "./client_service-B9fD3ZGe.js";
1
+ import "./decorate-BKZEjPRg.js";
2
+ import "./oauth_access_token-bsoM5KeU.js";
3
+ import { a as OAuthAuthorizationCode, i as OAuthConsent, r as OAuthPendingAuthorizationRequest, t as SesameManager } from "./sesame_manager-CA7dOGwg.js";
4
+ import { a as E_INVALID_REQUEST, n as E_INVALID_CLIENT, u as E_UNSUPPORTED_RESPONSE_TYPE } from "./oauth_error-BOn0846g.js";
5
+ import { t as OAuthClient } from "./oauth_client-Cpppi0_7.js";
6
+ import { t as TokenService } from "./token_service-BLKxKIHI.js";
7
+ import { t as ClientService } from "./client_service-DrkqOh37.js";
8
8
  import { DateTime } from "luxon";
9
9
  import vine from "@vinejs/vine";
10
10
  var AuthorizeController = class AuthorizeController {
@@ -17,11 +17,6 @@ var AuthorizeController = class AuthorizeController {
17
17
  code_challenge: vine.string().optional(),
18
18
  code_challenge_method: vine.string().optional()
19
19
  });
20
- #getAuthorizationSession(ctx) {
21
- const session = ctx.session;
22
- if (!session || typeof session.put !== "function") throw new E_INVALID_REQUEST("Session middleware is required for the browser authorization flow");
23
- return session;
24
- }
25
20
  #redirectWithError(ctx, manager, redirectUri, error, description, state) {
26
21
  const url = new URL(redirectUri);
27
22
  url.searchParams.set("error", error);
@@ -30,17 +25,21 @@ var AuthorizeController = class AuthorizeController {
30
25
  url.searchParams.set("iss", manager.config.issuer);
31
26
  return ctx.response.redirect().toPath(url.toString());
32
27
  }
33
- #buildAuthorizationRequestParams(ctx, manager, options) {
34
- const rawRequestToken = new TokenService(manager).generateOpaqueToken();
35
- const session = this.#getAuthorizationSession(ctx);
36
- session.put("sesame.authToken", rawRequestToken);
37
- session.put("sesame.authRequest", {
28
+ async #buildAuthorizationRequestParams(manager, options) {
29
+ const tokenService = new TokenService(manager);
30
+ const rawRequestToken = tokenService.generateOpaqueToken();
31
+ const ttl = manager.parseTtl(manager.config.authorizationRequestTtl);
32
+ await OAuthPendingAuthorizationRequest.create({
33
+ id: crypto.randomUUID(),
34
+ token: tokenService.hashToken(rawRequestToken),
35
+ userId: options.userId,
38
36
  clientId: options.clientId,
39
37
  redirectUri: options.redirectUri,
40
38
  scopes: options.scopes,
41
39
  state: options.state ?? null,
42
40
  codeChallenge: options.codeChallenge ?? null,
43
- codeChallengeMethod: options.codeChallengeMethod ?? null
41
+ codeChallengeMethod: options.codeChallengeMethod ?? null,
42
+ expiresAt: DateTime.now().plus({ seconds: ttl })
44
43
  });
45
44
  const params = new URLSearchParams();
46
45
  params.set("auth_token", rawRequestToken);
@@ -120,7 +119,8 @@ var AuthorizeController = class AuthorizeController {
120
119
  state: query.state
121
120
  });
122
121
  }
123
- const params = this.#buildAuthorizationRequestParams(ctx, manager, {
122
+ const params = await this.#buildAuthorizationRequestParams(manager, {
123
+ userId: String(user.id),
124
124
  clientId: client.clientId,
125
125
  redirectUri: query.redirect_uri,
126
126
  scopes: requestedScopes,
@@ -1,6 +1,6 @@
1
- import "./decorate-2_8Ex77k.js";
2
- import { a as E_INVALID_REQUEST, n as E_INVALID_CLIENT } from "./oauth_error-BQPqV-MV.js";
3
- import { t as OAuthClient } from "./oauth_client-eh0e5ql-.js";
1
+ import "./decorate-BKZEjPRg.js";
2
+ import { a as E_INVALID_REQUEST, n as E_INVALID_CLIENT } from "./oauth_error-BOn0846g.js";
3
+ import { t as OAuthClient } from "./oauth_client-Cpppi0_7.js";
4
4
  import vine from "@vinejs/vine";
5
5
  var ClientInfoController = class ClientInfoController {
6
6
  static validator = vine.create({ client_id: vine.string() });
@@ -1,4 +1,4 @@
1
- import { o as E_INVALID_SCOPE } from "./oauth_error-BQPqV-MV.js";
1
+ import { o as E_INVALID_SCOPE } from "./oauth_error-BOn0846g.js";
2
2
  import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
3
3
  var ClientService = class {
4
4
  parseBasicAuth(header) {
@@ -1,6 +1,6 @@
1
- import { t as __decorate } from "../decorate-2_8Ex77k.js";
2
- import "../oauth_access_token-BpG8sq-c.js";
3
- import { t as SesameManager } from "../sesame_manager-B4tO2PLO.js";
1
+ import { t as __decorate } from "../decorate-BKZEjPRg.js";
2
+ import "../oauth_access_token-bsoM5KeU.js";
3
+ import { t as SesameManager } from "../sesame_manager-CA7dOGwg.js";
4
4
  import { BaseCommand, flags } from "@adonisjs/core/ace";
5
5
  var SesamePurge = class extends BaseCommand {
6
6
  static commandName = "sesame:purge";
@@ -1,13 +1,15 @@
1
- import { t as stubsRoot } from "./main-kn40V-hF.js";
1
+ import { join } from "node:path";
2
2
  async function configure(command) {
3
3
  const codemods = await command.createCodemods();
4
+ const stubsRoot = join(import.meta.url, "./stubs");
4
5
  await codemods.makeUsingStub(stubsRoot, "config/sesame.stub", {});
5
6
  for (const stub of [
6
7
  "migrations/create_oauth_clients_table.stub",
7
8
  "migrations/create_oauth_authorization_codes_table.stub",
8
9
  "migrations/create_oauth_access_tokens_table.stub",
9
10
  "migrations/create_oauth_refresh_tokens_table.stub",
10
- "migrations/create_oauth_consents_table.stub"
11
+ "migrations/create_oauth_consents_table.stub",
12
+ "migrations/create_oauth_pending_authorization_requests_table.stub"
11
13
  ]) await codemods.makeUsingStub(stubsRoot, stub, {});
12
14
  await codemods.updateRcFile((rcFile) => {
13
15
  rcFile.addProvider("@julr/sesame/sesame_provider").addCommand("@julr/sesame/commands");
@@ -1,17 +1,19 @@
1
- import "./decorate-2_8Ex77k.js";
2
- import "./oauth_access_token-BpG8sq-c.js";
3
- import { i as OAuthAuthorizationCode, r as OAuthConsent, t as SesameManager } from "./sesame_manager-B4tO2PLO.js";
4
- import { a as E_INVALID_REQUEST, i as E_INVALID_GRANT, n as E_INVALID_CLIENT } from "./oauth_error-BQPqV-MV.js";
5
- import { t as OAuthClient } from "./oauth_client-eh0e5ql-.js";
6
- import { t as TokenService } from "./token_service-Czz9v5GI.js";
1
+ import "./decorate-BKZEjPRg.js";
2
+ import "./oauth_access_token-bsoM5KeU.js";
3
+ import { a as OAuthAuthorizationCode, i as OAuthConsent, r as OAuthPendingAuthorizationRequest, t as SesameManager } from "./sesame_manager-CA7dOGwg.js";
4
+ import { a as E_INVALID_REQUEST, i as E_INVALID_GRANT, n as E_INVALID_CLIENT } from "./oauth_error-BOn0846g.js";
5
+ import { t as OAuthClient } from "./oauth_client-Cpppi0_7.js";
6
+ import { t as TokenService } from "./token_service-BLKxKIHI.js";
7
7
  import { DateTime } from "luxon";
8
8
  import vine from "@vinejs/vine";
9
9
  var ConsentController = class ConsentController {
10
10
  static validator = vine.create({ auth_token: vine.string() });
11
- #getAuthorizationSession(ctx) {
12
- const session = ctx.session;
13
- if (!session || typeof session.pull !== "function") throw new E_INVALID_REQUEST("Session middleware is required for the browser authorization flow");
14
- return session;
11
+ async #consumePendingRequest(hashedToken, userId) {
12
+ const row = await OAuthPendingAuthorizationRequest.query().where("token", hashedToken).where("userId", userId).where("expiresAt", ">", DateTime.now().toSQL()).first();
13
+ if (!row) return null;
14
+ const deleted = await OAuthPendingAuthorizationRequest.query().where("id", row.id).delete();
15
+ if ((Array.isArray(deleted) ? Number(deleted[0]) : Number(deleted)) === 0) return null;
16
+ return row;
15
17
  }
16
18
  async #issueAuthorizationCode(ctx, manager, options) {
17
19
  const tokenService = new TokenService(manager);
@@ -37,50 +39,45 @@ var ConsentController = class ConsentController {
37
39
  }
38
40
  async handle(ctx) {
39
41
  const manager = await ctx.containerResolver.make(SesameManager);
40
- const session = this.#getAuthorizationSession(ctx);
41
42
  await ctx.auth.check();
42
43
  const user = ctx.auth.user;
43
44
  if (!user) throw new E_INVALID_REQUEST("User must be authenticated");
44
45
  const [error, body] = await ConsentController.validator.tryValidate(ctx.request.body());
45
46
  if (error) throw new E_INVALID_REQUEST("Missing required parameter: auth_token");
46
47
  const accept = ctx.request.body().accept;
47
- const expectedAuthToken = session.pull("sesame.authToken");
48
- const authorizationRequest = session.pull("sesame.authRequest");
49
- if (!expectedAuthToken || expectedAuthToken !== body.auth_token) {
50
- session.forget(["sesame.authToken", "sesame.authRequest"]);
51
- throw new E_INVALID_GRANT("Authorization request token mismatch");
52
- }
53
- if (!authorizationRequest) throw new E_INVALID_GRANT("Authorization request not found");
54
- const client = await OAuthClient.query().where("clientId", authorizationRequest.clientId).first();
48
+ const hashedToken = new TokenService(manager).hashToken(body.auth_token);
49
+ const pendingRequest = await this.#consumePendingRequest(hashedToken, String(user.id));
50
+ if (!pendingRequest) throw new E_INVALID_GRANT("Authorization request not found or expired");
51
+ const client = await OAuthClient.query().where("clientId", pendingRequest.clientId).first();
55
52
  if (!client) throw new E_INVALID_CLIENT("Client not found");
56
53
  if (client.isDisabled) throw new E_INVALID_CLIENT("Client is disabled");
57
- if (!client.redirectUris.includes(authorizationRequest.redirectUri)) throw new E_INVALID_REQUEST("Invalid redirect_uri");
54
+ if (!client.redirectUris.includes(pendingRequest.redirectUri)) throw new E_INVALID_REQUEST("Invalid redirect_uri");
58
55
  if (!accept) {
59
- const url = new URL(authorizationRequest.redirectUri);
56
+ const url = new URL(pendingRequest.redirectUri);
60
57
  url.searchParams.set("error", "access_denied");
61
58
  url.searchParams.set("error_description", "The user denied the authorization request");
62
- if (authorizationRequest.state) url.searchParams.set("state", authorizationRequest.state);
59
+ if (pendingRequest.state) url.searchParams.set("state", pendingRequest.state);
63
60
  url.searchParams.set("iss", manager.config.issuer);
64
61
  return ctx.response.redirect().toPath(url.toString());
65
62
  }
66
63
  const existingConsent = await OAuthConsent.query().where("clientId", client.clientId).where("userId", String(user.id)).first();
67
64
  if (existingConsent) {
68
- existingConsent.scopes = [...new Set([...existingConsent.scopes, ...authorizationRequest.scopes])];
65
+ existingConsent.scopes = [...new Set([...existingConsent.scopes, ...pendingRequest.scopes])];
69
66
  await existingConsent.save();
70
67
  } else await OAuthConsent.create({
71
68
  id: crypto.randomUUID(),
72
69
  clientId: client.clientId,
73
70
  userId: String(user.id),
74
- scopes: authorizationRequest.scopes
71
+ scopes: pendingRequest.scopes
75
72
  });
76
73
  return this.#issueAuthorizationCode(ctx, manager, {
77
74
  client,
78
75
  userId: String(user.id),
79
- scopes: authorizationRequest.scopes,
80
- redirectUri: authorizationRequest.redirectUri,
81
- codeChallenge: authorizationRequest.codeChallenge ?? void 0,
82
- codeChallengeMethod: authorizationRequest.codeChallengeMethod ?? void 0,
83
- state: authorizationRequest.state ?? void 0
76
+ scopes: pendingRequest.scopes,
77
+ redirectUri: pendingRequest.redirectUri,
78
+ codeChallenge: pendingRequest.codeChallenge ?? void 0,
79
+ codeChallengeMethod: pendingRequest.codeChallengeMethod ?? void 0,
80
+ state: pendingRequest.state ?? void 0
84
81
  });
85
82
  }
86
83
  };
package/build/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  export { configure } from './configure.ts';
2
- export { stubsRoot } from './stubs/main.ts';
3
2
  export { defineConfig } from './src/define_config.ts';
4
3
  export { SesameManager } from './src/sesame_manager.ts';
5
4
  export { OAuthError, E_INVALID_REQUEST, E_INVALID_CLIENT, E_INVALID_GRANT, E_INVALID_SCOPE, E_INVALID_TOKEN, E_UNSUPPORTED_GRANT_TYPE, E_UNSUPPORTED_RESPONSE_TYPE, E_ACCESS_DENIED, E_INVALID_CLIENT_METADATA, E_SERVER_ERROR, } from './src/oauth_error.ts';
package/build/index.js CHANGED
@@ -1,14 +1,13 @@
1
- import { t as stubsRoot } from "./main-kn40V-hF.js";
2
1
  import { configure } from "./configure.js";
3
- import "./decorate-2_8Ex77k.js";
4
- import { t as OAuthAccessToken } from "./oauth_access_token-BpG8sq-c.js";
5
- import { a as OAuthRefreshToken, i as OAuthAuthorizationCode, r as OAuthConsent, t as SesameManager } from "./sesame_manager-B4tO2PLO.js";
6
- import { a as E_INVALID_REQUEST, c as E_SERVER_ERROR, d as OAuthError, i as E_INVALID_GRANT, l as E_UNSUPPORTED_GRANT_TYPE, n as E_INVALID_CLIENT, o as E_INVALID_SCOPE, r as E_INVALID_CLIENT_METADATA, s as E_INVALID_TOKEN, t as E_ACCESS_DENIED, u as E_UNSUPPORTED_RESPONSE_TYPE } from "./oauth_error-BQPqV-MV.js";
7
- import { t as OAuthClient } from "./oauth_client-eh0e5ql-.js";
8
- import "./token_service-Czz9v5GI.js";
9
- import { n as OAuthGuard, t as OAuthLucidUserProvider } from "./user_provider-B3rXEUT3.js";
2
+ import "./decorate-BKZEjPRg.js";
3
+ import { t as OAuthAccessToken } from "./oauth_access_token-bsoM5KeU.js";
4
+ import { a as OAuthAuthorizationCode, i as OAuthConsent, o as OAuthRefreshToken, t as SesameManager } from "./sesame_manager-CA7dOGwg.js";
5
+ import { a as E_INVALID_REQUEST, c as E_SERVER_ERROR, d as OAuthError, i as E_INVALID_GRANT, l as E_UNSUPPORTED_GRANT_TYPE, n as E_INVALID_CLIENT, o as E_INVALID_SCOPE, r as E_INVALID_CLIENT_METADATA, s as E_INVALID_TOKEN, t as E_ACCESS_DENIED, u as E_UNSUPPORTED_RESPONSE_TYPE } from "./oauth_error-BOn0846g.js";
6
+ import { t as OAuthClient } from "./oauth_client-Cpppi0_7.js";
7
+ import "./token_service-BLKxKIHI.js";
8
+ import { n as OAuthGuard, t as OAuthLucidUserProvider } from "./user_provider-CQczt8_g.js";
10
9
  import { oauthGuard, oauthUserProvider } from "./src/guard/main.js";
11
- import { n as registerRoutes, t as registerProtectedResource } from "./routes-D6QCu0Pz.js";
10
+ import { n as registerRoutes, t as registerProtectedResource } from "./routes-qYJLHjeB.js";
12
11
  function defineConfig(config) {
13
12
  return {
14
13
  issuer: config.issuer,
@@ -18,10 +17,11 @@ function defineConfig(config) {
18
17
  accessTokenTtl: config.accessTokenTtl ?? "1h",
19
18
  refreshTokenTtl: config.refreshTokenTtl ?? "30d",
20
19
  authorizationCodeTtl: config.authorizationCodeTtl ?? "10m",
20
+ authorizationRequestTtl: config.authorizationRequestTtl ?? config.authorizationCodeTtl ?? "10m",
21
21
  loginPage: config.loginPage,
22
22
  consentPage: config.consentPage,
23
23
  allowDynamicRegistration: config.allowDynamicRegistration ?? false,
24
24
  allowPublicRegistration: config.allowPublicRegistration ?? false
25
25
  };
26
26
  }
27
- export { E_ACCESS_DENIED, E_INVALID_CLIENT, E_INVALID_CLIENT_METADATA, E_INVALID_GRANT, E_INVALID_REQUEST, E_INVALID_SCOPE, E_INVALID_TOKEN, E_SERVER_ERROR, E_UNSUPPORTED_GRANT_TYPE, E_UNSUPPORTED_RESPONSE_TYPE, OAuthAccessToken, OAuthAuthorizationCode, OAuthClient, OAuthConsent, OAuthError, OAuthGuard, OAuthLucidUserProvider, OAuthRefreshToken, SesameManager, configure, defineConfig, oauthGuard, oauthUserProvider, registerProtectedResource, registerRoutes, stubsRoot };
27
+ export { E_ACCESS_DENIED, E_INVALID_CLIENT, E_INVALID_CLIENT_METADATA, E_INVALID_GRANT, E_INVALID_REQUEST, E_INVALID_SCOPE, E_INVALID_TOKEN, E_SERVER_ERROR, E_UNSUPPORTED_GRANT_TYPE, E_UNSUPPORTED_RESPONSE_TYPE, OAuthAccessToken, OAuthAuthorizationCode, OAuthClient, OAuthConsent, OAuthError, OAuthGuard, OAuthLucidUserProvider, OAuthRefreshToken, SesameManager, configure, defineConfig, oauthGuard, oauthUserProvider, registerProtectedResource, registerRoutes };
@@ -1,10 +1,10 @@
1
- import "./decorate-2_8Ex77k.js";
2
- import { t as OAuthAccessToken } from "./oauth_access_token-BpG8sq-c.js";
3
- import { a as OAuthRefreshToken, t as SesameManager } from "./sesame_manager-B4tO2PLO.js";
4
- import { n as E_INVALID_CLIENT } from "./oauth_error-BQPqV-MV.js";
5
- import { t as OAuthClient } from "./oauth_client-eh0e5ql-.js";
6
- import { t as TokenService } from "./token_service-Czz9v5GI.js";
7
- import { t as ClientService } from "./client_service-B9fD3ZGe.js";
1
+ import "./decorate-BKZEjPRg.js";
2
+ import { t as OAuthAccessToken } from "./oauth_access_token-bsoM5KeU.js";
3
+ import { o as OAuthRefreshToken, t as SesameManager } from "./sesame_manager-CA7dOGwg.js";
4
+ import { n as E_INVALID_CLIENT } from "./oauth_error-BOn0846g.js";
5
+ import { t as OAuthClient } from "./oauth_client-Cpppi0_7.js";
6
+ import { t as TokenService } from "./token_service-BLKxKIHI.js";
7
+ import { t as ClientService } from "./client_service-DrkqOh37.js";
8
8
  const INACTIVE = { active: false };
9
9
  var IntrospectController = class {
10
10
  async handle(ctx) {
@@ -1,6 +1,6 @@
1
- import "./decorate-2_8Ex77k.js";
2
- import "./oauth_access_token-BpG8sq-c.js";
3
- import { t as SesameManager } from "./sesame_manager-B4tO2PLO.js";
1
+ import "./decorate-BKZEjPRg.js";
2
+ import "./oauth_access_token-bsoM5KeU.js";
3
+ import { t as SesameManager } from "./sesame_manager-CA7dOGwg.js";
4
4
  var MetadataController = class {
5
5
  async authServer(ctx) {
6
6
  const manager = await ctx.containerResolver.make(SesameManager);
@@ -1,4 +1,4 @@
1
- import { n as json, t as __decorate } from "./decorate-2_8Ex77k.js";
1
+ import { n as json, t as __decorate } from "./decorate-BKZEjPRg.js";
2
2
  import { BaseModel, column } from "@adonisjs/lucid/orm";
3
3
  var OAuthAccessToken = class extends BaseModel {
4
4
  static table = "oauth_access_tokens";
@@ -1,4 +1,4 @@
1
- import { n as json, t as __decorate } from "./decorate-2_8Ex77k.js";
1
+ import { n as json, t as __decorate } from "./decorate-BKZEjPRg.js";
2
2
  import { BaseModel, column } from "@adonisjs/lucid/orm";
3
3
  var OAuthClient = class extends BaseModel {
4
4
  static table = "oauth_clients";
@@ -1,6 +1,6 @@
1
- import "../decorate-2_8Ex77k.js";
2
- import "../oauth_access_token-BpG8sq-c.js";
3
- import { t as SesameManager } from "../sesame_manager-B4tO2PLO.js";
1
+ import "../decorate-BKZEjPRg.js";
2
+ import "../oauth_access_token-bsoM5KeU.js";
3
+ import { t as SesameManager } from "../sesame_manager-CA7dOGwg.js";
4
4
  var SesameProvider = class {
5
5
  constructor(app) {
6
6
  this.app = app;
@@ -12,7 +12,7 @@ var SesameProvider = class {
12
12
  }
13
13
  async boot() {
14
14
  const router = await this.app.container.make("router");
15
- const { registerRoutes } = await import("../routes-D6QCu0Pz.js").then((n) => n.r);
15
+ const { registerRoutes } = await import("../routes-qYJLHjeB.js").then((n) => n.r);
16
16
  registerRoutes(router);
17
17
  }
18
18
  };
@@ -1,9 +1,9 @@
1
- import "./decorate-2_8Ex77k.js";
2
- import "./oauth_access_token-BpG8sq-c.js";
3
- import { t as SesameManager } from "./sesame_manager-B4tO2PLO.js";
4
- import { a as E_INVALID_REQUEST, o as E_INVALID_SCOPE, r as E_INVALID_CLIENT_METADATA, t as E_ACCESS_DENIED } from "./oauth_error-BQPqV-MV.js";
5
- import { t as OAuthClient } from "./oauth_client-eh0e5ql-.js";
6
- import { t as ClientService } from "./client_service-B9fD3ZGe.js";
1
+ import "./decorate-BKZEjPRg.js";
2
+ import "./oauth_access_token-bsoM5KeU.js";
3
+ import { t as SesameManager } from "./sesame_manager-CA7dOGwg.js";
4
+ import { a as E_INVALID_REQUEST, o as E_INVALID_SCOPE, r as E_INVALID_CLIENT_METADATA, t as E_ACCESS_DENIED } from "./oauth_error-BOn0846g.js";
5
+ import { t as OAuthClient } from "./oauth_client-Cpppi0_7.js";
6
+ import { t as ClientService } from "./client_service-DrkqOh37.js";
7
7
  import vine from "@vinejs/vine";
8
8
  const DANGEROUS_SCHEMES = [
9
9
  "javascript:",
@@ -1,10 +1,10 @@
1
- import "./decorate-2_8Ex77k.js";
2
- import { t as OAuthAccessToken } from "./oauth_access_token-BpG8sq-c.js";
3
- import { a as OAuthRefreshToken, t as SesameManager } from "./sesame_manager-B4tO2PLO.js";
4
- import { n as E_INVALID_CLIENT } from "./oauth_error-BQPqV-MV.js";
5
- import { t as OAuthClient } from "./oauth_client-eh0e5ql-.js";
6
- import { t as TokenService } from "./token_service-Czz9v5GI.js";
7
- import { t as ClientService } from "./client_service-B9fD3ZGe.js";
1
+ import "./decorate-BKZEjPRg.js";
2
+ import { t as OAuthAccessToken } from "./oauth_access_token-bsoM5KeU.js";
3
+ import { o as OAuthRefreshToken, t as SesameManager } from "./sesame_manager-CA7dOGwg.js";
4
+ import { n as E_INVALID_CLIENT } from "./oauth_error-BOn0846g.js";
5
+ import { t as OAuthClient } from "./oauth_client-Cpppi0_7.js";
6
+ import { t as TokenService } from "./token_service-BLKxKIHI.js";
7
+ import { t as ClientService } from "./client_service-DrkqOh37.js";
8
8
  import { DateTime } from "luxon";
9
9
  var RevokeController = class {
10
10
  async handle(ctx) {
@@ -4,14 +4,14 @@ var routes_exports = /* @__PURE__ */ __exportAll({
4
4
  registerRoutes: () => registerRoutes
5
5
  });
6
6
  const controllers = {
7
- token: () => import("./token_controller-C9wh813f.js"),
8
- authorize: () => import("./authorize_controller-CUdEDNEi.js"),
9
- consent: () => import("./consent_controller-DFfx7qVs.js"),
10
- introspect: () => import("./introspect_controller-BzwfaUUE.js"),
11
- revoke: () => import("./revoke_controller-CNIgNKH3.js"),
12
- register: () => import("./register_controller-BA7uQAgt.js"),
13
- metadata: () => import("./metadata_controller-BSRRElQX.js"),
14
- clientInfo: () => import("./client_info_controller-DeIVcW8B.js")
7
+ token: () => import("./token_controller-CDFuzY6S.js"),
8
+ authorize: () => import("./authorize_controller-D3Kau97j.js"),
9
+ consent: () => import("./consent_controller-DXayMjAi.js"),
10
+ introspect: () => import("./introspect_controller-lK9gL03t.js"),
11
+ revoke: () => import("./revoke_controller-D0nl2hS5.js"),
12
+ register: () => import("./register_controller-B0saLAuc.js"),
13
+ metadata: () => import("./metadata_controller-DMAahJkn.js"),
14
+ clientInfo: () => import("./client_info_controller-CSipLLoV.js")
15
15
  };
16
16
  function registerRoutes(router) {
17
17
  router.post("/oauth/token", [controllers.token]).as("sesame.token");
@@ -28,7 +28,7 @@ function registerRoutes(router) {
28
28
  function registerProtectedResource(router, options) {
29
29
  const wellKnownPath = `/.well-known/oauth-protected-resource${options.resource}`;
30
30
  router.get(wellKnownPath, async (ctx) => {
31
- const { SesameManager } = await import("./sesame_manager-B4tO2PLO.js").then((n) => n.n);
31
+ const { SesameManager } = await import("./sesame_manager-CA7dOGwg.js").then((n) => n.n);
32
32
  const manager = await ctx.containerResolver.make(SesameManager);
33
33
  const issuer = manager.config.issuer;
34
34
  ctx.response.header("Cache-Control", "public, max-age=15, stale-while-revalidate=15, stale-if-error=86400");
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-BASaM9lw.js";
2
- import { n as json, t as __decorate } from "./decorate-2_8Ex77k.js";
3
- import { t as OAuthAccessToken } from "./oauth_access_token-BpG8sq-c.js";
2
+ import { n as json, t as __decorate } from "./decorate-BKZEjPRg.js";
3
+ import { t as OAuthAccessToken } from "./oauth_access_token-bsoM5KeU.js";
4
4
  import { DateTime } from "luxon";
5
5
  import { BaseModel, column } from "@adonisjs/lucid/orm";
6
6
  var OAuthRefreshToken = class extends BaseModel {
@@ -48,6 +48,20 @@ __decorate([column.dateTime({
48
48
  autoCreate: true,
49
49
  autoUpdate: true
50
50
  })], OAuthConsent.prototype, "updatedAt", void 0);
51
+ var OAuthPendingAuthorizationRequest = class extends BaseModel {
52
+ static table = "oauth_pending_authorization_requests";
53
+ };
54
+ __decorate([column({ isPrimary: true })], OAuthPendingAuthorizationRequest.prototype, "id", void 0);
55
+ __decorate([column()], OAuthPendingAuthorizationRequest.prototype, "token", void 0);
56
+ __decorate([column()], OAuthPendingAuthorizationRequest.prototype, "userId", void 0);
57
+ __decorate([column()], OAuthPendingAuthorizationRequest.prototype, "clientId", void 0);
58
+ __decorate([column()], OAuthPendingAuthorizationRequest.prototype, "redirectUri", void 0);
59
+ __decorate([json()], OAuthPendingAuthorizationRequest.prototype, "scopes", void 0);
60
+ __decorate([column()], OAuthPendingAuthorizationRequest.prototype, "state", void 0);
61
+ __decorate([column()], OAuthPendingAuthorizationRequest.prototype, "codeChallenge", void 0);
62
+ __decorate([column()], OAuthPendingAuthorizationRequest.prototype, "codeChallengeMethod", void 0);
63
+ __decorate([column.dateTime()], OAuthPendingAuthorizationRequest.prototype, "expiresAt", void 0);
64
+ __decorate([column.dateTime({ autoCreate: true })], OAuthPendingAuthorizationRequest.prototype, "createdAt", void 0);
51
65
  var sesame_manager_exports = /* @__PURE__ */ __exportAll({ SesameManager: () => SesameManager });
52
66
  var SesameManager = class {
53
67
  #config;
@@ -72,6 +86,7 @@ var SesameManager = class {
72
86
  await OAuthAccessToken.query().where("userId", userId).whereNull("revokedAt").update({ revokedAt: now.toSQL() });
73
87
  await OAuthRefreshToken.query().where("userId", userId).whereNull("revokedAt").update({ revokedAt: now.toSQL() });
74
88
  await OAuthAuthorizationCode.query().where("userId", userId).delete();
89
+ await OAuthPendingAuthorizationRequest.query().where("userId", userId).delete();
75
90
  await OAuthConsent.query().where("userId", userId).delete();
76
91
  }
77
92
  async purgeTokens(options) {
@@ -84,6 +99,7 @@ var SesameManager = class {
84
99
  let accessTokens = 0;
85
100
  let refreshTokens = 0;
86
101
  let authorizationCodes = 0;
102
+ let pendingRequests = 0;
87
103
  if (purgeRevoked) {
88
104
  accessTokens += await this.#deleteCount(OAuthAccessToken.query().whereNotNull("revokedAt").delete());
89
105
  refreshTokens += await this.#deleteCount(OAuthRefreshToken.query().whereNotNull("revokedAt").delete());
@@ -93,10 +109,12 @@ var SesameManager = class {
93
109
  refreshTokens += await this.#deleteCount(OAuthRefreshToken.query().where("expiresAt", "<", cutoff.toSQL()).whereNull("revokedAt").delete());
94
110
  authorizationCodes += await this.#deleteCount(OAuthAuthorizationCode.query().where("expiresAt", "<", cutoff.toSQL()).delete());
95
111
  }
112
+ pendingRequests += await this.#deleteCount(OAuthPendingAuthorizationRequest.query().where("expiresAt", "<", DateTime.now().toSQL()).delete());
96
113
  return {
97
114
  accessTokens,
98
115
  refreshTokens,
99
- authorizationCodes
116
+ authorizationCodes,
117
+ pendingRequests
100
118
  };
101
119
  }
102
120
  #deleteCount(result) {
@@ -113,4 +131,4 @@ var SesameManager = class {
113
131
  }[match[2]];
114
132
  }
115
133
  };
116
- export { OAuthRefreshToken as a, OAuthAuthorizationCode as i, sesame_manager_exports as n, OAuthConsent as r, SesameManager as t };
134
+ export { OAuthAuthorizationCode as a, OAuthConsent as i, sesame_manager_exports as n, OAuthRefreshToken as o, OAuthPendingAuthorizationRequest as r, SesameManager as t };
@@ -25,24 +25,24 @@ export default class AuthorizeController {
25
25
  code_challenge: import("@vinejs/vine/schema/base/literal").OptionalModifier<import("@vinejs/vine").VineString>;
26
26
  code_challenge_method: import("@vinejs/vine/schema/base/literal").OptionalModifier<import("@vinejs/vine").VineString>;
27
27
  }, {
28
- scope?: string | null | undefined;
29
28
  state?: string | null | undefined;
29
+ scope?: string | null | undefined;
30
30
  code_challenge?: string | null | undefined;
31
31
  code_challenge_method?: string | null | undefined;
32
32
  redirect_uri: string;
33
33
  client_id: string;
34
34
  response_type: string;
35
35
  }, {
36
- scope?: string | undefined;
37
36
  state?: string | undefined;
37
+ scope?: string | undefined;
38
38
  code_challenge?: string | undefined;
39
39
  code_challenge_method?: string | undefined;
40
40
  redirect_uri: string;
41
41
  client_id: string;
42
42
  response_type: string;
43
43
  }, {
44
- scope?: string | undefined;
45
44
  state?: string | undefined;
45
+ scope?: string | undefined;
46
46
  code_challenge?: string | undefined;
47
47
  code_challenge_method?: string | undefined;
48
48
  redirect_uri: string;
@@ -1,12 +1,12 @@
1
- import "../../decorate-2_8Ex77k.js";
2
- import "../../oauth_access_token-BpG8sq-c.js";
3
- import "../../oauth_client-eh0e5ql-.js";
4
- import "../../token_service-Czz9v5GI.js";
5
- import { n as OAuthGuard, t as OAuthLucidUserProvider } from "../../user_provider-B3rXEUT3.js";
1
+ import "../../decorate-BKZEjPRg.js";
2
+ import "../../oauth_access_token-bsoM5KeU.js";
3
+ import "../../oauth_client-Cpppi0_7.js";
4
+ import "../../token_service-BLKxKIHI.js";
5
+ import { n as OAuthGuard, t as OAuthLucidUserProvider } from "../../user_provider-CQczt8_g.js";
6
6
  function oauthGuard(config) {
7
7
  return { async resolver(name, app) {
8
8
  const emitter = await app.container.make("emitter");
9
- const { SesameManager } = await import("../../sesame_manager-B4tO2PLO.js").then((n) => n.n);
9
+ const { SesameManager } = await import("../../sesame_manager-CA7dOGwg.js").then((n) => n.n);
10
10
  const manager = await app.container.make(SesameManager);
11
11
  return (ctx) => new OAuthGuard(name, ctx, emitter, config.provider, manager, config.resource);
12
12
  } };
@@ -0,0 +1,29 @@
1
+ import { DateTime } from 'luxon';
2
+ import { BaseModel } from '@adonisjs/lucid/orm';
3
+ /**
4
+ * Temporary server-side record for an in-flight OAuth authorization
5
+ * request, created when the user is redirected to the consent page
6
+ * and consumed (deleted) when the user approves or denies.
7
+ *
8
+ * Stored in the database instead of the HTTP session to avoid
9
+ * last-write-wins race conditions when concurrent SPA requests
10
+ * overwrite session data.
11
+ *
12
+ * The `token` column stores a SHA-256 hash of the raw auth_token
13
+ * sent to the consent page, consistent with how authorization
14
+ * codes are stored.
15
+ */
16
+ export declare class OAuthPendingAuthorizationRequest extends BaseModel {
17
+ static table: string;
18
+ id: string;
19
+ token: string;
20
+ userId: string;
21
+ clientId: string;
22
+ redirectUri: string;
23
+ scopes: string[];
24
+ state: string | null;
25
+ codeChallenge: string | null;
26
+ codeChallengeMethod: string | null;
27
+ expiresAt: DateTime;
28
+ createdAt: DateTime;
29
+ }
@@ -3,6 +3,7 @@ export interface PurgeResult {
3
3
  accessTokens: number;
4
4
  refreshTokens: number;
5
5
  authorizationCodes: number;
6
+ pendingRequests: number;
6
7
  }
7
8
  /**
8
9
  * Central manager for the Sésame OAuth 2.1 server.
@@ -55,6 +55,12 @@ export interface SesameConfig {
55
55
  * Defaults to '10m'.
56
56
  */
57
57
  authorizationCodeTtl?: string;
58
+ /**
59
+ * Pending authorization request TTL as a string duration.
60
+ * Controls how long a user has to approve/deny a consent screen.
61
+ * Defaults to `authorizationCodeTtl` (typically '10m').
62
+ */
63
+ authorizationRequestTtl?: string;
58
64
  /**
59
65
  * Route or URL where unauthenticated users are redirected
60
66
  * to log in during the authorization flow. Can be a string
@@ -94,6 +100,7 @@ export interface ResolvedSesameConfig {
94
100
  accessTokenTtl: string;
95
101
  refreshTokenTtl: string;
96
102
  authorizationCodeTtl: string;
103
+ authorizationRequestTtl: string;
97
104
  loginPage: string | ((ctx: HttpContext, params: URLSearchParams) => string);
98
105
  consentPage: string | ((ctx: HttpContext, params: URLSearchParams) => string);
99
106
  allowDynamicRegistration: boolean;
@@ -1,5 +1,5 @@
1
1
  {{{
2
- exports: { defineConfig: 'import { defineConfig } from \'@adonisjs/sesame\'' }
2
+ exports: { defineConfig: 'import { defineConfig } from \'@julr/sesame\'' }
3
3
  }}}
4
4
  import env from '#start/env'
5
5
 
@@ -0,0 +1,32 @@
1
+ {{{
2
+ exports: { migration: "import { BaseSchema } from '@adonisjs/lucid/schema'" }
3
+ }}}
4
+
5
+ export default class extends BaseSchema {
6
+ protected tableName = 'oauth_pending_authorization_requests'
7
+
8
+ async up() {
9
+ this.schema.createTable(this.tableName, (table) => {
10
+ table.uuid('id').primary()
11
+ table.string('token').notNullable().index()
12
+ table
13
+ .string('client_id')
14
+ .notNullable()
15
+ .references('client_id')
16
+ .inTable('oauth_clients')
17
+ .onDelete('CASCADE')
18
+ table.string('user_id').notNullable()
19
+ table.json('scopes').notNullable()
20
+ table.text('redirect_uri').notNullable()
21
+ table.string('state').nullable()
22
+ table.string('code_challenge').nullable()
23
+ table.string('code_challenge_method').nullable()
24
+ table.timestamp('expires_at').notNullable()
25
+ table.timestamp('created_at').notNullable()
26
+ })
27
+ }
28
+
29
+ async down() {
30
+ this.schema.dropTable(this.tableName)
31
+ }
32
+ }
@@ -1,10 +1,10 @@
1
- import "./decorate-2_8Ex77k.js";
2
- import { t as OAuthAccessToken } from "./oauth_access_token-BpG8sq-c.js";
3
- import { a as OAuthRefreshToken, i as OAuthAuthorizationCode, t as SesameManager } from "./sesame_manager-B4tO2PLO.js";
4
- import { a as E_INVALID_REQUEST, i as E_INVALID_GRANT, l as E_UNSUPPORTED_GRANT_TYPE, n as E_INVALID_CLIENT, o as E_INVALID_SCOPE } from "./oauth_error-BQPqV-MV.js";
5
- import { t as OAuthClient } from "./oauth_client-eh0e5ql-.js";
6
- import { t as TokenService } from "./token_service-Czz9v5GI.js";
7
- import { t as ClientService } from "./client_service-B9fD3ZGe.js";
1
+ import "./decorate-BKZEjPRg.js";
2
+ import { t as OAuthAccessToken } from "./oauth_access_token-bsoM5KeU.js";
3
+ import { a as OAuthAuthorizationCode, o as OAuthRefreshToken, t as SesameManager } from "./sesame_manager-CA7dOGwg.js";
4
+ import { a as E_INVALID_REQUEST, i as E_INVALID_GRANT, l as E_UNSUPPORTED_GRANT_TYPE, n as E_INVALID_CLIENT, o as E_INVALID_SCOPE } from "./oauth_error-BOn0846g.js";
5
+ import { t as OAuthClient } from "./oauth_client-Cpppi0_7.js";
6
+ import { t as TokenService } from "./token_service-BLKxKIHI.js";
7
+ import { t as ClientService } from "./client_service-DrkqOh37.js";
8
8
  import { DateTime } from "luxon";
9
9
  import { createHash } from "node:crypto";
10
10
  import vine from "@vinejs/vine";
@@ -1,6 +1,6 @@
1
- import { t as OAuthAccessToken } from "./oauth_access_token-BpG8sq-c.js";
2
- import { t as OAuthClient } from "./oauth_client-eh0e5ql-.js";
3
- import { t as TokenService } from "./token_service-Czz9v5GI.js";
1
+ import { t as OAuthAccessToken } from "./oauth_access_token-bsoM5KeU.js";
2
+ import { t as OAuthClient } from "./oauth_client-Cpppi0_7.js";
3
+ import { t as TokenService } from "./token_service-BLKxKIHI.js";
4
4
  import { DateTime } from "luxon";
5
5
  import { RuntimeException } from "@adonisjs/core/exceptions";
6
6
  import { errors } from "@adonisjs/auth";
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.1.0",
4
+ "version": "0.2.0",
5
5
  "engines": {
6
6
  "node": ">=24.0.0"
7
7
  },
@@ -1,2 +0,0 @@
1
- const stubsRoot = import.meta.dirname;
2
- export { stubsRoot as t };