@rimelight/auth 0.0.2 → 0.0.4

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.
@@ -0,0 +1,115 @@
1
+ import { AuthAdapter, UserSessionContext } from "../types.mjs";
2
+ import { decodeJwt } from "jose";
3
+ //#region src/adapters/auth0.d.ts
4
+ export interface Auth0AdapterOptions {
5
+ domain: string;
6
+ audience?: string | undefined;
7
+ clientId?: string | undefined;
8
+ clientSecret?: string | undefined;
9
+ rolesClaim?: string | undefined;
10
+ userTypeClaim?: string | undefined;
11
+ permissionsClaim?: string | undefined;
12
+ sessionCookieName?: string | undefined;
13
+ sessionSecret?: string | undefined;
14
+ loginUrl?: string | undefined;
15
+ defaultRole?: string | undefined;
16
+ }
17
+ export interface Auth0AuthorizeUrlOptions {
18
+ domain: string;
19
+ clientId: string;
20
+ redirectUri: string;
21
+ audience?: string | undefined;
22
+ scope?: string | undefined;
23
+ state?: string | undefined;
24
+ codeChallenge?: string | undefined;
25
+ codeChallengeMethod?: "S256" | "plain" | undefined;
26
+ prompt?: string | undefined;
27
+ screenHint?: string | undefined;
28
+ }
29
+ export interface Auth0TokenResponse {
30
+ access_token: string;
31
+ id_token?: string | undefined;
32
+ token_type: string;
33
+ expires_in: number;
34
+ refresh_token?: string | undefined;
35
+ scope?: string | undefined;
36
+ }
37
+ export interface Auth0CodeExchangeOptions {
38
+ domain: string;
39
+ clientId: string;
40
+ clientSecret?: string | undefined;
41
+ code: string;
42
+ redirectUri: string;
43
+ codeVerifier?: string | undefined;
44
+ }
45
+ export interface Auth0PasswordLoginOptions {
46
+ domain: string;
47
+ clientId: string;
48
+ clientSecret?: string | undefined;
49
+ username: string;
50
+ password: string;
51
+ audience?: string | undefined;
52
+ scope?: string | undefined;
53
+ realm?: string | undefined;
54
+ }
55
+ export interface Auth0RefreshTokenOptions {
56
+ domain: string;
57
+ clientId: string;
58
+ clientSecret?: string | undefined;
59
+ refreshToken: string;
60
+ scope?: string | undefined;
61
+ }
62
+ /**
63
+ * Verifies an Auth0 RS256 JWT Access Token or ID Token against Auth0 JWKS endpoint
64
+ */
65
+ export declare function verifyAuth0Jwt(token: string, options: {
66
+ domain: string;
67
+ audience?: string | undefined;
68
+ issuer?: string | undefined;
69
+ }): Promise<Record<string, any> | null>;
70
+ /**
71
+ * Creates a signed compact session token (HS256) for local session cookie storage
72
+ */
73
+ export declare function createSessionToken(session: UserSessionContext, secret: string, expiresIn?: string | number): Promise<string>;
74
+ /**
75
+ * Verifies a signed session token from a session cookie
76
+ */
77
+ export declare function verifySessionToken(token: string, secret: string): Promise<UserSessionContext | null>;
78
+ /**
79
+ * Generates an Auth0 OAuth2 / OIDC authorization URL
80
+ */
81
+ export declare function createAuth0AuthorizeUrl(options: Auth0AuthorizeUrlOptions): string;
82
+ /**
83
+ * Exchanges an authorization code for Auth0 tokens
84
+ */
85
+ export declare function exchangeAuth0Code(options: Auth0CodeExchangeOptions): Promise<Auth0TokenResponse>;
86
+ /**
87
+ * Authenticates directly with Auth0 using Resource Owner Password Credentials (ROPC). Used by
88
+ * headless clients (e.g., Unreal Engine 5) to authenticate without browser redirects.
89
+ */
90
+ export declare function loginWithAuth0Password(options: Auth0PasswordLoginOptions): Promise<Auth0TokenResponse>;
91
+ /**
92
+ * Exchanges a refresh token for a new Auth0 access token. Ensures uninterrupted play sessions in
93
+ * native game clients.
94
+ */
95
+ export declare function refreshAuth0Token(options: Auth0RefreshTokenOptions): Promise<Auth0TokenResponse>;
96
+ /**
97
+ * Generates an Auth0 logout URL
98
+ */
99
+ export declare function createAuth0LogoutUrl(options: {
100
+ domain: string;
101
+ clientId: string;
102
+ returnTo: string;
103
+ }): string;
104
+ /**
105
+ * Auth0 / OIDC Auth Adapter
106
+ */
107
+ export declare function auth0Auth(options: Auth0AdapterOptions): AuthAdapter;
108
+ export declare function mapAuth0PayloadToSession(payload: Record<string, any>, options: {
109
+ rolesClaim: string;
110
+ userTypeClaim: string;
111
+ permissionsClaim: string;
112
+ defaultRole: string;
113
+ }): UserSessionContext;
114
+ //#endregion
115
+ export { decodeJwt };
@@ -0,0 +1,256 @@
1
+ import { SignJWT, createRemoteJWKSet, decodeJwt, jwtVerify } from "jose";
2
+ //#region src/adapters/auth0.ts
3
+ const jwksCache = /* @__PURE__ */ new Map();
4
+ function getOrCreateJwks(domain) {
5
+ const cleanDomain = domain.replace(/^https?:\/\//, "").replace(/\/$/, "");
6
+ if (!jwksCache.has(cleanDomain)) {
7
+ const jwksUrl = new URL(`https://${cleanDomain}/.well-known/jwks.json`);
8
+ jwksCache.set(cleanDomain, createRemoteJWKSet(jwksUrl));
9
+ }
10
+ return jwksCache.get(cleanDomain);
11
+ }
12
+ /**
13
+ * Verifies an Auth0 RS256 JWT Access Token or ID Token against Auth0 JWKS endpoint
14
+ */
15
+ async function verifyAuth0Jwt(token, options) {
16
+ try {
17
+ const cleanDomain = options.domain.replace(/^https?:\/\//, "").replace(/\/$/, "");
18
+ const jwks = getOrCreateJwks(cleanDomain);
19
+ const verifyOpts = { issuer: options.issuer || `https://${cleanDomain}/` };
20
+ if (options.audience) verifyOpts.audience = options.audience;
21
+ const { payload } = await jwtVerify(token, jwks, verifyOpts);
22
+ return payload;
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
27
+ /**
28
+ * Creates a signed compact session token (HS256) for local session cookie storage
29
+ */
30
+ async function createSessionToken(session, secret, expiresIn = "7d") {
31
+ const encSecret = new TextEncoder().encode(secret);
32
+ return new SignJWT({ ...session }).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(expiresIn).sign(encSecret);
33
+ }
34
+ /**
35
+ * Verifies a signed session token from a session cookie
36
+ */
37
+ async function verifySessionToken(token, secret) {
38
+ try {
39
+ const encSecret = new TextEncoder().encode(secret);
40
+ const { payload } = await jwtVerify(token, encSecret, { algorithms: ["HS256"] });
41
+ return payload;
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+ /**
47
+ * Generates an Auth0 OAuth2 / OIDC authorization URL
48
+ */
49
+ function createAuth0AuthorizeUrl(options) {
50
+ const cleanDomain = options.domain.replace(/^https?:\/\//, "").replace(/\/$/, "");
51
+ const url = new URL(`https://${cleanDomain}/authorize`);
52
+ url.searchParams.set("response_type", "code");
53
+ url.searchParams.set("client_id", options.clientId);
54
+ url.searchParams.set("redirect_uri", options.redirectUri);
55
+ url.searchParams.set("scope", options.scope || "openid profile email");
56
+ if (options.audience) url.searchParams.set("audience", options.audience);
57
+ if (options.state) url.searchParams.set("state", options.state);
58
+ if (options.codeChallenge) {
59
+ url.searchParams.set("code_challenge", options.codeChallenge);
60
+ url.searchParams.set("code_challenge_method", options.codeChallengeMethod || "S256");
61
+ }
62
+ if (options.prompt) url.searchParams.set("prompt", options.prompt);
63
+ if (options.screenHint) url.searchParams.set("screen_hint", options.screenHint);
64
+ return url.toString();
65
+ }
66
+ /**
67
+ * Exchanges an authorization code for Auth0 tokens
68
+ */
69
+ async function exchangeAuth0Code(options) {
70
+ const tokenUrl = `https://${options.domain.replace(/^https?:\/\//, "").replace(/\/$/, "")}/oauth/token`;
71
+ const body = {
72
+ grant_type: "authorization_code",
73
+ client_id: options.clientId,
74
+ code: options.code,
75
+ redirect_uri: options.redirectUri
76
+ };
77
+ if (options.clientSecret) body["client_secret"] = options.clientSecret;
78
+ if (options.codeVerifier) body["code_verifier"] = options.codeVerifier;
79
+ const response = await fetch(tokenUrl, {
80
+ method: "POST",
81
+ headers: { "Content-Type": "application/json" },
82
+ body: JSON.stringify(body)
83
+ });
84
+ if (!response.ok) {
85
+ const errText = await response.text();
86
+ throw new Error(`Auth0 token exchange failed (${response.status}): ${errText}`);
87
+ }
88
+ return await response.json();
89
+ }
90
+ /**
91
+ * Authenticates directly with Auth0 using Resource Owner Password Credentials (ROPC). Used by
92
+ * headless clients (e.g., Unreal Engine 5) to authenticate without browser redirects.
93
+ */
94
+ async function loginWithAuth0Password(options) {
95
+ const tokenUrl = `https://${options.domain.replace(/^https?:\/\//, "").replace(/\/$/, "")}/oauth/token`;
96
+ const body = {
97
+ grant_type: options.realm ? "http://auth0.com/oauth/grant-type/password-realm" : "password",
98
+ client_id: options.clientId,
99
+ username: options.username,
100
+ password: options.password,
101
+ scope: options.scope || "openid profile email offline_access"
102
+ };
103
+ if (options.realm) body["realm"] = options.realm;
104
+ if (options.clientSecret) body["client_secret"] = options.clientSecret;
105
+ if (options.audience) body["audience"] = options.audience;
106
+ const response = await fetch(tokenUrl, {
107
+ method: "POST",
108
+ headers: { "Content-Type": "application/json" },
109
+ body: JSON.stringify(body)
110
+ });
111
+ if (!response.ok) {
112
+ const errText = await response.text();
113
+ throw new Error(`Auth0 password login failed (${response.status}): ${errText}`);
114
+ }
115
+ return await response.json();
116
+ }
117
+ /**
118
+ * Exchanges a refresh token for a new Auth0 access token. Ensures uninterrupted play sessions in
119
+ * native game clients.
120
+ */
121
+ async function refreshAuth0Token(options) {
122
+ const tokenUrl = `https://${options.domain.replace(/^https?:\/\//, "").replace(/\/$/, "")}/oauth/token`;
123
+ const body = {
124
+ grant_type: "refresh_token",
125
+ client_id: options.clientId,
126
+ refresh_token: options.refreshToken
127
+ };
128
+ if (options.clientSecret) body["client_secret"] = options.clientSecret;
129
+ if (options.scope) body["scope"] = options.scope;
130
+ const response = await fetch(tokenUrl, {
131
+ method: "POST",
132
+ headers: { "Content-Type": "application/json" },
133
+ body: JSON.stringify(body)
134
+ });
135
+ if (!response.ok) {
136
+ const errText = await response.text();
137
+ throw new Error(`Auth0 token refresh failed (${response.status}): ${errText}`);
138
+ }
139
+ return await response.json();
140
+ }
141
+ /**
142
+ * Generates an Auth0 logout URL
143
+ */
144
+ function createAuth0LogoutUrl(options) {
145
+ const cleanDomain = options.domain.replace(/^https?:\/\//, "").replace(/\/$/, "");
146
+ const url = new URL(`https://${cleanDomain}/v2/logout`);
147
+ url.searchParams.set("client_id", options.clientId);
148
+ url.searchParams.set("returnTo", options.returnTo);
149
+ return url.toString();
150
+ }
151
+ function parseCookie(cookieHeader, name) {
152
+ if (!cookieHeader) return null;
153
+ const cookies = cookieHeader.split(";");
154
+ for (const cookie of cookies) {
155
+ const [k, v] = cookie.trim().split("=");
156
+ if (k === name && v) return decodeURIComponent(v);
157
+ }
158
+ return null;
159
+ }
160
+ /**
161
+ * Auth0 / OIDC Auth Adapter
162
+ */
163
+ function auth0Auth(options) {
164
+ const { domain, audience, rolesClaim = "https://rimelight.com/roles", userTypeClaim = "https://rimelight.com/user_type", permissionsClaim = "permissions", sessionCookieName = "rimelight_session", sessionSecret, loginUrl = "/api/auth/login", defaultRole = "user" } = options;
165
+ return {
166
+ name: "auth0",
167
+ options,
168
+ async getSession(req) {
169
+ if (!req) return null;
170
+ const getHeader = (name) => {
171
+ try {
172
+ if (req.headers && typeof req.headers.get === "function") return req.headers.get(name);
173
+ const h = req?.headers;
174
+ if (h && typeof h === "object") return h[name] || h[name.toLowerCase()] || null;
175
+ } catch {}
176
+ return null;
177
+ };
178
+ const authHeader = getHeader("authorization");
179
+ if (authHeader?.startsWith("Bearer ")) {
180
+ const payload = await verifyAuth0Jwt(authHeader.slice(7).trim(), {
181
+ domain,
182
+ audience
183
+ });
184
+ if (payload) return mapAuth0PayloadToSession(payload, {
185
+ rolesClaim,
186
+ userTypeClaim,
187
+ permissionsClaim,
188
+ defaultRole
189
+ });
190
+ }
191
+ const sessionCookie = parseCookie(getHeader("cookie"), sessionCookieName);
192
+ if (sessionCookie) {
193
+ if (sessionSecret) {
194
+ const verified = await verifySessionToken(sessionCookie, sessionSecret);
195
+ if (verified) return verified;
196
+ } else {
197
+ const payload = await verifyAuth0Jwt(sessionCookie, {
198
+ domain,
199
+ audience
200
+ });
201
+ if (payload) return mapAuth0PayloadToSession(payload, {
202
+ rolesClaim,
203
+ userTypeClaim,
204
+ permissionsClaim,
205
+ defaultRole
206
+ });
207
+ }
208
+ }
209
+ return null;
210
+ },
211
+ handleUnauthorized(req) {
212
+ if (loginUrl) {
213
+ try {
214
+ if (req && req.url) {
215
+ const origin = new URL(req.url).origin;
216
+ const fullUrl = new URL(loginUrl, origin).toString();
217
+ return Response.redirect(fullUrl, 302);
218
+ }
219
+ } catch {}
220
+ try {
221
+ return Response.redirect(loginUrl, 302);
222
+ } catch {
223
+ return new Response(null, {
224
+ status: 302,
225
+ headers: { Location: loginUrl }
226
+ });
227
+ }
228
+ }
229
+ return new Response("Unauthorized Auth0 Identity", { status: 401 });
230
+ }
231
+ };
232
+ }
233
+ function mapAuth0PayloadToSession(payload, options) {
234
+ const roles = Array.isArray(payload[options.rolesClaim]) ? payload[options.rolesClaim] : Array.isArray(payload["roles"]) ? payload["roles"] : [options.defaultRole];
235
+ const permissions = Array.isArray(payload[options.permissionsClaim]) ? payload[options.permissionsClaim] : Array.isArray(payload["permissions"]) ? payload["permissions"] : [];
236
+ const isStaff = roles.some((r) => [
237
+ "owner",
238
+ "admin",
239
+ "editor",
240
+ "employee",
241
+ "superadmin"
242
+ ].includes(r.toLowerCase()));
243
+ const userType = payload[options.userTypeClaim] || payload["user_type"] || (isStaff ? "employee" : "user");
244
+ return {
245
+ userId: payload["sub"] || "anonymous",
246
+ email: payload["email"],
247
+ name: payload["name"] || payload["nickname"] || payload["email"]?.split("@")[0] || "User",
248
+ avatar: payload["picture"],
249
+ roles,
250
+ permissions,
251
+ userType,
252
+ metadata: payload
253
+ };
254
+ }
255
+ //#endregion
256
+ export { auth0Auth, createAuth0AuthorizeUrl, createAuth0LogoutUrl, createSessionToken, decodeJwt, exchangeAuth0Code, loginWithAuth0Password, mapAuth0PayloadToSession, refreshAuth0Token, verifyAuth0Jwt, verifySessionToken };
@@ -0,0 +1,12 @@
1
+ import { AuthAdapter } from "../types.mjs";
2
+ //#region src/adapters/cf-access.d.ts
3
+ export interface CfAccessAdapterOptions {
4
+ aud?: string | undefined;
5
+ teamDomain?: string | undefined;
6
+ defaultRole?: string | undefined;
7
+ adminEmails?: string[] | undefined;
8
+ verifyJwt?: boolean | undefined;
9
+ loginUrl?: string | undefined;
10
+ }
11
+ export declare function cfAccessAuth(options?: CfAccessAdapterOptions): AuthAdapter;
12
+ //#endregion
@@ -0,0 +1,66 @@
1
+ import { createRemoteJWKSet, jwtVerify } from "jose";
2
+ //#region src/adapters/cf-access.ts
3
+ function cfAccessAuth(options = {}) {
4
+ const { aud, teamDomain, defaultRole = "admin", adminEmails = [], verifyJwt = false, loginUrl } = options;
5
+ let jwks = null;
6
+ if (teamDomain && verifyJwt) {
7
+ const certsUrl = new URL(`https://${teamDomain.replace(/^https?:\/\//, "")}/cdn-cgi/access/certs`);
8
+ jwks = createRemoteJWKSet(certsUrl);
9
+ }
10
+ return {
11
+ name: "cf-access",
12
+ options,
13
+ async getSession(req) {
14
+ const emailHeader = req.headers.get("cf-access-authenticated-user-email");
15
+ const jwtAssertion = req.headers.get("cf-access-jwt-assertion");
16
+ if (!emailHeader && !jwtAssertion) return null;
17
+ let email = emailHeader?.toLowerCase().trim();
18
+ let name;
19
+ let customClaims = {};
20
+ if (jwtAssertion && jwks) try {
21
+ const verifyOpts = {};
22
+ if (aud) verifyOpts.audience = aud;
23
+ if (teamDomain) verifyOpts.issuer = `https://${teamDomain.replace(/^https?:\/\//, "")}`;
24
+ const { payload } = await jwtVerify(jwtAssertion, jwks, verifyOpts);
25
+ if (payload["email"] && typeof payload["email"] === "string") email = payload["email"].toLowerCase().trim();
26
+ if (payload["name"] && typeof payload["name"] === "string") name = payload["name"];
27
+ customClaims = payload;
28
+ } catch {
29
+ return null;
30
+ }
31
+ const userEmail = email || "cf-access-user";
32
+ const isAdmin = adminEmails && adminEmails.length > 0 ? adminEmails.map((e) => e.toLowerCase()).includes(userEmail) : true;
33
+ return {
34
+ userId: userEmail,
35
+ email: userEmail,
36
+ name: name || userEmail.split("@")[0],
37
+ roles: isAdmin ? [defaultRole] : ["viewer"],
38
+ permissions: isAdmin ? ["*"] : [],
39
+ userType: "employee",
40
+ metadata: customClaims
41
+ };
42
+ },
43
+ handleUnauthorized(req) {
44
+ if (loginUrl) {
45
+ try {
46
+ if (req && req.url) {
47
+ const origin = new URL(req.url).origin;
48
+ const fullUrl = new URL(loginUrl, origin).toString();
49
+ return Response.redirect(fullUrl, 302);
50
+ }
51
+ } catch {}
52
+ try {
53
+ return Response.redirect(loginUrl, 302);
54
+ } catch {
55
+ return new Response(null, {
56
+ status: 302,
57
+ headers: { Location: loginUrl }
58
+ });
59
+ }
60
+ }
61
+ return new Response("Unauthorized Cloudflare Access Identity", { status: 401 });
62
+ }
63
+ };
64
+ }
65
+ //#endregion
66
+ export { cfAccessAuth };
@@ -0,0 +1,8 @@
1
+ import { AuthAdapter, UserSessionContext } from "../types.mjs";
2
+ //#region src/adapters/mock.d.ts
3
+ export interface MockAuthOptions {
4
+ session?: Partial<UserSessionContext> | null;
5
+ unauthorizedResponse?: Response;
6
+ }
7
+ export declare function mockAuth(options?: MockAuthOptions | Partial<UserSessionContext>): AuthAdapter;
8
+ //#endregion
@@ -0,0 +1,31 @@
1
+ //#region src/adapters/mock.ts
2
+ function mockAuth(options = {}) {
3
+ const opts = "session" in options || "unauthorizedResponse" in options ? options : { session: options };
4
+ const defaultSession = {
5
+ userId: "mock-user-id",
6
+ email: "dev@rimelight.com",
7
+ name: "Mock Dev User",
8
+ avatar: "https://cdn.rimelight.com/Images/default_avatar.png",
9
+ roles: ["admin", "editor"],
10
+ permissions: ["*"],
11
+ userType: "employee",
12
+ metadata: { isMock: true }
13
+ };
14
+ return {
15
+ name: "mock",
16
+ options: opts,
17
+ async getSession() {
18
+ if (opts.session === null) return null;
19
+ return {
20
+ ...defaultSession,
21
+ ...opts.session
22
+ };
23
+ },
24
+ handleUnauthorized() {
25
+ if (opts.unauthorizedResponse) return opts.unauthorizedResponse;
26
+ return new Response("Unauthorized Mock Session", { status: 401 });
27
+ }
28
+ };
29
+ }
30
+ //#endregion
31
+ export { mockAuth };
@@ -0,0 +1,8 @@
1
+ import { AuthAdapter, UserSessionContext, UserType } from "./types.mjs";
2
+ import { Auth0AdapterOptions, Auth0AuthorizeUrlOptions, Auth0CodeExchangeOptions, Auth0PasswordLoginOptions, Auth0RefreshTokenOptions, Auth0TokenResponse, auth0Auth, createAuth0AuthorizeUrl, createAuth0LogoutUrl, createSessionToken, decodeJwt, exchangeAuth0Code, loginWithAuth0Password, mapAuth0PayloadToSession, refreshAuth0Token, verifyAuth0Jwt, verifySessionToken } from "./adapters/auth0.mjs";
3
+ import { CfAccessAdapterOptions, cfAccessAuth } from "./adapters/cf-access.mjs";
4
+ import { MockAuthOptions, mockAuth } from "./adapters/mock.mjs";
5
+ import { AccessControl, AccessRole, StatementMap, adminAc, createAccessControl, createPermissions, defaultStatements, evaluateAccess, hasPermission, hasRole, memberAc, ownerAc } from "./permissions/index.mjs";
6
+ import { RESTRICTED_SET, STANDARD_RESTRICTED_GROUPS, createRestrictedUsernameSet, normalizeUsername } from "./plugins/reserved-usernames/index.mjs";
7
+ import { CONSTRUCTION_GUEST_COOKIE, GuestEnv, isConstructionGuest, signInConstructionGuest } from "./plugins/construction-guest/index.mjs";
8
+ export { AccessControl, AccessRole, Auth0AdapterOptions, Auth0AuthorizeUrlOptions, Auth0CodeExchangeOptions, Auth0PasswordLoginOptions, Auth0RefreshTokenOptions, Auth0TokenResponse, AuthAdapter, CONSTRUCTION_GUEST_COOKIE, CfAccessAdapterOptions, GuestEnv, MockAuthOptions, RESTRICTED_SET, STANDARD_RESTRICTED_GROUPS, StatementMap, UserSessionContext, UserType, adminAc, auth0Auth, cfAccessAuth, createAccessControl, createAuth0AuthorizeUrl, createAuth0LogoutUrl, createPermissions, createRestrictedUsernameSet, createSessionToken, decodeJwt, defaultStatements, evaluateAccess, exchangeAuth0Code, hasPermission, hasRole, isConstructionGuest, loginWithAuth0Password, mapAuth0PayloadToSession, memberAc, mockAuth, normalizeUsername, ownerAc, refreshAuth0Token, signInConstructionGuest, verifyAuth0Jwt, verifySessionToken };
package/dist/index.mjs ADDED
@@ -0,0 +1,8 @@
1
+ import { auth0Auth, createAuth0AuthorizeUrl, createAuth0LogoutUrl, createSessionToken, decodeJwt, exchangeAuth0Code, loginWithAuth0Password, mapAuth0PayloadToSession, refreshAuth0Token, verifyAuth0Jwt, verifySessionToken } from "./adapters/auth0.mjs";
2
+ import { cfAccessAuth } from "./adapters/cf-access.mjs";
3
+ import { mockAuth } from "./adapters/mock.mjs";
4
+ import "./types.mjs";
5
+ import { AccessControl, adminAc, createAccessControl, createPermissions, defaultStatements, evaluateAccess, hasPermission, hasRole, memberAc, ownerAc } from "./permissions/index.mjs";
6
+ import { RESTRICTED_SET, STANDARD_RESTRICTED_GROUPS, createRestrictedUsernameSet, normalizeUsername } from "./plugins/reserved-usernames/index.mjs";
7
+ import { CONSTRUCTION_GUEST_COOKIE, isConstructionGuest, signInConstructionGuest } from "./plugins/construction-guest/index.mjs";
8
+ export { AccessControl, CONSTRUCTION_GUEST_COOKIE, RESTRICTED_SET, STANDARD_RESTRICTED_GROUPS, adminAc, auth0Auth, cfAccessAuth, createAccessControl, createAuth0AuthorizeUrl, createAuth0LogoutUrl, createPermissions, createRestrictedUsernameSet, createSessionToken, decodeJwt, defaultStatements, evaluateAccess, exchangeAuth0Code, hasPermission, hasRole, isConstructionGuest, loginWithAuth0Password, mapAuth0PayloadToSession, memberAc, mockAuth, normalizeUsername, ownerAc, refreshAuth0Token, signInConstructionGuest, verifyAuth0Jwt, verifySessionToken };
@@ -0,0 +1,46 @@
1
+ import { UserSessionContext } from "../types.mjs";
2
+ //#region src/permissions/index.d.ts
3
+ export declare function hasRole(session: UserSessionContext | null | undefined, requiredRoles: string | string[]): boolean;
4
+ export declare function hasPermission(session: UserSessionContext | null | undefined, requiredPermission: string): boolean;
5
+ export declare function evaluateAccess(session: UserSessionContext | null | undefined, constraints: {
6
+ roles?: string[];
7
+ permission?: string;
8
+ }): boolean;
9
+ export type StatementMap = Record<string, readonly string[]>;
10
+ export interface AccessRole<TStatements extends StatementMap> {
11
+ statements: Partial<{ [K in keyof TStatements]: TStatements[K][number][]; }>;
12
+ can(statement: string, action: string): boolean;
13
+ }
14
+ export declare class AccessControl<TStatements extends StatementMap> {
15
+ readonly statements: TStatements;
16
+ constructor(statements: TStatements);
17
+ newRole(permissions: Partial<{ [K in keyof TStatements]: TStatements[K][number][]; }>): AccessRole<TStatements>;
18
+ }
19
+ export declare function createAccessControl<const TStatements extends StatementMap>(statements: TStatements): AccessControl<TStatements>;
20
+ export declare const defaultStatements: {
21
+ user: readonly ["create", "list", "set-role", "ban", "impersonate", "delete", "setRole"];
22
+ };
23
+ export declare const ownerAc: {
24
+ statements: {
25
+ user: string[];
26
+ };
27
+ };
28
+ export declare const adminAc: {
29
+ statements: {
30
+ user: string[];
31
+ };
32
+ };
33
+ export declare const memberAc: {
34
+ statements: {
35
+ user: string[];
36
+ };
37
+ };
38
+ export declare function createPermissions<const TStatements extends StatementMap>(statements: TStatements): {
39
+ ac: AccessControl<{
40
+ user: readonly ["create", "list", "set-role", "ban", "impersonate", "delete", "setRole"];
41
+ } & TStatements>;
42
+ statements: {
43
+ user: readonly ["create", "list", "set-role", "ban", "impersonate", "delete", "setRole"];
44
+ } & TStatements;
45
+ };
46
+ //#endregion
@@ -0,0 +1,83 @@
1
+ //#region src/permissions/index.ts
2
+ function hasRole(session, requiredRoles) {
3
+ if (!session || !session.roles || session.roles.length === 0) return false;
4
+ const normalized = (Array.isArray(requiredRoles) ? requiredRoles : [requiredRoles]).map((r) => r.toLowerCase());
5
+ if (normalized.includes("*")) return true;
6
+ return session.roles.some((r) => normalized.includes(r.toLowerCase()));
7
+ }
8
+ function hasPermission(session, requiredPermission) {
9
+ if (!session || !session.permissions || session.permissions.length === 0) return false;
10
+ if (session.permissions.includes("*") || session.permissions.includes("admin")) return true;
11
+ if (session.permissions.includes(requiredPermission)) return true;
12
+ const [resource] = requiredPermission.split(":");
13
+ if (resource && session.permissions.includes(`${resource}:*`)) return true;
14
+ return false;
15
+ }
16
+ function evaluateAccess(session, constraints) {
17
+ if (constraints.roles && constraints.roles.length > 0) {
18
+ if (!hasRole(session, constraints.roles)) return false;
19
+ }
20
+ if (constraints.permission) {
21
+ if (!hasPermission(session, constraints.permission)) return false;
22
+ }
23
+ return true;
24
+ }
25
+ var AccessControl = class {
26
+ statements;
27
+ constructor(statements) {
28
+ this.statements = statements;
29
+ }
30
+ newRole(permissions) {
31
+ const stmts = permissions;
32
+ return {
33
+ statements: stmts,
34
+ can(statement, action) {
35
+ const allowed = stmts[statement] ?? [];
36
+ return allowed.includes(action) || allowed.includes("*");
37
+ }
38
+ };
39
+ }
40
+ };
41
+ function createAccessControl(statements) {
42
+ return new AccessControl(statements);
43
+ }
44
+ const defaultStatements = { user: [
45
+ "create",
46
+ "list",
47
+ "set-role",
48
+ "ban",
49
+ "impersonate",
50
+ "delete",
51
+ "setRole"
52
+ ] };
53
+ const ownerAc = { statements: { user: [
54
+ "create",
55
+ "list",
56
+ "set-role",
57
+ "ban",
58
+ "impersonate",
59
+ "delete",
60
+ "setRole"
61
+ ] } };
62
+ const adminAc = { statements: { user: [
63
+ "create",
64
+ "list",
65
+ "set-role",
66
+ "ban",
67
+ "impersonate",
68
+ "delete",
69
+ "setRole"
70
+ ] } };
71
+ const memberAc = { statements: { user: ["create", "list"] } };
72
+ function createPermissions(statements) {
73
+ const mergedStatements = {
74
+ ...defaultStatements,
75
+ ...statements
76
+ };
77
+ return {
78
+ ac: createAccessControl(mergedStatements),
79
+ statements: mergedStatements
80
+ };
81
+ }
82
+ //#endregion
83
+ export { AccessControl, adminAc, createAccessControl, createPermissions, defaultStatements, evaluateAccess, hasPermission, hasRole, memberAc, ownerAc };
@@ -0,0 +1,8 @@
1
+ //#region src/plugins/construction-guest/index.d.ts
2
+ export declare const CONSTRUCTION_GUEST_COOKIE = "rimelight-construction-guest";
3
+ export type GuestEnv = {
4
+ CONSTRUCTION_PASSPHRASE?: string;
5
+ };
6
+ export declare const isConstructionGuest: (c: any) => Promise<boolean>;
7
+ export declare const signInConstructionGuest: (c: any, passphrase: string, rememberMe?: boolean) => Promise<boolean>;
8
+ //#endregion