@open-rap/runtime 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +14 -2
- package/dist/abap/background.d.ts +14 -0
- package/dist/abap/background.js +130 -0
- package/dist/abap/background.js.map +1 -0
- package/dist/abap/bridge.d.ts +62 -0
- package/dist/abap/bridge.js +402 -0
- package/dist/abap/bridge.js.map +1 -0
- package/dist/abap/convert.d.ts +26 -0
- package/dist/abap/convert.js +197 -0
- package/dist/abap/convert.js.map +1 -0
- package/dist/abap/http.d.ts +34 -0
- package/dist/abap/http.js +133 -0
- package/dist/abap/http.js.map +1 -0
- package/dist/abap/mail.d.ts +10 -0
- package/dist/abap/mail.js +83 -0
- package/dist/abap/mail.js.map +1 -0
- package/dist/abap/odata-client.d.ts +33 -0
- package/dist/abap/odata-client.js +576 -0
- package/dist/abap/odata-client.js.map +1 -0
- package/dist/abap/sql.d.ts +92 -0
- package/dist/abap/sql.js +261 -0
- package/dist/abap/sql.js.map +1 -0
- package/dist/abap/utils.d.ts +41 -0
- package/dist/abap/utils.js +976 -0
- package/dist/abap/utils.js.map +1 -0
- package/dist/app.d.ts +53 -0
- package/dist/app.js +169 -0
- package/dist/app.js.map +1 -0
- package/dist/auth/auth.d.ts +68 -0
- package/dist/auth/auth.js +140 -0
- package/dist/auth/auth.js.map +1 -0
- package/dist/auth/authorizations.d.ts +39 -0
- package/dist/auth/authorizations.js +103 -0
- package/dist/auth/authorizations.js.map +1 -0
- package/dist/auth/business-users.d.ts +15 -0
- package/dist/auth/business-users.js +36 -0
- package/dist/auth/business-users.js.map +1 -0
- package/dist/auth/dev-oauth.d.ts +38 -0
- package/dist/auth/dev-oauth.js +156 -0
- package/dist/auth/dev-oauth.js.map +1 -0
- package/dist/auth/environment.d.ts +7 -0
- package/dist/auth/environment.js +57 -0
- package/dist/auth/environment.js.map +1 -0
- package/dist/auth/session.d.ts +45 -0
- package/dist/auth/session.js +257 -0
- package/dist/auth/session.js.map +1 -0
- package/dist/auth/user.d.ts +21 -0
- package/dist/auth/user.js +12 -0
- package/dist/auth/user.js.map +1 -0
- package/dist/db/database.d.ts +30 -0
- package/dist/db/database.js +131 -0
- package/dist/db/database.js.map +1 -0
- package/dist/db/seed.d.ts +9 -0
- package/dist/db/seed.js +63 -0
- package/dist/db/seed.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/launchpad/dev.d.ts +14 -0
- package/dist/launchpad/dev.js +48 -0
- package/dist/launchpad/dev.js.map +1 -0
- package/dist/launchpad/proxy.d.ts +30 -0
- package/dist/launchpad/proxy.js +98 -0
- package/dist/launchpad/proxy.js.map +1 -0
- package/dist/launchpad/server.d.ts +77 -0
- package/dist/launchpad/server.js +177 -0
- package/dist/launchpad/server.js.map +1 -0
- package/dist/mcp/server.d.ts +22 -0
- package/dist/mcp/server.js +222 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/odata/access.d.ts +31 -0
- package/dist/odata/access.js +142 -0
- package/dist/odata/access.js.map +1 -0
- package/dist/odata/batch.d.ts +36 -0
- package/dist/odata/batch.js +147 -0
- package/dist/odata/batch.js.map +1 -0
- package/dist/odata/csrf.d.ts +13 -0
- package/dist/odata/csrf.js +27 -0
- package/dist/odata/csrf.js.map +1 -0
- package/dist/odata/custom.d.ts +90 -0
- package/dist/odata/custom.js +373 -0
- package/dist/odata/custom.js.map +1 -0
- package/dist/odata/draft.d.ts +78 -0
- package/dist/odata/draft.js +590 -0
- package/dist/odata/draft.js.map +1 -0
- package/dist/odata/errors.d.ts +27 -0
- package/dist/odata/errors.js +41 -0
- package/dist/odata/errors.js.map +1 -0
- package/dist/odata/handler.d.ts +90 -0
- package/dist/odata/handler.js +312 -0
- package/dist/odata/handler.js.map +1 -0
- package/dist/odata/json.d.ts +23 -0
- package/dist/odata/json.js +85 -0
- package/dist/odata/json.js.map +1 -0
- package/dist/odata/read.d.ts +90 -0
- package/dist/odata/read.js +686 -0
- package/dist/odata/read.js.map +1 -0
- package/dist/odata/service.d.ts +45 -0
- package/dist/odata/service.js +145 -0
- package/dist/odata/service.js.map +1 -0
- package/dist/odata/uri/ast.d.ts +103 -0
- package/dist/odata/uri/ast.js +3 -0
- package/dist/odata/uri/ast.js.map +1 -0
- package/dist/odata/uri/parser.d.ts +26 -0
- package/dist/odata/uri/parser.js +697 -0
- package/dist/odata/uri/parser.js.map +1 -0
- package/dist/odata/write.d.ts +127 -0
- package/dist/odata/write.js +799 -0
- package/dist/odata/write.js.map +1 -0
- package/dist/platform.d.ts +14 -0
- package/dist/platform.js +66 -0
- package/dist/platform.js.map +1 -0
- package/dist/rap/bo.d.ts +76 -0
- package/dist/rap/bo.js +184 -0
- package/dist/rap/bo.js.map +1 -0
- package/dist/rap/kernel.d.ts +7 -0
- package/dist/rap/kernel.js +287 -0
- package/dist/rap/kernel.js.map +1 -0
- package/dist/rap/runtime.d.ts +84 -0
- package/dist/rap/runtime.js +128 -0
- package/dist/rap/runtime.js.map +1 -0
- package/dist/rap/transaction.d.ts +227 -0
- package/dist/rap/transaction.js +1385 -0
- package/dist/rap/transaction.js.map +1 -0
- package/dist/rap/types.d.ts +74 -0
- package/dist/rap/types.js +25 -0
- package/dist/rap/types.js.map +1 -0
- package/dist/server.d.ts +34 -0
- package/dist/server.js +114 -0
- package/dist/server.js.map +1 -0
- package/dist/ui/plugin.d.ts +14 -0
- package/dist/ui/plugin.js +47 -0
- package/dist/ui/plugin.js.map +1 -0
- package/dist/ui/resources.d.ts +22 -0
- package/dist/ui/resources.js +88 -0
- package/dist/ui/resources.js.map +1 -0
- package/dist/ui/variants.d.ts +25 -0
- package/dist/ui/variants.js +68 -0
- package/dist/ui/variants.js.map +1 -0
- package/dist/values.d.ts +43 -0
- package/dist/values.js +367 -0
- package/dist/values.js.map +1 -0
- package/package.json +60 -3
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The authorizations of the users (decision D34): the roles of the identity provider (claims of
|
|
3
|
+
* the token) give authorizations, instances of authorization objects with field values, defined
|
|
4
|
+
* in rapt/authorizations.json — the counterpart of the business roles of the ABAP environment.
|
|
5
|
+
* AUTHORITY-CHECK and the access controls (pfcg_auth) read them.
|
|
6
|
+
*/
|
|
7
|
+
import { authorizationClaims } from "@open-rap/model";
|
|
8
|
+
const CLAIM = /^\{claim:([^}]+)\}$/;
|
|
9
|
+
/** A value of an authorization field against an accepted value of a role (PFCG semantics). */
|
|
10
|
+
export function valueMatches(value, accepted) {
|
|
11
|
+
if (typeof accepted !== "string") {
|
|
12
|
+
return value >= accepted.from && value <= accepted.to;
|
|
13
|
+
}
|
|
14
|
+
if (accepted === "*") {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
if (accepted.endsWith("*")) {
|
|
18
|
+
return value.startsWith(accepted.slice(0, -1));
|
|
19
|
+
}
|
|
20
|
+
return value === accepted;
|
|
21
|
+
}
|
|
22
|
+
/** The roles of rapt/authorizations.json (`roles`: role name → authorizations) as a model. */
|
|
23
|
+
export function rolesFromJson(json) {
|
|
24
|
+
const roles = json?.roles;
|
|
25
|
+
if (typeof roles !== "object" || roles === null || Array.isArray(roles)) {
|
|
26
|
+
throw new Error('the authorizations file needs "roles": role name → { "authorizations": [...] }');
|
|
27
|
+
}
|
|
28
|
+
return Object.entries(roles).map(([name, role]) => {
|
|
29
|
+
if (!Array.isArray(role?.authorizations)) {
|
|
30
|
+
throw new Error(`roles.${name}: "authorizations" must be a list`);
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
name,
|
|
34
|
+
...(typeof role.description === "string" ? { description: role.description } : {}),
|
|
35
|
+
authorizations: role.authorizations.map((a) => ({
|
|
36
|
+
object: String(a.object).toUpperCase(),
|
|
37
|
+
fields: Object.fromEntries(Object.entries(a.fields ?? {}).map(([field, values]) => [field.toUpperCase(), (Array.isArray(values) ? values : [values])])),
|
|
38
|
+
})),
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
export class Authorizations {
|
|
43
|
+
roles;
|
|
44
|
+
constructor(model) {
|
|
45
|
+
this.roles = model?.roles ?? [];
|
|
46
|
+
}
|
|
47
|
+
/** Whether roles are defined (else AUTHORITY-CHECK fails for every user: no authorization). */
|
|
48
|
+
get defined() {
|
|
49
|
+
return this.roles.length > 0;
|
|
50
|
+
}
|
|
51
|
+
/** The claims of the token that values of the roles read (`{claim:agency}`). */
|
|
52
|
+
claims() {
|
|
53
|
+
return authorizationClaims({ roles: this.roles });
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The development user without DEV_ROLES (`*`) has every authorization, like SAP_ALL in a
|
|
57
|
+
* development system: every role, and AUTHORITY-CHECK passes for every object.
|
|
58
|
+
*/
|
|
59
|
+
isUnrestricted(user) {
|
|
60
|
+
return user.authenticatedBy === "development" && user.roles.includes("*");
|
|
61
|
+
}
|
|
62
|
+
/** The roles of a user: those of its token; all of them for the unrestricted development user. */
|
|
63
|
+
rolesOf(user) {
|
|
64
|
+
if (this.isUnrestricted(user)) {
|
|
65
|
+
return this.roles;
|
|
66
|
+
}
|
|
67
|
+
return this.roles.filter((r) => user.roles.includes(r.name) || user.roles.some((u) => u.toLowerCase() === r.name.toLowerCase()));
|
|
68
|
+
}
|
|
69
|
+
/** The authorizations of a user for an authorization object, the claims of its token expanded. */
|
|
70
|
+
of(user, object) {
|
|
71
|
+
const name = object.toUpperCase();
|
|
72
|
+
const result = [];
|
|
73
|
+
for (const role of this.rolesOf(user)) {
|
|
74
|
+
for (const authorization of role.authorizations.filter((a) => a.object === name)) {
|
|
75
|
+
const fields = {};
|
|
76
|
+
for (const [field, values] of Object.entries(authorization.fields)) {
|
|
77
|
+
fields[field] = values.flatMap((v) => {
|
|
78
|
+
const claim = typeof v === "string" ? CLAIM.exec(v)?.[1] : undefined;
|
|
79
|
+
return claim === undefined ? [v] : (user.attributes?.[claim] ?? []);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
result.push(fields);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* AUTHORITY-CHECK: 0 when one authorization of the object accepts every checked field (a field
|
|
89
|
+
* given with DUMMY is not checked), 4 when none does, 12 without authorization for the object.
|
|
90
|
+
*/
|
|
91
|
+
check(user, object, fields) {
|
|
92
|
+
if (this.isUnrestricted(user)) {
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
const authorizations = this.of(user, object);
|
|
96
|
+
if (authorizations.length === 0) {
|
|
97
|
+
return 12;
|
|
98
|
+
}
|
|
99
|
+
const granted = authorizations.some((authorization) => fields.every((field) => field.value === undefined || (authorization[field.id.toUpperCase()] ?? []).some((accepted) => valueMatches(field.value, accepted))));
|
|
100
|
+
return granted ? 0 : 4;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=authorizations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorizations.js","sourceRoot":"","sources":["../../src/auth/authorizations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The users of I_BusinessUserBasic (D44): on SAP, the business users of the system; here, the
|
|
3
|
+
* users of the identity provider who used the application, recorded at their first request (and
|
|
4
|
+
* their name and e-mail updated once per process).
|
|
5
|
+
*/
|
|
6
|
+
import type { Database } from "../db/database.js";
|
|
7
|
+
import type { User } from "./user.js";
|
|
8
|
+
export declare const BUSINESS_USER_TABLE = "rapt_bususer";
|
|
9
|
+
export declare class BusinessUsers {
|
|
10
|
+
private readonly db;
|
|
11
|
+
private readonly seen;
|
|
12
|
+
constructor(db: () => Database);
|
|
13
|
+
/** Records the user (fire and forget: a failure never fails the request). */
|
|
14
|
+
record(user: User): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The users of I_BusinessUserBasic (D44): on SAP, the business users of the system; here, the
|
|
3
|
+
* users of the identity provider who used the application, recorded at their first request (and
|
|
4
|
+
* their name and e-mail updated once per process).
|
|
5
|
+
*/
|
|
6
|
+
import { sql } from "kysely";
|
|
7
|
+
export const BUSINESS_USER_TABLE = "rapt_bususer";
|
|
8
|
+
export class BusinessUsers {
|
|
9
|
+
db;
|
|
10
|
+
seen = new Set();
|
|
11
|
+
constructor(db) {
|
|
12
|
+
this.db = db;
|
|
13
|
+
}
|
|
14
|
+
/** Records the user (fire and forget: a failure never fails the request). */
|
|
15
|
+
record(user) {
|
|
16
|
+
if (this.seen.has(user.abapName)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
this.seen.add(user.abapName);
|
|
20
|
+
const full = user.name ?? user.abapName;
|
|
21
|
+
const parts = full.trim().split(/\s+/);
|
|
22
|
+
const first = parts.length > 1 ? parts.slice(0, -1).join(" ") : "";
|
|
23
|
+
const last = parts.length > 1 ? (parts[parts.length - 1] ?? "") : full;
|
|
24
|
+
void sql `INSERT INTO rapt_bususer (userid, partner, fullname, firstname, lastname, email)
|
|
25
|
+
SELECT ${user.abapName}, lpad((coalesce(max(CASE WHEN partner ~ '^[0-9]+$' THEN partner::bigint END), 0) + 1)::text, 10, '0'),
|
|
26
|
+
${full.slice(0, 80)}, ${first.slice(0, 40)}, ${last.slice(0, 40)}, ${(user.email ?? "").slice(0, 241)}
|
|
27
|
+
FROM rapt_bususer
|
|
28
|
+
ON CONFLICT (userid) DO UPDATE SET fullname = EXCLUDED.fullname, firstname = EXCLUDED.firstname, lastname = EXCLUDED.lastname,
|
|
29
|
+
email = CASE WHEN EXCLUDED.email = '' THEN rapt_bususer.email ELSE EXCLUDED.email END`
|
|
30
|
+
.execute(this.db())
|
|
31
|
+
.catch(() => {
|
|
32
|
+
this.seen.delete(user.abapName);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=business-users.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"business-users.js","sourceRoot":"","sources":["../../src/auth/business-users.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAI7B,MAAM,CAAC,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAElD,MAAM,OAAO,aAAa;IAGY,EAAE;IAFrB,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE1C,YAAoC,EAAkB;kBAAlB,EAAE;IAAmB,CAAC;IAE1D,6EAA6E;IACtE,MAAM,CAAC,IAAU;QACtB,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACvC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACvE,KAAK,GAAG,CAAA;eACG,IAAI,CAAC,QAAQ;UAClB,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;;;8FAGf;aACvF,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;aAClB,KAAK,CAAC,GAAG,EAAE;YACV,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;IACP,CAAC;CACF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A development OAuth 2.0 authorization server (D60), served under /oauth by `rapt dev` (and by
|
|
3
|
+
* an application started with DEV_OAUTH=on in the development mode), to call the service as a
|
|
4
|
+
* Web API with the flows of a real identity provider, without one:
|
|
5
|
+
*
|
|
6
|
+
* - client credentials: POST /oauth/token grant_type=client_credentials with the client id and
|
|
7
|
+
* secret (form or Basic): a token of the technical user of the client;
|
|
8
|
+
* - authorization code with PKCE (S256): GET /oauth/authorize (a page to choose the user), then
|
|
9
|
+
* POST /oauth/token grant_type=authorization_code with the code_verifier;
|
|
10
|
+
* - /oauth/.well-known/openid-configuration and /oauth/jwks for the clients that discover them.
|
|
11
|
+
*
|
|
12
|
+
* Tokens are RS256 JWTs signed with a key generated at start-up: they end with the process. In
|
|
13
|
+
* production, the tokens come from the identity provider (AUTH_MODE=oidc).
|
|
14
|
+
*/
|
|
15
|
+
import type { FastifyInstance } from "fastify";
|
|
16
|
+
import { type JWTPayload } from "jose";
|
|
17
|
+
export interface DevOAuthClient {
|
|
18
|
+
clientId: string;
|
|
19
|
+
/** Confidential clients (client credentials); a public client (PKCE) has none. */
|
|
20
|
+
clientSecret?: string;
|
|
21
|
+
/** Roles of the technical user of a confidential client (default: every role, `*`). */
|
|
22
|
+
roles?: string[];
|
|
23
|
+
}
|
|
24
|
+
/** The clients when none are configured (DEV_OAUTH_CLIENTS). */
|
|
25
|
+
export declare const DEFAULT_DEV_CLIENTS: DevOAuthClient[];
|
|
26
|
+
export declare class DevOAuthServer {
|
|
27
|
+
private readonly clients;
|
|
28
|
+
private readonly privateKey;
|
|
29
|
+
private readonly jwk;
|
|
30
|
+
private readonly codes;
|
|
31
|
+
private constructor();
|
|
32
|
+
static create(clients?: DevOAuthClient[]): Promise<DevOAuthServer>;
|
|
33
|
+
/** Verifies a token of this server; its claims. */
|
|
34
|
+
verify(token: string): Promise<JWTPayload>;
|
|
35
|
+
private issuer;
|
|
36
|
+
private token;
|
|
37
|
+
register(app: FastifyInstance): void;
|
|
38
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A development OAuth 2.0 authorization server (D60), served under /oauth by `rapt dev` (and by
|
|
3
|
+
* an application started with DEV_OAUTH=on in the development mode), to call the service as a
|
|
4
|
+
* Web API with the flows of a real identity provider, without one:
|
|
5
|
+
*
|
|
6
|
+
* - client credentials: POST /oauth/token grant_type=client_credentials with the client id and
|
|
7
|
+
* secret (form or Basic): a token of the technical user of the client;
|
|
8
|
+
* - authorization code with PKCE (S256): GET /oauth/authorize (a page to choose the user), then
|
|
9
|
+
* POST /oauth/token grant_type=authorization_code with the code_verifier;
|
|
10
|
+
* - /oauth/.well-known/openid-configuration and /oauth/jwks for the clients that discover them.
|
|
11
|
+
*
|
|
12
|
+
* Tokens are RS256 JWTs signed with a key generated at start-up: they end with the process. In
|
|
13
|
+
* production, the tokens come from the identity provider (AUTH_MODE=oidc).
|
|
14
|
+
*/
|
|
15
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
16
|
+
import { createLocalJWKSet, exportJWK, generateKeyPair, jwtVerify, SignJWT } from "jose";
|
|
17
|
+
/** The clients when none are configured (DEV_OAUTH_CLIENTS). */
|
|
18
|
+
export const DEFAULT_DEV_CLIENTS = [
|
|
19
|
+
{ clientId: "rapt-client", clientSecret: "rapt-secret", roles: ["*"] },
|
|
20
|
+
{ clientId: "rapt-pkce" },
|
|
21
|
+
];
|
|
22
|
+
const TOKEN_LIFETIME = 3600;
|
|
23
|
+
const escapeHtml = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
24
|
+
export class DevOAuthServer {
|
|
25
|
+
clients;
|
|
26
|
+
privateKey;
|
|
27
|
+
jwk;
|
|
28
|
+
codes = new Map();
|
|
29
|
+
constructor(clients, privateKey, jwk) {
|
|
30
|
+
this.clients = clients;
|
|
31
|
+
this.privateKey = privateKey;
|
|
32
|
+
this.jwk = jwk;
|
|
33
|
+
}
|
|
34
|
+
static async create(clients = DEFAULT_DEV_CLIENTS) {
|
|
35
|
+
const { privateKey, publicKey } = await generateKeyPair("RS256", { extractable: true });
|
|
36
|
+
const jwk = { ...(await exportJWK(publicKey)), kid: "rapt-dev", alg: "RS256", use: "sig" };
|
|
37
|
+
return new DevOAuthServer(clients, privateKey, jwk);
|
|
38
|
+
}
|
|
39
|
+
/** Verifies a token of this server; its claims. */
|
|
40
|
+
async verify(token) {
|
|
41
|
+
const { payload } = await jwtVerify(token, createLocalJWKSet({ keys: [this.jwk] }));
|
|
42
|
+
return payload;
|
|
43
|
+
}
|
|
44
|
+
issuer(request) {
|
|
45
|
+
const proto = String(request.headers["x-forwarded-proto"] ?? request.protocol ?? "http").split(",")[0];
|
|
46
|
+
return `${proto}://${request.headers.host ?? "localhost"}/oauth`;
|
|
47
|
+
}
|
|
48
|
+
async token(issuer, claims) {
|
|
49
|
+
return new SignJWT(claims)
|
|
50
|
+
.setProtectedHeader({ alg: "RS256", kid: "rapt-dev" })
|
|
51
|
+
.setIssuer(issuer)
|
|
52
|
+
.setIssuedAt()
|
|
53
|
+
.setExpirationTime(`${TOKEN_LIFETIME}s`)
|
|
54
|
+
.sign(this.privateKey);
|
|
55
|
+
}
|
|
56
|
+
register(app) {
|
|
57
|
+
const form = (body) => new URLSearchParams(typeof body === "string" ? body : Buffer.isBuffer(body) ? body.toString("utf8") : "");
|
|
58
|
+
const error = (code, description) => JSON.stringify({ error: code, error_description: description });
|
|
59
|
+
app.get("/oauth/.well-known/openid-configuration", async (request, reply) => {
|
|
60
|
+
const issuer = this.issuer(request);
|
|
61
|
+
return reply.header("Content-Type", "application/json").send(JSON.stringify({
|
|
62
|
+
issuer,
|
|
63
|
+
authorization_endpoint: `${issuer}/authorize`,
|
|
64
|
+
token_endpoint: `${issuer}/token`,
|
|
65
|
+
jwks_uri: `${issuer}/jwks`,
|
|
66
|
+
response_types_supported: ["code"],
|
|
67
|
+
grant_types_supported: ["authorization_code", "client_credentials"],
|
|
68
|
+
code_challenge_methods_supported: ["S256"],
|
|
69
|
+
token_endpoint_auth_methods_supported: ["client_secret_post", "client_secret_basic", "none"],
|
|
70
|
+
id_token_signing_alg_values_supported: ["RS256"],
|
|
71
|
+
}));
|
|
72
|
+
});
|
|
73
|
+
app.get("/oauth/jwks", async (_request, reply) => reply.header("Content-Type", "application/json").send(JSON.stringify({ keys: [this.jwk] })));
|
|
74
|
+
// authorization code with PKCE: a page to choose the user of the development token
|
|
75
|
+
app.get("/oauth/authorize", async (request, reply) => {
|
|
76
|
+
const query = request.query;
|
|
77
|
+
const client = this.clients.find((c) => c.clientId === query["client_id"]);
|
|
78
|
+
if (query["response_type"] !== "code" || client === undefined || query["redirect_uri"] === undefined) {
|
|
79
|
+
return reply.code(400).header("Content-Type", "text/plain").send("response_type=code, a known client_id and a redirect_uri are required");
|
|
80
|
+
}
|
|
81
|
+
if (query["code_challenge"] === undefined || (query["code_challenge_method"] ?? "plain") !== "S256") {
|
|
82
|
+
return reply.code(400).header("Content-Type", "text/plain").send("PKCE is required: code_challenge with code_challenge_method=S256");
|
|
83
|
+
}
|
|
84
|
+
const hidden = ["client_id", "redirect_uri", "state", "code_challenge"].map((k) => `<input type="hidden" name="${k}" value="${escapeHtml(query[k] ?? "")}">`).join("");
|
|
85
|
+
return reply.header("Content-Type", "text/html; charset=utf-8").send(`<!doctype html><html><head><meta charset="utf-8"><title>rapt dev sign-in</title>
|
|
86
|
+
<style>body{font-family:system-ui,sans-serif;max-width:26rem;margin:4rem auto;padding:0 1rem}label{display:block;margin:.8rem 0 .2rem}input{width:100%;padding:.4rem;box-sizing:border-box}button{margin-top:1.2rem;padding:.5rem 1rem}</style></head>
|
|
87
|
+
<body><h1>rapt dev sign-in</h1><p>Development authorization server: the token is issued for the user you enter. Client <b>${escapeHtml(client.clientId)}</b>.</p>
|
|
88
|
+
<form method="post" action="/oauth/authorize">${hidden}<label for="user">User</label><input id="user" name="user" value="DEVELOPER" required><label for="roles">Roles (comma separated, * for all)</label><input id="roles" name="roles" value="*"><button id="signin" type="submit">Sign in</button></form></body></html>`);
|
|
89
|
+
});
|
|
90
|
+
app.post("/oauth/authorize", async (request, reply) => {
|
|
91
|
+
const params = form(request.body);
|
|
92
|
+
const client = this.clients.find((c) => c.clientId === params.get("client_id"));
|
|
93
|
+
const redirectUri = params.get("redirect_uri");
|
|
94
|
+
const challenge = params.get("code_challenge");
|
|
95
|
+
if (client === undefined || redirectUri === null || challenge === null) {
|
|
96
|
+
return reply.code(400).header("Content-Type", "text/plain").send("invalid authorization request");
|
|
97
|
+
}
|
|
98
|
+
const code = randomBytes(24).toString("base64url");
|
|
99
|
+
const user = (params.get("user") ?? "DEVELOPER").trim() || "DEVELOPER";
|
|
100
|
+
const roles = (params.get("roles") ?? "*").split(",").map((r) => r.trim()).filter((r) => r !== "");
|
|
101
|
+
this.codes.set(code, { clientId: client.clientId, redirectUri, challenge, user, roles, expires: Date.now() + 120_000 });
|
|
102
|
+
const target = new URL(redirectUri);
|
|
103
|
+
target.searchParams.set("code", code);
|
|
104
|
+
const state = params.get("state");
|
|
105
|
+
if (state !== null && state !== "")
|
|
106
|
+
target.searchParams.set("state", state);
|
|
107
|
+
return reply.redirect(target.toString());
|
|
108
|
+
});
|
|
109
|
+
app.post("/oauth/token", async (request, reply) => {
|
|
110
|
+
const params = form(request.body);
|
|
111
|
+
const json = (status, body) => reply.code(status).header("Content-Type", "application/json").header("Cache-Control", "no-store").send(body);
|
|
112
|
+
// client authentication: client_secret_basic or client_secret_post
|
|
113
|
+
let clientId = params.get("client_id");
|
|
114
|
+
let clientSecret = params.get("client_secret");
|
|
115
|
+
const basic = /^Basic\s+(.+)$/i.exec(request.headers.authorization ?? "");
|
|
116
|
+
if (basic !== null) {
|
|
117
|
+
const [id = "", secret = ""] = Buffer.from(basic[1], "base64").toString("utf8").split(":");
|
|
118
|
+
clientId = decodeURIComponent(id);
|
|
119
|
+
clientSecret = decodeURIComponent(secret);
|
|
120
|
+
}
|
|
121
|
+
const client = this.clients.find((c) => c.clientId === clientId);
|
|
122
|
+
if (client === undefined) {
|
|
123
|
+
return json(401, error("invalid_client", "unknown client"));
|
|
124
|
+
}
|
|
125
|
+
const issuer = this.issuer(request);
|
|
126
|
+
const grant = params.get("grant_type");
|
|
127
|
+
if (grant === "client_credentials") {
|
|
128
|
+
if (client.clientSecret === undefined || client.clientSecret !== clientSecret) {
|
|
129
|
+
return json(401, error("invalid_client", "client credentials need the secret of a confidential client"));
|
|
130
|
+
}
|
|
131
|
+
const accessToken = await this.token(issuer, { sub: client.clientId, client_id: client.clientId, azp: client.clientId, roles: client.roles ?? ["*"], scope: params.get("scope") ?? "" });
|
|
132
|
+
return json(200, JSON.stringify({ access_token: accessToken, token_type: "Bearer", expires_in: TOKEN_LIFETIME }));
|
|
133
|
+
}
|
|
134
|
+
if (grant === "authorization_code") {
|
|
135
|
+
const code = this.codes.get(params.get("code") ?? "");
|
|
136
|
+
this.codes.delete(params.get("code") ?? "");
|
|
137
|
+
if (code === undefined || code.expires < Date.now() || code.clientId !== client.clientId || code.redirectUri !== params.get("redirect_uri")) {
|
|
138
|
+
return json(400, error("invalid_grant", "unknown, expired or mismatched authorization code"));
|
|
139
|
+
}
|
|
140
|
+
if (client.clientSecret !== undefined && clientSecret !== null && clientSecret !== client.clientSecret) {
|
|
141
|
+
return json(401, error("invalid_client", "wrong client secret"));
|
|
142
|
+
}
|
|
143
|
+
const verifier = params.get("code_verifier") ?? "";
|
|
144
|
+
if (createHash("sha256").update(verifier).digest("base64url") !== code.challenge) {
|
|
145
|
+
return json(400, error("invalid_grant", "the code_verifier does not match the code_challenge (PKCE)"));
|
|
146
|
+
}
|
|
147
|
+
const claims = { sub: code.user, preferred_username: code.user, azp: client.clientId, roles: code.roles };
|
|
148
|
+
const accessToken = await this.token(issuer, claims);
|
|
149
|
+
const idToken = await this.token(issuer, { ...claims, aud: client.clientId });
|
|
150
|
+
return json(200, JSON.stringify({ access_token: accessToken, id_token: idToken, token_type: "Bearer", expires_in: TOKEN_LIFETIME }));
|
|
151
|
+
}
|
|
152
|
+
return json(400, error("unsupported_grant_type", "client_credentials or authorization_code"));
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=dev-oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-oauth.js","sourceRoot":"","sources":["../../src/auth/dev-oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The authentication of a generated application or launchpad, from its environment: the
|
|
3
|
+
* development user (`AUTH_MODE=none`) or OIDC (bearer tokens, and browser sessions when
|
|
4
|
+
* `OIDC_CLIENT_ID` is set). See server.ts for the variables.
|
|
5
|
+
*/
|
|
6
|
+
import type { AuthOptions } from "./auth.js";
|
|
7
|
+
export declare function authFromEnvironment(env: Record<string, string | undefined>, publicUrl: string, attributeClaims: string[]): AuthOptions;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The authentication of a generated application or launchpad, from its environment: the
|
|
3
|
+
* development user (`AUTH_MODE=none`) or OIDC (bearer tokens, and browser sessions when
|
|
4
|
+
* `OIDC_CLIENT_ID` is set). See server.ts for the variables.
|
|
5
|
+
*/
|
|
6
|
+
export function authFromEnvironment(env, publicUrl, attributeClaims) {
|
|
7
|
+
if ((env["AUTH_MODE"] ?? "none") === "none") {
|
|
8
|
+
const auth = { mode: "none" };
|
|
9
|
+
if (env["DEV_USER"] !== undefined) {
|
|
10
|
+
auth.user = env["DEV_USER"];
|
|
11
|
+
}
|
|
12
|
+
// the roles of the development user (default: all), its claims as JSON ({"agency":["070001"]})
|
|
13
|
+
if (env["DEV_ROLES"] !== undefined) {
|
|
14
|
+
auth.roles = env["DEV_ROLES"].split(",").map((r) => r.trim()).filter((r) => r !== "");
|
|
15
|
+
}
|
|
16
|
+
if (env["DEV_ATTRIBUTES"] !== undefined && env["DEV_ATTRIBUTES"] !== "") {
|
|
17
|
+
const attributes = JSON.parse(env["DEV_ATTRIBUTES"]);
|
|
18
|
+
auth.attributes = Object.fromEntries(Object.entries(attributes).map(([k, v]) => [k, (Array.isArray(v) ? v : [v]).map(String)]));
|
|
19
|
+
}
|
|
20
|
+
return auth;
|
|
21
|
+
}
|
|
22
|
+
const issuer = env["OIDC_ISSUER"];
|
|
23
|
+
if (issuer === undefined) {
|
|
24
|
+
throw new Error("AUTH_MODE=oidc needs OIDC_ISSUER");
|
|
25
|
+
}
|
|
26
|
+
const auth = { mode: "oidc", issuer };
|
|
27
|
+
if (attributeClaims.length > 0) {
|
|
28
|
+
auth.attributeClaims = attributeClaims;
|
|
29
|
+
}
|
|
30
|
+
const optional = [
|
|
31
|
+
["discoveryUrl", "OIDC_DISCOVERY_URL"],
|
|
32
|
+
["audience", "OIDC_AUDIENCE"],
|
|
33
|
+
["requiredRole", "OIDC_REQUIRED_ROLE"],
|
|
34
|
+
["rolesClaim", "OIDC_ROLES_CLAIM"],
|
|
35
|
+
["userClaim", "OIDC_USER_CLAIM"],
|
|
36
|
+
];
|
|
37
|
+
for (const [key, name] of optional) {
|
|
38
|
+
const value = env[name];
|
|
39
|
+
if (value !== undefined && value !== "") {
|
|
40
|
+
auth[key] = value;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const clientId = env["OIDC_CLIENT_ID"];
|
|
44
|
+
if (clientId !== undefined && clientId !== "") {
|
|
45
|
+
const secret = env["SESSION_SECRET"];
|
|
46
|
+
if (secret === undefined || secret.length < 32) {
|
|
47
|
+
throw new Error("browser sessions (OIDC_CLIENT_ID) need SESSION_SECRET, 32 characters or more");
|
|
48
|
+
}
|
|
49
|
+
auth.session = { clientId, baseUrl: publicUrl, secret };
|
|
50
|
+
const clientSecret = env["OIDC_CLIENT_SECRET"];
|
|
51
|
+
if (clientSecret !== undefined && clientSecret !== "") {
|
|
52
|
+
auth.session.clientSecret = clientSecret;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return auth;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=environment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment.js","sourceRoot":"","sources":["../../src/auth/environment.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,MAAM,UAAU,mBAAmB,CAAC,GAAuC,EAAE,SAAiB,EAAE,eAAyB;IACvH,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,MAAM,CAAC,KAAK,MAAM,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAC3C,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,SAAS,EAAE,CAAC;YAClC,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC;QAC9B,CAAC;QACD,+FAA+F;QAC/F,IAAI,GAAG,CAAC,WAAW,CAAC,KAAK,SAAS,EAAE,CAAC;YACnC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QACxF,CAAC;QACD,IAAI,GAAG,CAAC,gBAAgB,CAAC,KAAK,SAAS,IAAI,GAAG,CAAC,gBAAgB,CAAC,KAAK,EAAE,EAAE,CAAC;YACxE,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAA4B,CAAC;YAChF,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAClI,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC;IAClC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;IACD,MAAM,IAAI,GAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IACnD,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;IACzC,CAAC;IACD,MAAM,QAAQ,GAAuC;QACnD,CAAC,cAAc,EAAE,oBAAoB,CAAC;QACtC,CAAC,UAAU,EAAE,eAAe,CAAC;QAC7B,CAAC,cAAc,EAAE,oBAAoB,CAAC;QACtC,CAAC,YAAY,EAAE,kBAAkB,CAAC;QAClC,CAAC,WAAW,EAAE,iBAAiB,CAAC;KACjC,CAAC;IACF,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,QAAQ,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;QACxB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YACvC,IAA0C,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC3D,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACvC,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;QAC9C,MAAM,MAAM,GAAG,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACrC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAC/C,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAC;QAClG,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QACxD,MAAM,YAAY,GAAG,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAC/C,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,KAAK,EAAE,EAAE,CAAC;YACtD,IAAI,CAAC,OAAO,CAAC,YAAY,GAAG,YAAY,CAAC;QAC3C,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser sessions over OIDC (PLAN.md §6.8): authorization code flow with PKCE, the user kept in
|
|
3
|
+
* a signed, HttpOnly cookie (no server-side store). API clients keep using bearer tokens.
|
|
4
|
+
*
|
|
5
|
+
* Routes: GET /auth/login?returnTo=…, GET /auth/callback, GET /auth/logout, GET /auth/me.
|
|
6
|
+
* The discovery document may be fetched from an internal URL (`discoveryUrl`) while the tokens
|
|
7
|
+
* name the public issuer: the usual setup of Keycloak in docker-compose.
|
|
8
|
+
*/
|
|
9
|
+
import type { FastifyInstance, FastifyRequest } from "fastify";
|
|
10
|
+
import { type Authenticator, type OidcAuthOptions } from "./auth.js";
|
|
11
|
+
import type { User } from "./user.js";
|
|
12
|
+
export declare const SESSION_COOKIE = "rapt_session";
|
|
13
|
+
/**
|
|
14
|
+
* The value of a session cookie for a user, signed with the secret of the sessions: what the
|
|
15
|
+
* login callback sets. For the tests, and the tools that open a session for a known user.
|
|
16
|
+
*/
|
|
17
|
+
export declare function signSession(user: User, secret: string, maxAge?: number): string;
|
|
18
|
+
export declare function parseCookies(header: string | undefined): Record<string, string>;
|
|
19
|
+
export declare class OidcSessions {
|
|
20
|
+
private readonly authenticator;
|
|
21
|
+
private readonly options;
|
|
22
|
+
private readonly signer;
|
|
23
|
+
private metadataPromise;
|
|
24
|
+
private jwks;
|
|
25
|
+
private readonly secure;
|
|
26
|
+
private readonly maxAge;
|
|
27
|
+
constructor(authenticator: Authenticator, options: OidcAuthOptions & {
|
|
28
|
+
session: NonNullable<OidcAuthOptions["session"]>;
|
|
29
|
+
});
|
|
30
|
+
private metadata;
|
|
31
|
+
private cookie;
|
|
32
|
+
private redirectUri;
|
|
33
|
+
/**
|
|
34
|
+
* The user of a session cookie; undefined without a valid session. The session may have been
|
|
35
|
+
* opened by another application sharing the secret (a launchpad, D37): the role required by
|
|
36
|
+
* this one is checked at each request (AuthenticationError 403 without it).
|
|
37
|
+
*/
|
|
38
|
+
user(request: FastifyRequest): User | undefined;
|
|
39
|
+
/** `/auth/login?returnTo=…` for the current page. */
|
|
40
|
+
loginUrl(returnTo: string): string;
|
|
41
|
+
register(app: FastifyInstance): void;
|
|
42
|
+
private login;
|
|
43
|
+
private callback;
|
|
44
|
+
private logout;
|
|
45
|
+
}
|