@julr/sesame 0.1.0 → 0.2.1
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 +167 -86
- package/build/{authorize_controller-CUdEDNEi.js → authorize_controller-BNWhlPZQ.js} +21 -20
- package/build/{client_info_controller-DeIVcW8B.js → client_info_controller-BucHGx4u.js} +3 -3
- package/build/{client_service-B9fD3ZGe.js → client_service-BqPSlaTS.js} +1 -1
- package/build/commands/sesame_purge.js +3 -3
- package/build/configure.js +11 -2
- package/build/{consent_controller-DFfx7qVs.js → consent_controller-COvvkpHM.js} +28 -30
- package/build/index.d.ts +1 -3
- package/build/index.js +9 -10
- package/build/{introspect_controller-BzwfaUUE.js → introspect_controller-JjAAXFIV.js} +7 -7
- package/build/metadata_controller-BzCjyqUG.js +73 -0
- package/build/{oauth_access_token-BpG8sq-c.js → oauth_access_token-bsoM5KeU.js} +1 -1
- package/build/{oauth_client-eh0e5ql-.js → oauth_client-BIoY5jBR.js} +1 -1
- package/build/{oauth_error-BQPqV-MV.js → oauth_error-CnJ3L8tf.js} +17 -1
- package/build/providers/sesame_provider.d.ts +0 -8
- package/build/providers/sesame_provider.js +3 -8
- package/build/{register_controller-BA7uQAgt.js → register_controller-DOlN9wNl.js} +6 -6
- package/build/{revoke_controller-CNIgNKH3.js → revoke_controller-B281b8ZO.js} +7 -7
- package/build/{sesame_manager-B4tO2PLO.js → sesame_manager-CFq4VEIZ.js} +75 -15
- package/build/src/controllers/metadata_controller.d.ts +1 -0
- package/build/src/define_config.d.ts +6 -1
- package/build/src/guard/guard.d.ts +4 -3
- package/build/src/guard/main.js +6 -6
- package/build/src/middleware/any_scope_middleware.d.ts +15 -0
- package/build/src/middleware/any_scope_middleware.js +18 -0
- package/build/src/middleware/scope_middleware.d.ts +15 -0
- package/build/src/middleware/scope_middleware.js +18 -0
- package/build/src/models/oauth_pending_authorization_request.d.ts +29 -0
- package/build/src/oauth_error.d.ts +32 -0
- package/build/src/routes.d.ts +21 -20
- package/build/src/sesame_manager.d.ts +39 -8
- package/build/src/types.d.ts +34 -0
- package/build/stubs/config/sesame.stub +6 -1
- package/build/stubs/migrations/create_oauth_pending_authorization_requests_table.stub +32 -0
- package/build/{token_controller-C9wh813f.js → token_controller-ll9sjcvn.js} +12 -11
- package/build/{token_service-Czz9v5GI.js → token_service-fhoA4slP.js} +2 -1
- package/build/{user_provider-B3rXEUT3.js → user_provider-DXAOfv8-.js} +3 -3
- package/package.json +7 -2
- package/build/main-kn40V-hF.js +0 -2
- package/build/metadata_controller-BSRRElQX.js +0 -51
- package/build/rolldown-runtime-BASaM9lw.js +0 -12
- package/build/routes-D6QCu0Pz.js +0 -43
- /package/build/{decorate-2_8Ex77k.js → decorate-BKZEjPRg.js} +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { t as SesameManager } from "./sesame_manager-CFq4VEIZ.js";
|
|
2
|
+
import "./decorate-BKZEjPRg.js";
|
|
3
|
+
import "./oauth_access_token-bsoM5KeU.js";
|
|
4
|
+
import { l as E_SERVER_ERROR } from "./oauth_error-CnJ3L8tf.js";
|
|
5
|
+
const DISCOVERY_ROUTE_NAMES = {
|
|
6
|
+
authorization_endpoint: "sesame.authorize",
|
|
7
|
+
token_endpoint: "sesame.token",
|
|
8
|
+
registration_endpoint: "sesame.register",
|
|
9
|
+
introspection_endpoint: "sesame.introspect",
|
|
10
|
+
revocation_endpoint: "sesame.revoke"
|
|
11
|
+
};
|
|
12
|
+
var MetadataController = class {
|
|
13
|
+
#assertDiscoveryRoutes(router, options) {
|
|
14
|
+
const requiredRoutes = [
|
|
15
|
+
DISCOVERY_ROUTE_NAMES.authorization_endpoint,
|
|
16
|
+
DISCOVERY_ROUTE_NAMES.token_endpoint,
|
|
17
|
+
DISCOVERY_ROUTE_NAMES.introspection_endpoint,
|
|
18
|
+
DISCOVERY_ROUTE_NAMES.revocation_endpoint
|
|
19
|
+
];
|
|
20
|
+
if (options.includeRegistration) requiredRoutes.push(DISCOVERY_ROUTE_NAMES.registration_endpoint);
|
|
21
|
+
const missingRoutes = requiredRoutes.filter((routeName) => !router.has(routeName));
|
|
22
|
+
if (missingRoutes.length > 0) throw new E_SERVER_ERROR(`OAuth discovery is misconfigured. Missing named route(s): ${missingRoutes.join(", ")}. Register OAuth routes with sesame.registerRoutes(router) before exposing well-known metadata.`);
|
|
23
|
+
}
|
|
24
|
+
async authServer(ctx) {
|
|
25
|
+
const manager = await ctx.containerResolver.make(SesameManager);
|
|
26
|
+
const router = await ctx.containerResolver.make("router");
|
|
27
|
+
const issuer = manager.config.issuer;
|
|
28
|
+
const prefixUrl = issuer;
|
|
29
|
+
this.#assertDiscoveryRoutes(router, { includeRegistration: manager.config.allowDynamicRegistration });
|
|
30
|
+
ctx.response.header("Cache-Control", "public, max-age=15, stale-while-revalidate=15, stale-if-error=86400");
|
|
31
|
+
return {
|
|
32
|
+
issuer,
|
|
33
|
+
authorization_endpoint: router.makeUrl("sesame.authorize", {}, { prefixUrl }),
|
|
34
|
+
token_endpoint: router.makeUrl("sesame.token", {}, { prefixUrl }),
|
|
35
|
+
registration_endpoint: manager.config.allowDynamicRegistration ? router.makeUrl("sesame.register", {}, { prefixUrl }) : void 0,
|
|
36
|
+
introspection_endpoint: router.makeUrl("sesame.introspect", {}, { prefixUrl }),
|
|
37
|
+
revocation_endpoint: router.makeUrl("sesame.revoke", {}, { prefixUrl }),
|
|
38
|
+
response_types_supported: ["code"],
|
|
39
|
+
response_modes_supported: ["query"],
|
|
40
|
+
grant_types_supported: manager.config.grantTypes,
|
|
41
|
+
token_endpoint_auth_methods_supported: [
|
|
42
|
+
"none",
|
|
43
|
+
"client_secret_basic",
|
|
44
|
+
"client_secret_post"
|
|
45
|
+
],
|
|
46
|
+
introspection_endpoint_auth_methods_supported: ["client_secret_basic", "client_secret_post"],
|
|
47
|
+
revocation_endpoint_auth_methods_supported: ["client_secret_basic", "client_secret_post"],
|
|
48
|
+
code_challenge_methods_supported: ["S256"],
|
|
49
|
+
authorization_response_iss_parameter_supported: true
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
async oidc(ctx) {
|
|
53
|
+
const base = await this.authServer(ctx);
|
|
54
|
+
const manager = await ctx.containerResolver.make(SesameManager);
|
|
55
|
+
return {
|
|
56
|
+
...base,
|
|
57
|
+
subject_types_supported: ["public"],
|
|
58
|
+
scopes_supported: Object.keys(manager.config.scopes)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async protectedResource(ctx) {
|
|
62
|
+
const manager = await ctx.containerResolver.make(SesameManager);
|
|
63
|
+
const issuer = manager.config.issuer;
|
|
64
|
+
ctx.response.header("Cache-Control", "public, max-age=15, stale-while-revalidate=15, stale-if-error=86400");
|
|
65
|
+
return {
|
|
66
|
+
resource: issuer,
|
|
67
|
+
authorization_servers: [issuer],
|
|
68
|
+
scopes_supported: Object.keys(manager.config.scopes),
|
|
69
|
+
bearer_methods_supported: ["header"]
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
export { MetadataController as default };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as json, t as __decorate } from "./decorate-
|
|
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-
|
|
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";
|
|
@@ -75,4 +75,20 @@ const E_SERVER_ERROR = class extends OAuthError {
|
|
|
75
75
|
static message = "Server error";
|
|
76
76
|
static oauthCode = "server_error";
|
|
77
77
|
};
|
|
78
|
-
|
|
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,10 +1,6 @@
|
|
|
1
1
|
import type { ApplicationService } from '@adonisjs/core/types';
|
|
2
2
|
/**
|
|
3
3
|
* AdonisJS service provider for the Sésame OAuth 2.1 server.
|
|
4
|
-
*
|
|
5
|
-
* - `register()`: Binds `SesameManager` as a singleton in the IoC
|
|
6
|
-
* container, reading the resolved config from `config/sesame.ts`.
|
|
7
|
-
* - `boot()`: Registers all OAuth routes on the AdonisJS router.
|
|
8
4
|
*/
|
|
9
5
|
export default class SesameProvider {
|
|
10
6
|
protected app: ApplicationService;
|
|
@@ -14,8 +10,4 @@ export default class SesameProvider {
|
|
|
14
10
|
* The manager is resolved from the `sesame` config key.
|
|
15
11
|
*/
|
|
16
12
|
register(): void;
|
|
17
|
-
/**
|
|
18
|
-
* Boot the provider by registering all OAuth 2.1 routes.
|
|
19
|
-
*/
|
|
20
|
-
boot(): Promise<void>;
|
|
21
13
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import "../
|
|
2
|
-
import "../
|
|
3
|
-
import
|
|
1
|
+
import { t as SesameManager } from "../sesame_manager-CFq4VEIZ.js";
|
|
2
|
+
import "../decorate-BKZEjPRg.js";
|
|
3
|
+
import "../oauth_access_token-bsoM5KeU.js";
|
|
4
4
|
var SesameProvider = class {
|
|
5
5
|
constructor(app) {
|
|
6
6
|
this.app = app;
|
|
@@ -10,10 +10,5 @@ var SesameProvider = class {
|
|
|
10
10
|
return new SesameManager(this.app.config.get("sesame"));
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
|
-
async boot() {
|
|
14
|
-
const router = await this.app.container.make("router");
|
|
15
|
-
const { registerRoutes } = await import("../routes-D6QCu0Pz.js").then((n) => n.r);
|
|
16
|
-
registerRoutes(router);
|
|
17
|
-
}
|
|
18
13
|
};
|
|
19
14
|
export { SesameProvider as default };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import "./
|
|
2
|
-
import "./
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { t as OAuthClient } from "./oauth_client-
|
|
6
|
-
import { t as ClientService } from "./client_service-
|
|
1
|
+
import { t as SesameManager } from "./sesame_manager-CFq4VEIZ.js";
|
|
2
|
+
import "./decorate-BKZEjPRg.js";
|
|
3
|
+
import "./oauth_access_token-bsoM5KeU.js";
|
|
4
|
+
import { i as E_INVALID_CLIENT_METADATA, o as E_INVALID_REQUEST, s as E_INVALID_SCOPE, t as E_ACCESS_DENIED } from "./oauth_error-CnJ3L8tf.js";
|
|
5
|
+
import { t as OAuthClient } from "./oauth_client-BIoY5jBR.js";
|
|
6
|
+
import { t as ClientService } from "./client_service-BqPSlaTS.js";
|
|
7
7
|
import vine from "@vinejs/vine";
|
|
8
8
|
const DANGEROUS_SCHEMES = [
|
|
9
9
|
"javascript:",
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import "./
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { t as OAuthClient } from "./oauth_client-
|
|
6
|
-
import { t as TokenService } from "./token_service-
|
|
7
|
-
import { t as ClientService } from "./client_service-
|
|
1
|
+
import { o as OAuthRefreshToken, t as SesameManager } from "./sesame_manager-CFq4VEIZ.js";
|
|
2
|
+
import "./decorate-BKZEjPRg.js";
|
|
3
|
+
import { t as OAuthAccessToken } from "./oauth_access_token-bsoM5KeU.js";
|
|
4
|
+
import { 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-BqPSlaTS.js";
|
|
8
8
|
import { DateTime } from "luxon";
|
|
9
9
|
var RevokeController = class {
|
|
10
10
|
async handle(ctx) {
|
|
@@ -1,8 +1,42 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import { n as json, t as __decorate } from "./decorate-BKZEjPRg.js";
|
|
2
|
+
import { t as OAuthAccessToken } from "./oauth_access_token-bsoM5KeU.js";
|
|
3
|
+
import "node:module";
|
|
4
4
|
import { DateTime } from "luxon";
|
|
5
5
|
import { BaseModel, column } from "@adonisjs/lucid/orm";
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __exportAll = (all, no_symbols) => {
|
|
8
|
+
let target = {};
|
|
9
|
+
for (var name in all) __defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true
|
|
12
|
+
});
|
|
13
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
14
|
+
return target;
|
|
15
|
+
};
|
|
16
|
+
const controllers = {
|
|
17
|
+
token: () => import("./token_controller-ll9sjcvn.js"),
|
|
18
|
+
authorize: () => import("./authorize_controller-BNWhlPZQ.js"),
|
|
19
|
+
consent: () => import("./consent_controller-COvvkpHM.js"),
|
|
20
|
+
introspect: () => import("./introspect_controller-JjAAXFIV.js"),
|
|
21
|
+
revoke: () => import("./revoke_controller-B281b8ZO.js"),
|
|
22
|
+
register: () => import("./register_controller-DOlN9wNl.js"),
|
|
23
|
+
metadata: () => import("./metadata_controller-BzCjyqUG.js"),
|
|
24
|
+
clientInfo: () => import("./client_info_controller-BucHGx4u.js")
|
|
25
|
+
};
|
|
26
|
+
function registerOAuthRoutes(router) {
|
|
27
|
+
router.post("/token", [controllers.token]).as("sesame.token");
|
|
28
|
+
router.get("/authorize", [controllers.authorize]).as("sesame.authorize");
|
|
29
|
+
router.post("/consent", [controllers.consent]).as("sesame.consent");
|
|
30
|
+
router.get("/client-info", [controllers.clientInfo]).as("sesame.clientInfo");
|
|
31
|
+
router.post("/introspect", [controllers.introspect]).as("sesame.introspect");
|
|
32
|
+
router.post("/revoke", [controllers.revoke]).as("sesame.revoke");
|
|
33
|
+
router.post("/register", [controllers.register]).as("sesame.register");
|
|
34
|
+
}
|
|
35
|
+
function registerWellKnownRoutes(router) {
|
|
36
|
+
router.get("/.well-known/oauth-authorization-server", [controllers.metadata, "authServer"]).as("sesame.metadata.authServer");
|
|
37
|
+
router.get("/.well-known/openid-configuration", [controllers.metadata, "oidc"]).as("sesame.metadata.oidc");
|
|
38
|
+
router.get("/.well-known/oauth-protected-resource", [controllers.metadata, "protectedResource"]).as("sesame.metadata.protectedResource");
|
|
39
|
+
}
|
|
6
40
|
var OAuthRefreshToken = class extends BaseModel {
|
|
7
41
|
static table = "oauth_refresh_tokens";
|
|
8
42
|
};
|
|
@@ -48,6 +82,20 @@ __decorate([column.dateTime({
|
|
|
48
82
|
autoCreate: true,
|
|
49
83
|
autoUpdate: true
|
|
50
84
|
})], OAuthConsent.prototype, "updatedAt", void 0);
|
|
85
|
+
var OAuthPendingAuthorizationRequest = class extends BaseModel {
|
|
86
|
+
static table = "oauth_pending_authorization_requests";
|
|
87
|
+
};
|
|
88
|
+
__decorate([column({ isPrimary: true })], OAuthPendingAuthorizationRequest.prototype, "id", void 0);
|
|
89
|
+
__decorate([column()], OAuthPendingAuthorizationRequest.prototype, "token", void 0);
|
|
90
|
+
__decorate([column()], OAuthPendingAuthorizationRequest.prototype, "userId", void 0);
|
|
91
|
+
__decorate([column()], OAuthPendingAuthorizationRequest.prototype, "clientId", void 0);
|
|
92
|
+
__decorate([column()], OAuthPendingAuthorizationRequest.prototype, "redirectUri", void 0);
|
|
93
|
+
__decorate([json()], OAuthPendingAuthorizationRequest.prototype, "scopes", void 0);
|
|
94
|
+
__decorate([column()], OAuthPendingAuthorizationRequest.prototype, "state", void 0);
|
|
95
|
+
__decorate([column()], OAuthPendingAuthorizationRequest.prototype, "codeChallenge", void 0);
|
|
96
|
+
__decorate([column()], OAuthPendingAuthorizationRequest.prototype, "codeChallengeMethod", void 0);
|
|
97
|
+
__decorate([column.dateTime()], OAuthPendingAuthorizationRequest.prototype, "expiresAt", void 0);
|
|
98
|
+
__decorate([column.dateTime({ autoCreate: true })], OAuthPendingAuthorizationRequest.prototype, "createdAt", void 0);
|
|
51
99
|
var sesame_manager_exports = /* @__PURE__ */ __exportAll({ SesameManager: () => SesameManager });
|
|
52
100
|
var SesameManager = class {
|
|
53
101
|
#config;
|
|
@@ -72,6 +120,7 @@ var SesameManager = class {
|
|
|
72
120
|
await OAuthAccessToken.query().where("userId", userId).whereNull("revokedAt").update({ revokedAt: now.toSQL() });
|
|
73
121
|
await OAuthRefreshToken.query().where("userId", userId).whereNull("revokedAt").update({ revokedAt: now.toSQL() });
|
|
74
122
|
await OAuthAuthorizationCode.query().where("userId", userId).delete();
|
|
123
|
+
await OAuthPendingAuthorizationRequest.query().where("userId", userId).delete();
|
|
75
124
|
await OAuthConsent.query().where("userId", userId).delete();
|
|
76
125
|
}
|
|
77
126
|
async purgeTokens(options) {
|
|
@@ -84,6 +133,7 @@ var SesameManager = class {
|
|
|
84
133
|
let accessTokens = 0;
|
|
85
134
|
let refreshTokens = 0;
|
|
86
135
|
let authorizationCodes = 0;
|
|
136
|
+
let pendingRequests = 0;
|
|
87
137
|
if (purgeRevoked) {
|
|
88
138
|
accessTokens += await this.#deleteCount(OAuthAccessToken.query().whereNotNull("revokedAt").delete());
|
|
89
139
|
refreshTokens += await this.#deleteCount(OAuthRefreshToken.query().whereNotNull("revokedAt").delete());
|
|
@@ -93,24 +143,34 @@ var SesameManager = class {
|
|
|
93
143
|
refreshTokens += await this.#deleteCount(OAuthRefreshToken.query().where("expiresAt", "<", cutoff.toSQL()).whereNull("revokedAt").delete());
|
|
94
144
|
authorizationCodes += await this.#deleteCount(OAuthAuthorizationCode.query().where("expiresAt", "<", cutoff.toSQL()).delete());
|
|
95
145
|
}
|
|
146
|
+
pendingRequests += await this.#deleteCount(OAuthPendingAuthorizationRequest.query().where("expiresAt", "<", DateTime.now().toSQL()).delete());
|
|
96
147
|
return {
|
|
97
148
|
accessTokens,
|
|
98
149
|
refreshTokens,
|
|
99
|
-
authorizationCodes
|
|
150
|
+
authorizationCodes,
|
|
151
|
+
pendingRequests
|
|
100
152
|
};
|
|
101
153
|
}
|
|
154
|
+
registerRoutes(router) {
|
|
155
|
+
registerOAuthRoutes(router);
|
|
156
|
+
}
|
|
157
|
+
registerWellKnownRoutes(router) {
|
|
158
|
+
registerWellKnownRoutes(router);
|
|
159
|
+
}
|
|
160
|
+
registerProtectedResource(router, options) {
|
|
161
|
+
const wellKnownPath = `/.well-known/oauth-protected-resource${options.resource}`;
|
|
162
|
+
router.get(wellKnownPath, async (ctx) => {
|
|
163
|
+
ctx.response.header("Cache-Control", "public, max-age=15, stale-while-revalidate=15, stale-if-error=86400");
|
|
164
|
+
return {
|
|
165
|
+
resource: `${this.#config.issuer}${options.resource}`,
|
|
166
|
+
authorization_servers: [this.#config.issuer],
|
|
167
|
+
scopes_supported: options.scopes ?? Object.keys(this.#config.scopes),
|
|
168
|
+
bearer_methods_supported: ["header"]
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
}
|
|
102
172
|
#deleteCount(result) {
|
|
103
173
|
return result.then((r) => Array.isArray(r) ? Number(r[0] ?? 0) : Number(r));
|
|
104
174
|
}
|
|
105
|
-
parseTtl(ttl) {
|
|
106
|
-
const match = ttl.match(/^(\d+)(s|m|h|d)$/);
|
|
107
|
-
if (!match) throw new Error(`Invalid TTL format: ${ttl}`);
|
|
108
|
-
return Number.parseInt(match[1], 10) * {
|
|
109
|
-
s: 1,
|
|
110
|
-
m: 60,
|
|
111
|
-
h: 3600,
|
|
112
|
-
d: 86400
|
|
113
|
-
}[match[2]];
|
|
114
|
-
}
|
|
115
175
|
};
|
|
116
|
-
export {
|
|
176
|
+
export { OAuthAuthorizationCode as a, OAuthConsent as i, sesame_manager_exports as n, OAuthRefreshToken as o, OAuthPendingAuthorizationRequest as r, SesameManager as t };
|
|
@@ -13,6 +13,7 @@ import type { AuthServerMetadata, ResourceServerMetadata } from '../types.ts';
|
|
|
13
13
|
* @see https://openid.net/specs/openid-connect-discovery-1_0.html
|
|
14
14
|
*/
|
|
15
15
|
export default class MetadataController {
|
|
16
|
+
#private;
|
|
16
17
|
/**
|
|
17
18
|
* OAuth 2.0 Authorization Server Metadata (RFC 8414).
|
|
18
19
|
*
|
|
@@ -13,4 +13,9 @@ import type { SesameConfig, ResolvedSesameConfig } from './types.ts';
|
|
|
13
13
|
* - `allowDynamicRegistration`: `false`
|
|
14
14
|
* - `allowPublicRegistration`: `false`
|
|
15
15
|
*/
|
|
16
|
-
export declare function defineConfig(config: SesameConfig
|
|
16
|
+
export declare function defineConfig<const TScopes extends Record<string, string>>(config: Omit<SesameConfig, 'scopes' | 'defaultScopes'> & {
|
|
17
|
+
scopes?: TScopes;
|
|
18
|
+
defaultScopes?: Array<Extract<keyof TScopes, string>>;
|
|
19
|
+
}): Omit<ResolvedSesameConfig, 'scopes'> & {
|
|
20
|
+
scopes: TScopes;
|
|
21
|
+
};
|
|
@@ -2,6 +2,7 @@ import type { HttpContext } from '@adonisjs/core/http';
|
|
|
2
2
|
import type { EmitterLike } from '@adonisjs/core/types/events';
|
|
3
3
|
import { symbols } from '@adonisjs/auth';
|
|
4
4
|
import type { AuthClientResponse, GuardContract } from '@adonisjs/auth/types';
|
|
5
|
+
import type { Scope } from '../types.ts';
|
|
5
6
|
import type { SesameManager } from '../sesame_manager.ts';
|
|
6
7
|
import type { OAuthGuardEvents, OAuthUserProviderContract } from './types.ts';
|
|
7
8
|
/**
|
|
@@ -18,13 +19,13 @@ export declare class OAuthGuard<UserProvider extends OAuthUserProviderContract<u
|
|
|
18
19
|
authenticationAttempted: boolean;
|
|
19
20
|
isAuthenticated: boolean;
|
|
20
21
|
user?: UserProvider[typeof symbols.PROVIDER_REAL_USER];
|
|
21
|
-
scopes:
|
|
22
|
+
scopes: Scope[];
|
|
22
23
|
clientId?: string;
|
|
23
24
|
constructor(name: string, ctx: HttpContext, emitter: EmitterLike<OAuthGuardEvents<UserProvider[typeof symbols.PROVIDER_REAL_USER]>>, userProvider: UserProvider, manager: SesameManager, resource?: string);
|
|
24
25
|
getUserOrFail(): UserProvider[typeof symbols.PROVIDER_REAL_USER];
|
|
25
26
|
authenticate(): Promise<UserProvider[typeof symbols.PROVIDER_REAL_USER]>;
|
|
26
27
|
check(): Promise<boolean>;
|
|
27
|
-
hasScope(...scopes:
|
|
28
|
-
hasAnyScope(...scopes:
|
|
28
|
+
hasScope(...scopes: Scope[]): boolean;
|
|
29
|
+
hasAnyScope(...scopes: Scope[]): boolean;
|
|
29
30
|
authenticateAsClient(user: UserProvider[typeof symbols.PROVIDER_REAL_USER]): Promise<AuthClientResponse>;
|
|
30
31
|
}
|
package/build/src/guard/main.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import "../../decorate-
|
|
2
|
-
import "../../oauth_access_token-
|
|
3
|
-
import "../../oauth_client-
|
|
4
|
-
import "../../token_service-
|
|
5
|
-
import { n as OAuthGuard, t as OAuthLucidUserProvider } from "../../user_provider-
|
|
1
|
+
import "../../decorate-BKZEjPRg.js";
|
|
2
|
+
import "../../oauth_access_token-bsoM5KeU.js";
|
|
3
|
+
import "../../oauth_client-BIoY5jBR.js";
|
|
4
|
+
import "../../token_service-fhoA4slP.js";
|
|
5
|
+
import { n as OAuthGuard, t as OAuthLucidUserProvider } from "../../user_provider-DXAOfv8-.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-
|
|
9
|
+
const { SesameManager } = await import("../../sesame_manager-CFq4VEIZ.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,15 @@
|
|
|
1
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
2
|
+
import type { NextFn } from '@adonisjs/core/types/http';
|
|
3
|
+
import type { Scope } from '../types.ts';
|
|
4
|
+
/**
|
|
5
|
+
* Any-scope middleware requires ANY of the listed scopes on the
|
|
6
|
+
* authenticated OAuth token. Throws 403 if none match.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* router.get('/data', [DataController]).use(middleware.anyScope({ scopes: ['read', 'read-all'] }))
|
|
10
|
+
*/
|
|
11
|
+
export default class AnyScopeMiddleware {
|
|
12
|
+
handle(ctx: HttpContext, next: NextFn, options: {
|
|
13
|
+
scopes: Scope[];
|
|
14
|
+
}): Promise<any>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { n as E_INSUFFICIENT_SCOPE } from "../../oauth_error-CnJ3L8tf.js";
|
|
2
|
+
var AnyScopeMiddleware = class {
|
|
3
|
+
async handle(ctx, next, options) {
|
|
4
|
+
const guard = ctx.auth.use("oauth");
|
|
5
|
+
if (guard.isAuthenticated) {
|
|
6
|
+
if (!guard.hasAnyScope(...options.scopes)) throw new E_INSUFFICIENT_SCOPE(options.scopes);
|
|
7
|
+
return next();
|
|
8
|
+
}
|
|
9
|
+
if (ctx.request.header("authorization")) {
|
|
10
|
+
await guard.authenticate();
|
|
11
|
+
if (!guard.hasAnyScope(...options.scopes)) throw new E_INSUFFICIENT_SCOPE(options.scopes);
|
|
12
|
+
return next();
|
|
13
|
+
}
|
|
14
|
+
if (await ctx.auth.check()) return next();
|
|
15
|
+
await guard.authenticate();
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
export { AnyScopeMiddleware as default };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
2
|
+
import type { NextFn } from '@adonisjs/core/types/http';
|
|
3
|
+
import type { Scope } from '../types.ts';
|
|
4
|
+
/**
|
|
5
|
+
* Scope middleware requires ALL listed scopes on the authenticated
|
|
6
|
+
* OAuth token. Throws 403 if any scope is missing.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* router.get('/admin', [AdminController]).use(middleware.scopes({ scopes: ['admin', 'manage'] }))
|
|
10
|
+
*/
|
|
11
|
+
export default class ScopeMiddleware {
|
|
12
|
+
handle(ctx: HttpContext, next: NextFn, options: {
|
|
13
|
+
scopes: Scope[];
|
|
14
|
+
}): Promise<any>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { n as E_INSUFFICIENT_SCOPE } from "../../oauth_error-CnJ3L8tf.js";
|
|
2
|
+
var ScopeMiddleware = class {
|
|
3
|
+
async handle(ctx, next, options) {
|
|
4
|
+
const guard = ctx.auth.use("oauth");
|
|
5
|
+
if (guard.isAuthenticated) {
|
|
6
|
+
if (!guard.hasScope(...options.scopes)) throw new E_INSUFFICIENT_SCOPE(options.scopes);
|
|
7
|
+
return next();
|
|
8
|
+
}
|
|
9
|
+
if (ctx.request.header("authorization")) {
|
|
10
|
+
await guard.authenticate();
|
|
11
|
+
if (!guard.hasScope(...options.scopes)) throw new E_INSUFFICIENT_SCOPE(options.scopes);
|
|
12
|
+
return next();
|
|
13
|
+
}
|
|
14
|
+
if (await ctx.auth.check()) return next();
|
|
15
|
+
await guard.authenticate();
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
export { ScopeMiddleware as default };
|
|
@@ -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
|
+
}
|
|
@@ -357,3 +357,35 @@ export declare const E_SERVER_ERROR: {
|
|
|
357
357
|
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
358
358
|
stackTraceLimit: number;
|
|
359
359
|
};
|
|
360
|
+
/**
|
|
361
|
+
* The access token does not have the required scope(s) to access
|
|
362
|
+
* the protected resource. Returns 403 with a WWW-Authenticate header
|
|
363
|
+
* per RFC 6750 §3.1.
|
|
364
|
+
*
|
|
365
|
+
* @see https://datatracker.ietf.org/doc/html/rfc6750#section-3.1
|
|
366
|
+
*/
|
|
367
|
+
export declare const E_INSUFFICIENT_SCOPE: {
|
|
368
|
+
new (missingScopes: string[], message?: string): {
|
|
369
|
+
missingScopes: string[];
|
|
370
|
+
handle(error: /*elided*/ any, ctx: HttpContext): void;
|
|
371
|
+
get oauthCode(): string;
|
|
372
|
+
name: string;
|
|
373
|
+
help?: string;
|
|
374
|
+
code?: string;
|
|
375
|
+
status: number;
|
|
376
|
+
toString(): string;
|
|
377
|
+
get [Symbol.toStringTag](): string;
|
|
378
|
+
message: string;
|
|
379
|
+
stack?: string;
|
|
380
|
+
cause?: unknown;
|
|
381
|
+
};
|
|
382
|
+
readonly status: number;
|
|
383
|
+
readonly code: string;
|
|
384
|
+
readonly message: string;
|
|
385
|
+
readonly oauthCode: string;
|
|
386
|
+
help?: string;
|
|
387
|
+
isError(error: unknown): error is Error;
|
|
388
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
389
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
390
|
+
stackTraceLimit: number;
|
|
391
|
+
};
|
package/build/src/routes.d.ts
CHANGED
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
import { Router } from '@adonisjs/core/http';
|
|
2
2
|
/**
|
|
3
|
-
* Register
|
|
3
|
+
* Register OAuth 2.1 endpoint routes on the given router.
|
|
4
|
+
*
|
|
5
|
+
* Paths are relative (no prefix) — the user wraps the call
|
|
6
|
+
* in a `router.group().prefix('/oauth')` to control the mount point.
|
|
4
7
|
*
|
|
5
8
|
* Endpoints registered:
|
|
6
|
-
* - `POST /
|
|
7
|
-
* - `GET /
|
|
8
|
-
* - `POST /
|
|
9
|
-
* - `POST /
|
|
10
|
-
* - `POST /
|
|
11
|
-
* - `POST /
|
|
12
|
-
* - `GET /
|
|
13
|
-
* - `GET /.well-known/oauth-authorization-server` — Server metadata (RFC 8414)
|
|
14
|
-
* - `GET /.well-known/openid-configuration` — OpenID Connect discovery
|
|
15
|
-
* - `GET /.well-known/oauth-protected-resource` — Protected resource metadata (RFC 9728)
|
|
9
|
+
* - `POST /token` — Token endpoint (RFC 6749 §3.2)
|
|
10
|
+
* - `GET /authorize` — Authorization endpoint (RFC 6749 §3.1)
|
|
11
|
+
* - `POST /consent` — User consent submission
|
|
12
|
+
* - `POST /introspect` — Token introspection (RFC 7662)
|
|
13
|
+
* - `POST /revoke` — Token revocation (RFC 7009)
|
|
14
|
+
* - `POST /register` — Dynamic client registration (RFC 7591)
|
|
15
|
+
* - `GET /client-info` — Public client information (RFC 6819 §4.4.1.4)
|
|
16
16
|
*/
|
|
17
|
-
export declare function
|
|
17
|
+
export declare function registerOAuthRoutes(router: Router): void;
|
|
18
18
|
/**
|
|
19
|
-
* Register
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* Register well-known discovery routes at the root level.
|
|
20
|
+
*
|
|
21
|
+
* These must be registered outside any prefix group so they
|
|
22
|
+
* remain at `/.well-known/...`.
|
|
22
23
|
*
|
|
23
|
-
*
|
|
24
|
+
* Endpoints registered:
|
|
25
|
+
* - `GET /.well-known/oauth-authorization-server` — Server metadata (RFC 8414)
|
|
26
|
+
* - `GET /.well-known/openid-configuration` — OpenID Connect discovery
|
|
27
|
+
* - `GET /.well-known/oauth-protected-resource` — Protected resource metadata (RFC 9728)
|
|
24
28
|
*/
|
|
25
|
-
export declare function
|
|
26
|
-
resource: string;
|
|
27
|
-
scopes?: string[];
|
|
28
|
-
}): void;
|
|
29
|
+
export declare function registerWellKnownRoutes(router: Router): void;
|