@ouim/logto-authkit 0.3.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.
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import type { NavigationOptions } from './types.js';
3
+ type NavigateFunction = (url: string, options?: NavigationOptions) => void;
4
+ interface NavigationProviderProps {
5
+ children: React.ReactNode;
6
+ customNavigate?: NavigateFunction;
7
+ }
8
+ export declare const NavigationProvider: ({ children, customNavigate }: NavigationProviderProps) => import("react/jsx-runtime").JSX.Element;
9
+ export declare const useNavigation: () => NavigateFunction;
10
+ export {};
@@ -0,0 +1,53 @@
1
+ import type { AuthContext, AuthPayload } from './types.js';
2
+ export type AuthorizationMode = 'all' | 'any';
3
+ export interface ScopeCheckOptions {
4
+ mode?: AuthorizationMode;
5
+ }
6
+ export interface RoleCheckOptions {
7
+ claimKeys?: string[];
8
+ }
9
+ type AuthSubject = AuthContext | AuthPayload | null | undefined;
10
+ /**
11
+ * Return `true` when the token payload contains the requested scopes.
12
+ *
13
+ * Accepts either a raw `AuthPayload` or a full `AuthContext`. Scope comparison is
14
+ * whitespace-delimited to match the standard OAuth `scope` claim shape used by Logto.
15
+ *
16
+ * @example
17
+ * hasScopes(auth.payload, ['read:profile', 'write:profile'])
18
+ * hasScopes(auth, ['read:profile', 'write:profile'], { mode: 'any' })
19
+ */
20
+ export declare function hasScopes(subject: AuthSubject, scopes: string | string[], options?: ScopeCheckOptions): boolean;
21
+ /**
22
+ * Assert that the token payload contains the requested scopes.
23
+ *
24
+ * Throws a descriptive error listing the missing scopes when the check fails.
25
+ *
26
+ * @example
27
+ * requireScopes(auth, ['read:profile', 'write:profile'])
28
+ * requireScopes(payload, ['admin:read', 'admin:write'], { mode: 'any' })
29
+ */
30
+ export declare function requireScopes(subject: AuthSubject, scopes: string | string[], options?: ScopeCheckOptions): void;
31
+ /**
32
+ * Return `true` when the token payload contains the requested role.
33
+ *
34
+ * By default the helper checks `roles` first and then `role`. Override
35
+ * `claimKeys` when your tenant emits roles under a custom claim name.
36
+ *
37
+ * @example
38
+ * hasRole(auth, 'admin')
39
+ * hasRole(auth.payload, 'support', { claimKeys: ['https://example.com/roles'] })
40
+ */
41
+ export declare function hasRole(subject: AuthSubject, role: string, options?: RoleCheckOptions): boolean;
42
+ /**
43
+ * Assert that the token payload contains the requested role.
44
+ *
45
+ * Throws a descriptive error when the role claim is missing or does not contain
46
+ * the required role.
47
+ *
48
+ * @example
49
+ * requireRole(auth, 'admin')
50
+ * requireRole(payload, 'tenant:owner', { claimKeys: ['https://example.com/roles'] })
51
+ */
52
+ export declare function requireRole(subject: AuthSubject, role: string, options?: RoleCheckOptions): void;
53
+ export {};
@@ -0,0 +1,247 @@
1
+ /**
2
+ * CSRF Protection Helpers for `@ouim/logto-authkit`
3
+ *
4
+ * Implements the **Double-Submit Cookie** pattern — the most portable stateless
5
+ * CSRF defence that requires no server-side session storage.
6
+ *
7
+ * ─────────────────────────────────────────────────────────────────────────────
8
+ * THREAT MODEL
9
+ * ─────────────────────────────────────────────────────────────────────────────
10
+ *
11
+ * WHAT IS CSRF?
12
+ * A Cross-Site Request Forgery attack tricks a victim's authenticated browser
13
+ * into making a state-changing request (POST/PUT/DELETE) to your API. Because
14
+ * browsers automatically include cookies with cross-origin requests, the server
15
+ * sees what appears to be a legitimate authenticated request even though the
16
+ * user never intended to make it.
17
+ *
18
+ * JWT cookies are NOT immune. If `logto_authtoken` is a `SameSite=Lax` cookie
19
+ * (the browser default when `SameSite` is omitted), cross-site top-level
20
+ * navigations (e.g. a form POST from an attacker's page) will include it.
21
+ * Even `SameSite=Strict` cookies are sent in some redirect flows and are not
22
+ * universally enforced across all browser versions.
23
+ *
24
+ * HOW THE DOUBLE-SUBMIT COOKIE PATTERN WORKS:
25
+ * 1. The server generates a random token and sets it in a **non-HttpOnly**
26
+ * cookie (the browser's same-origin policy keeps it readable only to your
27
+ * own JavaScript, not to scripts on other origins).
28
+ * 2. For every mutating request (POST, PUT, PATCH, DELETE), client-side JS
29
+ * reads the CSRF cookie value and sends it as a custom request header
30
+ * (e.g. `X-CSRF-Token`).
31
+ * 3. The server middleware compares the header value to the cookie value.
32
+ * A mismatch → 403 Forbidden.
33
+ *
34
+ * WHY AN ATTACKER CAN'T FORGE THIS:
35
+ * - Cross-origin JavaScript cannot read cookies from your domain
36
+ * (same-origin policy).
37
+ * - Cross-origin requests cannot set custom headers without a successful
38
+ * CORS preflight, which your server controls.
39
+ * - An attacker who can read your cookies has already achieved XSS on your
40
+ * domain — CSRF is no longer the threat at that point.
41
+ *
42
+ * LIMITATIONS / NOTES:
43
+ * - CSRF protection is complementary to, not a replacement for, a strict
44
+ * Content-Security-Policy and XSS hardening.
45
+ * - `SameSite=Strict` on the *auth* cookie provides strong CSRF protection
46
+ * on its own for modern browsers: the browser withholds the cookie on all
47
+ * cross-site requests, including cross-site form POSTs. The double-submit
48
+ * pattern here is defence-in-depth for older browsers and programmatic
49
+ * enforcement when you want to verify CSRF explicitly in middleware logic.
50
+ * - The CSRF cookie is intentionally **not** `HttpOnly` — the client JS
51
+ * must be able to read it. Do not store anything sensitive in it.
52
+ * - Safe HTTP methods (GET, HEAD, OPTIONS, TRACE) are exempt from CSRF
53
+ * validation. Ensure your API does not use GET for state changes.
54
+ *
55
+ * ─────────────────────────────────────────────────────────────────────────────
56
+ * QUICK-START — Express
57
+ * ─────────────────────────────────────────────────────────────────────────────
58
+ *
59
+ * ```ts
60
+ * import express from 'express';
61
+ * import { createExpressAuthMiddleware } from '@ouim/logto-authkit/server';
62
+ * import { createCsrfMiddleware, generateCsrfToken, buildCsrfCookieHeader } from '@ouim/logto-authkit/server';
63
+ *
64
+ * const app = express();
65
+ *
66
+ * // Mount CSRF middleware on all routes that mutate state.
67
+ * // The middleware sets the CSRF cookie on GET requests (if absent)
68
+ * // and validates it on POST/PUT/PATCH/DELETE.
69
+ * app.use(createCsrfMiddleware());
70
+ *
71
+ * // Auth middleware can be layered on top.
72
+ * app.use('/api', createExpressAuthMiddleware({ logtoUrl: process.env.LOGTO_ENDPOINT }));
73
+ * ```
74
+ *
75
+ * Client-side fetch helper:
76
+ * ```ts
77
+ * function getCsrfToken(): string {
78
+ * return document.cookie
79
+ * .split('; ')
80
+ * .find(c => c.startsWith('logto_csrf_token='))
81
+ * ?.split('=')[1] ?? '';
82
+ * }
83
+ *
84
+ * // HTTP headers are case-insensitive; use lowercase to match CSRF_HEADER_NAME
85
+ * await fetch('/api/data', {
86
+ * method: 'POST',
87
+ * headers: { 'x-csrf-token': getCsrfToken(), 'Content-Type': 'application/json' },
88
+ * body: JSON.stringify({ ... }),
89
+ * });
90
+ * ```
91
+ *
92
+ * ─────────────────────────────────────────────────────────────────────────────
93
+ * QUICK-START — Next.js
94
+ * ─────────────────────────────────────────────────────────────────────────────
95
+ *
96
+ * ```ts
97
+ * // app/api/data/route.ts
98
+ * import { NextRequest, NextResponse } from 'next/server';
99
+ * import { verifyCsrfToken, generateCsrfToken, buildCsrfCookieHeader } from '@ouim/logto-authkit/server';
100
+ *
101
+ * // GET — issue a fresh CSRF token
102
+ * export async function GET() {
103
+ * const token = generateCsrfToken();
104
+ * return new Response(JSON.stringify({ csrfToken: token }), {
105
+ * headers: { 'Set-Cookie': buildCsrfCookieHeader(token) },
106
+ * });
107
+ * }
108
+ *
109
+ * // POST — validate the CSRF token before processing
110
+ * export async function POST(request: NextRequest) {
111
+ * const csrf = verifyCsrfToken(request);
112
+ * if (!csrf.valid) {
113
+ * return NextResponse.json({ error: csrf.error }, { status: 403 });
114
+ * }
115
+ * // ... process the request
116
+ * }
117
+ * ```
118
+ */
119
+ import type { ExpressRequest, ExpressResponse, ExpressNext, NextRequest } from './types.js';
120
+ /** Default name for the CSRF token cookie (non-HttpOnly, readable by JS). */
121
+ export declare const CSRF_COOKIE_NAME = "logto_csrf_token";
122
+ /** Default request header that clients must send the CSRF token in. */
123
+ export declare const CSRF_HEADER_NAME = "x-csrf-token";
124
+ /**
125
+ * Generate a cryptographically random CSRF token string.
126
+ *
127
+ * Uses `randomUUID` from `node:crypto` (available since Node.js 14.17,
128
+ * works in both CJS and ESM builds). Provides 122 bits of entropy —
129
+ * sufficient for CSRF tokens.
130
+ *
131
+ * @example
132
+ * import { generateCsrfToken } from '@ouim/logto-authkit/server';
133
+ *
134
+ * const csrfToken = generateCsrfToken();
135
+ * // Store it in a cookie or send it to the client in a bootstrap response
136
+ */
137
+ export declare function generateCsrfToken(): string;
138
+ /**
139
+ * Build a `Set-Cookie` header string for the CSRF token cookie.
140
+ *
141
+ * The cookie is deliberately **not** `HttpOnly` — client-side JavaScript must
142
+ * be able to read this value and send it as a request header. An attacker on
143
+ * a different origin cannot read it because of the browser's same-origin policy.
144
+ *
145
+ * @param {string} token - The CSRF token value (from `generateCsrfToken()`).
146
+ * @param {object} [options]
147
+ * @param {string} [options.cookieName='logto_csrf_token'] - Cookie name.
148
+ * @param {number} [options.maxAge=86400] - Cookie lifetime in seconds (default: 1 day).
149
+ * @param {string} [options.domain] - Cookie domain (omit for current host).
150
+ * @param {string} [options.path='/'] - Cookie path.
151
+ * @param {'Strict'|'Lax'} [options.sameSite='Strict'] - SameSite policy.
152
+ * `'None'` is intentionally excluded: CSRF cookies sent to third parties
153
+ * defeat the purpose of the double-submit pattern.
154
+ *
155
+ * @returns {string} A complete `Set-Cookie` header value.
156
+ *
157
+ * @example
158
+ * import { generateCsrfToken, buildCsrfCookieHeader } from '@ouim/logto-authkit/server';
159
+ *
160
+ * const token = generateCsrfToken();
161
+ * const cookie = buildCsrfCookieHeader(token, { sameSite: 'Strict' });
162
+ * res.setHeader('Set-Cookie', cookie);
163
+ */
164
+ export declare function buildCsrfCookieHeader(token: string, options?: {
165
+ cookieName?: string;
166
+ maxAge?: number;
167
+ domain?: string;
168
+ path?: string;
169
+ sameSite?: 'Strict' | 'Lax';
170
+ }): string;
171
+ /** Options for {@link createCsrfMiddleware}. */
172
+ export interface CsrfMiddlewareOptions {
173
+ /** Cookie name to read/write the CSRF token. Default: `'logto_csrf_token'`. */
174
+ cookieName?: string;
175
+ /** Request header clients must send the CSRF token in. Default: `'x-csrf-token'`. */
176
+ headerName?: string;
177
+ /** Cookie domain to set when issuing a new CSRF token. */
178
+ domain?: string;
179
+ /** Cookie path. Default: `'/'`. */
180
+ path?: string;
181
+ /** Cookie `SameSite` policy. Default: `'Strict'`. */
182
+ sameSite?: 'Strict' | 'Lax';
183
+ /** Cookie lifetime in seconds. Default: `86400` (1 day). */
184
+ maxAge?: number;
185
+ }
186
+ /**
187
+ * Express middleware that implements CSRF double-submit cookie protection.
188
+ *
189
+ * **Behaviour:**
190
+ * - **Safe methods** (GET, HEAD, OPTIONS, TRACE): if no CSRF cookie is present,
191
+ * a new token is generated and set via `Set-Cookie`. The request is passed
192
+ * through unconditionally.
193
+ * - **Unsafe methods** (POST, PUT, PATCH, DELETE, …): the `X-CSRF-Token` header
194
+ * value is compared to the CSRF cookie value. A mismatch returns `403 Forbidden`.
195
+ *
196
+ * Mount this middleware **before** your auth middleware so CSRF errors are caught
197
+ * even on unauthenticated requests (defence-in-depth).
198
+ *
199
+ * @example
200
+ * import { createCsrfMiddleware } from '@ouim/logto-authkit/server';
201
+ *
202
+ * // Apply globally
203
+ * app.use(createCsrfMiddleware());
204
+ *
205
+ * // Apply only to API routes
206
+ * app.use('/api', createCsrfMiddleware({ sameSite: 'Lax' }));
207
+ */
208
+ export declare function createCsrfMiddleware(options?: CsrfMiddlewareOptions): (req: ExpressRequest, res: ExpressResponse, next: ExpressNext) => void;
209
+ /** Result returned by {@link verifyCsrfToken}. */
210
+ export interface CsrfVerifyResult {
211
+ /** `true` if the CSRF token is present and matches. */
212
+ valid: boolean;
213
+ /** Human-readable reason for failure (only set when `valid` is `false`). */
214
+ error?: string;
215
+ }
216
+ /**
217
+ * Verify the CSRF double-submit token for a Next.js request.
218
+ *
219
+ * Compares the value in the `X-CSRF-Token` request header against the
220
+ * `logto_csrf_token` cookie. Returns `{ valid: true }` on success or
221
+ * `{ valid: false, error: '...' }` on failure.
222
+ *
223
+ * Intended for use in Next.js Route Handlers and Middleware for mutating
224
+ * endpoints (POST, PUT, PATCH, DELETE).
225
+ *
226
+ * @param {NextRequest} request - The incoming Next.js request object.
227
+ * @param {object} [options]
228
+ * @param {string} [options.cookieName='logto_csrf_token'] - Cookie name to read.
229
+ * @param {string} [options.headerName='x-csrf-token'] - Header name to read.
230
+ *
231
+ * @returns {CsrfVerifyResult}
232
+ *
233
+ * @example
234
+ * import { verifyCsrfToken } from '@ouim/logto-authkit/server';
235
+ *
236
+ * export async function POST(request: NextRequest) {
237
+ * const csrf = verifyCsrfToken(request);
238
+ * if (!csrf.valid) {
239
+ * return NextResponse.json({ error: csrf.error }, { status: 403 });
240
+ * }
241
+ * // proceed with request handling
242
+ * }
243
+ */
244
+ export declare function verifyCsrfToken(request: NextRequest, options?: {
245
+ cookieName?: string;
246
+ headerName?: string;
247
+ }): CsrfVerifyResult;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const $=require("jose"),D=require("cookie-parser"),O=require("node:crypto");function x(e){return typeof e=="object"&&e!==null&&"get"in e&&typeof e.get=="function"}function K(e){return typeof e=="object"&&e!==null}function L(e){return typeof e=="object"&&e!==null}const l=new Map,P=300*1e3;function J(e){return`${e.replace(/\/+$/,"")}/oidc`}function f(e,t="guest_logto_authtoken"){if(x(e)){const n=e.get(t);return(n==null?void 0:n.value)??void 0}else if(K(e))return e[t]??void 0}function m(e,t="logto_authtoken"){if(x(e)){const n=e.get(t);return(n==null?void 0:n.value)||null}else if(K(e))return e[t]||null;return null}function A(e){if(!L(e))return null;const t=typeof e.get=="function"?e.get("authorization"):e.authorization;return typeof t=="string"&&t.startsWith("Bearer ")?t.slice(7):null}function z(e){let t=e.replace(/-/g,"+").replace(/_/g,"/");const n=t.length%4;return n&&(t+="=".repeat(4-n)),typeof atob<"u"?atob(t):Buffer.from(t,"base64").toString()}async function b(e){const{logtoUrl:t,jwksCacheTtlMs:n=P,skipJwksCache:o=!1}=e,r=E(t),i=Date.now(),s=l.get(r);if(!o&&s&&s.expires>i)return s.keys;try{const a=await fetch(r);if(!a.ok)throw new Error(`Failed to fetch JWKS: ${a.status} ${a.statusText}`);const c=(await a.json()).keys||[];return!o&&n>0?l.set(r,{keys:c,expires:i+n}):l.delete(r),c}catch(a){throw new Error(`Failed to fetch JWKS from ${r}: ${a instanceof Error?a.message:"Unknown error"}`)}}function E(e){return`${J(e)}/jwks`}function B(e){l.delete(E(e))}function V(){l.clear()}function Y(e,t,n){if(!e||e.length===0)throw new Error("No keys found in JWKS");if(t){const r=e.find(i=>i.kid===t);if(r)return r;throw new Error(`Key with kid "${t}" not found in JWKS`)}if(n){const r=e.find(i=>i.alg===n);if(r)return r}const o=e.find(r=>r.kty==="RSA"&&(r.use==="sig"||!r.use));return o||e[0]}function q(e,t){const{logtoUrl:n,audience:o,requiredScope:r}=t,i=typeof e.exp=="number"?e.exp:void 0,s=typeof e.nbf=="number"?e.nbf:void 0,a=J(n);if(e.iss!==a)throw new Error(`Invalid issuer. Expected: ${a}, Got: ${e.iss}`);if(o){const c=e.aud,d=Array.isArray(o)?o:[o];if(!(Array.isArray(c)?c:c!==void 0?[c]:[]).some(y=>d.includes(y)))throw new Error(`Invalid audience. Expected one of: ${JSON.stringify(d)}, Got: ${Array.isArray(c)?JSON.stringify(c):c}`)}const u=Math.floor(Date.now()/1e3);if(i!==void 0&&i<u)throw new Error("Token has expired");if(s!==void 0&&s>u)throw new Error("Token is not yet valid");if(r&&(!e.scope||!e.scope.includes(r)))throw new Error(`Missing required scope: ${r}`)}function Q(e){if(typeof e!="object"||e===null)throw new Error("JWT payload is missing or not an object");const t=e;if(typeof t.sub!="string"||t.sub.trim()==="")throw new Error('JWT payload is missing required field "sub" (user ID)');if(typeof t.iss!="string"||t.iss.trim()==="")throw new Error('JWT payload is missing required field "iss" (issuer)');if(t.exp!==void 0&&typeof t.exp!="number")throw new Error('JWT payload field "exp" must be a number');if(t.nbf!==void 0&&typeof t.nbf!="number")throw new Error('JWT payload field "nbf" must be a number');if(t.aud!==void 0){const n=t.aud;if(!(typeof n=="string"||Array.isArray(n)&&n.every(r=>typeof r=="string")))throw new Error('JWT payload field "aud" must be a string or string[]')}if(t.scope!==void 0&&typeof t.scope!="string")throw new Error('JWT payload field "scope" must be a string')}function X(e){if(!(e instanceof Error))return!1;if(e.message.startsWith("No keys found in JWKS")||e.message.startsWith("Key with kid"))return!0;const t=e.code;return t==="ERR_JWKS_NO_MATCHING_KEY"||t==="ERR_JWS_SIGNATURE_VERIFICATION_FAILED"}async function _(e,t,n,o){const r=typeof t.kid=="string"?t.kid:void 0,i=typeof t.alg=="string"?t.alg:void 0,s=Y(n,r,i),a=await $.importJWK(s,i||"RS256"),{payload:u}=await $.jwtVerify(e,a);return Q(u),q(u,o),{userId:u.sub,isAuthenticated:!0,payload:u,isGuest:!1}}async function w(e,t){const{logtoUrl:n}=t,o=E(n);try{const[r]=e.split(".");if(!r)throw new Error("Invalid JWT format");const i=z(r),s=JSON.parse(i),a=await b(t);try{return await _(e,s,a,t)}catch(u){if(!X(u))throw u;console.warn("[verifyLogtoToken] Key/signature error — invalidating JWKS cache and retrying with fresh keys"),l.delete(o);const c=await b(t);return await _(e,s,c,t)}}catch(r){throw new Error(`Token verification failed: ${r instanceof Error?r.message:"Unknown error"}`)}}function Z(e){const t=D(),n=async(o,r,i)=>{try{let s=m(o.cookies,e.cookieName);if(s||(s=A(o.headers)),!s){if(e.allowGuest){const u=f(o.cookies);return o.auth={userId:null,isAuthenticated:!1,payload:null,isGuest:!0,guestId:u},i()}return r.status(401).json({error:"Authentication required",message:"No token found in cookies or Authorization header"})}const a=await w(s,e);return o.auth=a,i()}catch(s){if(e.allowGuest){const a=f(o.cookies);return o.auth={userId:null,isAuthenticated:!1,payload:null,isGuest:!0,guestId:a||void 0},i()}return r.status(401).json({error:"Authentication failed",message:s instanceof Error?s.message:"Unknown error"})}};return async(o,r,i)=>{o.cookies?await n(o,r,i):t(o,r,async s=>{if(s)return i(s);await n(o,r,i)})}}async function ee(e,t){try{let n=m(e.cookies,t.cookieName);return n||(n=A(e.headers)),n?{success:!0,auth:await w(n,t)}:t.allowGuest?{success:!0,auth:{userId:null,isAuthenticated:!1,payload:null,isGuest:!0,guestId:f(e.cookies)||void 0}}:{success:!1,error:"No token found in cookies or Authorization header"}}catch(n){if(t.allowGuest){const o=f(e.cookies),r=n instanceof Error?n.message:"Unknown error";return console.warn(`[verifyNextAuth] Token verification failed, falling back to guest: ${r}`),{success:!0,auth:{userId:null,isAuthenticated:!1,payload:null,isGuest:!0,guestId:o||void 0}}}return{success:!1,error:n instanceof Error?n.message:"Unknown error"}}}async function te(e,t){let n;if(typeof e=="string")n=e;else{const o=m(e.cookies,t.cookieName)||A(e.headers);if(!o){if(t.allowGuest)return{userId:null,isAuthenticated:!1,payload:null,isGuest:!0,guestId:f(e.cookies)||void 0};throw new Error("No token found in request")}n=o}try{return await w(n,t)}catch(o){if(t.allowGuest&&typeof e=="object")return{userId:null,isAuthenticated:!1,payload:null,isGuest:!0,guestId:f(e.cookies)||void 0};throw o}}function re(e,t={}){const{cookieName:n="logto_authtoken",maxAge:o=10080*60,domain:r,path:i="/",sameSite:s="Strict"}=t;let a=`${n}=${e}; Max-Age=${o}; Path=${i}; HttpOnly; Secure; SameSite=${s}`;return r&&(a+=`; Domain=${r}`),a}const k="logto_csrf_token",v="x-csrf-token",ne=new Set(["GET","HEAD","OPTIONS","TRACE"]);function N(){return O.randomUUID()}function M(e,t={}){const{cookieName:n=k,maxAge:o=1440*60,domain:r,path:i="/",sameSite:s="Strict"}=t;if(/[\r\n;]/.test(e))throw new Error("CSRF token contains invalid characters (newline or semicolon)");let a=`${n}=${e}; Max-Age=${o}; Path=${i}; Secure; SameSite=${s}`;return r&&(a+=`; Domain=${r}`),a}function oe(e={}){const{cookieName:t=k,headerName:n=v,domain:o,path:r="/",sameSite:i="Strict",maxAge:s=1440*60}=e;return(a,u,c)=>{var T,C,I;const d=(a.method??"GET").toUpperCase();if(ne.has(d)){if(!((T=a.cookies)!=null&&T[t])){const U=N(),H=M(U,{cookieName:t,maxAge:s,domain:o,path:r,sameSite:i});typeof u.setHeader=="function"&&u.setHeader("Set-Cookie",H)}return c()}const p=(C=a.cookies)==null?void 0:C[t],h=(I=a.headers)==null?void 0:I[n],y=Array.isArray(h)?h[0]:h;if(!p||!y||p!==y){u.status(403).json({error:"CSRF validation failed",message:`Include the '${n}' request header with the value from the '${t}' cookie.`});return}return c()}}function ie(e,t={}){var s,a;const{cookieName:n=k,headerName:o=v}=t,r=(a=(s=e.cookies)==null?void 0:s.get(n))==null?void 0:a.value,i=e.headers.get(o);return r?i?r!==i?{valid:!1,error:"CSRF token mismatch. The header value does not match the cookie value."}:{valid:!0}:{valid:!1,error:`Missing '${o}' request header. Read the '${n}' cookie value and send it in this header.`}:{valid:!1,error:`CSRF cookie '${n}' not found. Ensure GET requests are made first to receive the CSRF cookie.`}}const R=["roles","role"];function se(e){return typeof e=="object"&&e!==null&&"isAuthenticated"in e}function j(e){return e?se(e)?e.payload:e:null}function S(e){return(Array.isArray(e)?e:[e]).flatMap(t=>t.split(/\s+/)).map(t=>t.trim()).filter(Boolean)}function g(e){return(Array.isArray(e)?e:[e]).flatMap(t=>t.split(/[,\s]+/)).map(t=>t.trim()).filter(Boolean)}function W(e){const t=j(e);return t!=null&&t.scope?S(t.scope):[]}function G(e,t){const n=j(e);if(!n)return[];for(const o of t){const r=n[o];if(Array.isArray(r)&&r.every(i=>typeof i=="string")||typeof r=="string")return g(r)}return[]}function F(e,t,n={}){const o=S(t);if(o.length===0)return!0;const r=W(e),{mode:i="all"}=n;return i==="any"?o.some(s=>r.includes(s)):o.every(s=>r.includes(s))}function ae(e,t,n={}){const o=S(t);if(o.length===0)return;const r=W(e),{mode:i="all"}=n;if(F(e,o,{mode:i}))return;const s=o.filter(u=>!r.includes(u)),a=i==="any"?"at least one of":"all of";throw new Error(`Missing required scopes (${a}: ${o.join(", ")}). Token scopes: ${r.join(", ")||"(none)"}. Missing: ${s.join(", ")||o.join(", ")}`)}function ue(e,t,n={}){var s;const o=g(t);if(o.length===0)return!0;const r=(s=n.claimKeys)!=null&&s.length?n.claimKeys:R,i=G(e,r);return o.every(a=>i.includes(a))}function ce(e,t,n={}){var s;const o=g(t);if(o.length===0)return;const r=(s=n.claimKeys)!=null&&s.length?n.claimKeys:R,i=G(e,r);if(!o.every(a=>i.includes(a)))throw new Error(`Missing required role: ${o.join(", ")}. Checked claims: ${r.join(", ")}. Token roles: ${i.join(", ")||"(none)"}`)}exports.CSRF_COOKIE_NAME=k;exports.CSRF_HEADER_NAME=v;exports.buildAuthCookieHeader=re;exports.buildCsrfCookieHeader=M;exports.clearJwksCache=V;exports.createCsrfMiddleware=oe;exports.createExpressAuthMiddleware=Z;exports.generateCsrfToken=N;exports.hasRole=ue;exports.hasScopes=F;exports.invalidateJwksCache=B;exports.requireRole=ce;exports.requireScopes=ae;exports.verifyAuth=te;exports.verifyCsrfToken=ie;exports.verifyLogtoToken=w;exports.verifyNextAuth=ee;
@@ -0,0 +1,4 @@
1
+ export * from './verify-auth.js';
2
+ export * from './csrf.js';
3
+ export * from './authorization.js';
4
+ export * from './types.js';