@dreadkn1ght123/auth-express 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # @dreadkn1ght123/auth-express
2
+
3
+ Express adapter for `@dreadkn1ght123/auth-core`. Mount `auth.router`, then mount
4
+ `auth.authenticate` before protected application routes. Authorization behavior
5
+ is explicit: pass `{ mode: "html" }` for login redirects or `{ mode: "api" }`
6
+ for JSON 401/403 responses.
7
+
8
+ The logger retains stable event names, including `vvp.auth.login.started`,
9
+ `vvp.auth.callback.succeeded`, `vvp.auth.callback.rejected`,
10
+ `vvp.auth.logout.succeeded`, `vvp.auth.logout.rejected`,
11
+ `vvp.auth.session.refresh_failed`, and `vvp.auth.access.denied`. Rejections add
12
+ stable `stage`, `reason`, error/cause codes, and correlation ID where known.
13
+ Startup emits `vvp.auth.ready` only after configuration, discovery, and
14
+ store checks succeed. Unexpected adapter errors use `vvp.auth.internal_error`.
15
+ These events are diagnostic metadata, never an audit record of provider logout
16
+ completion.
17
+
18
+ Production requires shared implementations of `SessionStore` and
19
+ `AuthTransactionStore`; transaction `consume` must be atomic.
20
+ `MemorySessionStore` and `MemoryAuthTransactionStore` are process-local
21
+ development/test utilities and are rejected by this adapter in production.
22
+
23
+ `AuthTransactionStore.consume(id, expectedState)` must compare the expected
24
+ state and atomically return `{ outcome: "consumed", transaction }` or a safe
25
+ failure outcome while deleting only a matching, unexpired record. A mismatch
26
+ must not consume a valid transaction. Distributed stores may return
27
+ `ambiguous` when they cannot safely distinguish a missing record from an
28
+ already-consumed record; never infer or log transaction IDs or state values.
29
+
30
+ ## SDK-owned browser flow
31
+
32
+ The adapter owns the complete single-provider UX. Applications must not render
33
+ an SSO chooser, local login form, callback error page, or logout landing page.
34
+
35
+ | Default route | Method | Purpose |
36
+ |---|---:|---|
37
+ | `/auth/start` | GET | Safe normal entry; preserves validated `returnTo` and provides iframe fallback |
38
+ | `/auth/login` | GET | Internal transaction creation and redirect to IAM; retained for compatibility |
39
+ | `/auth/callback` | GET | Validates callback, creates session, then returns or redirects to SDK error UI |
40
+ | `/auth/logout` | POST | Deletes local session and starts provider logout |
41
+ | `/auth/signed-out` | GET | Non-automatic signed-out page with explicit “sign in again” action |
42
+ | `/auth/error` | GET | Allowlisted safe error and retry page |
43
+ | `/auth/me` | GET | Token-free public session or 401 |
44
+
45
+ `requireAuth({ mode: "html" })` sends an unauthenticated browser to
46
+ `/auth/start`, not directly to `/auth/login`. `/auth/login` remains the internal
47
+ transaction endpoint; consumers must not hardcode it as their normal entry.
48
+ API mode still returns JSON 401/403.
49
+
50
+ Override routes centrally when needed:
51
+
52
+ ```ts
53
+ const auth = await createVvpAuth({
54
+ config,
55
+ sessionStore,
56
+ transactionStore,
57
+ routes: {
58
+ basePath: "/identity",
59
+ start: "start",
60
+ login: "login",
61
+ callback: "complete",
62
+ logout: "logout",
63
+ signedOut: "signed-out",
64
+ error: "error",
65
+ },
66
+ });
67
+ ```
68
+
69
+ Production `config.publicOrigin` derives exact callback and post-logout
70
+ redirects from these routes. Register both resulting URIs in Keycloak. The
71
+ transaction-cookie path follows `basePath`; an explicit cookie path must
72
+ contain login and callback routes or startup fails with `CONFIG_INVALID`.
73
+
74
+ For server-rendered navigation use `auth.createLoginUrl(returnTo)` or
75
+ `auth.createLoginLink(returnTo)`: both target the SDK start route.
76
+ `createLoginLink` supplies `target: "_top"` and safe `rel`. Login must be a
77
+ browser navigation, never `fetch`/XHR. Invalid external `returnTo` values are
78
+ rejected or normalized by SDK policy; never concatenate one manually.
79
+
80
+ Instance role helpers use configured `clientId`, avoiding a stale hardcoded ID
81
+ after a rename. Prefer `auth.hasClientRole(actor, role)` and
82
+ `auth.requireClientRole(role, behavior)` without `behavior.clientId`;
83
+ module-level helpers and the optional `clientId` behavior field remain
84
+ available for intentional cross-client policies.
85
+
86
+ SDK pages are Russian by default with English locale support, responsive,
87
+ keyboard accessible, independent of app CSS/JavaScript, and protected by a
88
+ strict CSP. Optional `ui` branding (`productName`, local `logoUrl`,
89
+ `supportText`, allowlisted text overrides) is escaped and must contain no HTML
90
+ or remote script dependency. Error pages accept only public allowlisted reasons,
91
+ contain a safe retry to `/auth/start` and optional correlation ID, and never
92
+ expose provider/exception text. The signed-out page never automatically starts
93
+ SSO, preventing an immediate logout/login loop.
94
+
95
+ Public error reasons are limited to `transaction_missing`,
96
+ `transaction_expired`, `transaction_already_used`, `provider_denied`,
97
+ `token_validation_failed`, `provider_unavailable`, `session_failed`,
98
+ `configuration_error`, and `unknown`. They select user text only; detailed
99
+ callback stage/reason remains in safe structured logs and is never copied into
100
+ the browser URL.
101
+ See the workspace [`UPGRADE.md`](../../UPGRADE.md) and
102
+ [`docs/sdk-usage.md`](../../docs/sdk-usage.md) for the `0.2.0` migration.
@@ -0,0 +1,165 @@
1
+ import { type AuthorizationOptions, type AuthConfig, type AuthConfigInput, type AuthLogger, type AuthStoreCapabilities, type Actor, type ServerSession, type SessionStore, type RuntimeEnvironment, type TransactionConsumeOutcome } from "@dreadkn1ght123/auth-core";
2
+ import { Router, type Request, type RequestHandler } from "express";
3
+ declare global {
4
+ namespace Express {
5
+ interface Request {
6
+ vvpActor?: Actor;
7
+ vvpCorrelationId?: string;
8
+ }
9
+ }
10
+ }
11
+ export declare const AUTH_EXPRESS_EVENTS: {
12
+ readonly ready: "vvp.auth.ready";
13
+ readonly loginStarted: "vvp.auth.login.started";
14
+ readonly callbackSucceeded: "vvp.auth.callback.succeeded";
15
+ readonly callbackRejected: "vvp.auth.callback.rejected";
16
+ readonly logoutSucceeded: "vvp.auth.logout.succeeded";
17
+ readonly logoutRejected: "vvp.auth.logout.rejected";
18
+ readonly logoutLocalSessionDeleted: "vvp.auth.logout.local_session_deleted";
19
+ readonly logoutRedirectCreated: "vvp.auth.logout.redirect_created";
20
+ readonly logoutProviderRejected: "vvp.auth.logout.provider_rejected";
21
+ readonly logoutInvalidRedirect: "vvp.auth.logout.invalid_redirect";
22
+ readonly sessionRefreshFailed: "vvp.auth.session.refresh_failed";
23
+ readonly accessDenied: "vvp.auth.access.denied";
24
+ readonly internalError: "vvp.auth.internal_error";
25
+ };
26
+ export interface AuthTransaction {
27
+ id: string;
28
+ state: string;
29
+ nonce: string;
30
+ codeVerifier: string;
31
+ returnTo: string;
32
+ expiresAt: number;
33
+ }
34
+ export type AuthTransactionConsumeOutcome = TransactionConsumeOutcome<AuthTransaction>;
35
+ /** This operation must be implemented as one atomic compare-and-delete. */
36
+ export interface AuthTransactionStore {
37
+ readonly capabilities?: AuthStoreCapabilities;
38
+ set(transaction: AuthTransaction, ttlSeconds: number): Promise<void>;
39
+ consume(id: string, expectedState: string): Promise<AuthTransactionConsumeOutcome>;
40
+ }
41
+ export declare class MemoryAuthTransactionStore implements AuthTransactionStore {
42
+ #private;
43
+ constructor(environment: RuntimeEnvironment, options?: {
44
+ now?: () => number;
45
+ });
46
+ set(transaction: AuthTransaction, ttlSeconds: number): Promise<void>;
47
+ consume(id: string, expectedState: string): Promise<AuthTransactionConsumeOutcome>;
48
+ }
49
+ export interface VvpOidcOperations {
50
+ createAuthorizationRequest(options?: AuthorizationOptions): Promise<{
51
+ authorizationUrl: URL;
52
+ state: string;
53
+ nonce: string;
54
+ codeVerifier: string;
55
+ correlationId?: string;
56
+ }>;
57
+ handleCallback(input: {
58
+ callbackUrl: URL;
59
+ state: string;
60
+ nonce: string;
61
+ codeVerifier: string;
62
+ }): Promise<ServerSession>;
63
+ refreshSession(sessionId: string): Promise<ServerSession>;
64
+ logout(sessionId: string): Promise<URL>;
65
+ }
66
+ export interface AccessBehavior {
67
+ /** html redirects unauthenticated users; api returns JSON 401/403. */
68
+ mode: "html" | "api";
69
+ }
70
+ export interface CreateVvpAuthOptions {
71
+ config: AuthConfigInput;
72
+ sessionStore?: SessionStore;
73
+ transactionStore?: AuthTransactionStore;
74
+ logger?: AuthLogger;
75
+ oidcClient?: VvpOidcOperations;
76
+ cookieName?: string;
77
+ transactionCookieName?: string;
78
+ transactionTtlSeconds?: number;
79
+ refreshBeforeExpirySeconds?: number;
80
+ routes?: AuthRouteOptions;
81
+ /** Safe, optional branding for the SDK-owned browser pages. */
82
+ ui?: AuthUiOptions;
83
+ /** Path for the session cookie. Defaults to /. */
84
+ cookiePath?: string;
85
+ /** Path for the short-lived transaction cookie. Defaults to basePath. */
86
+ transactionCookiePath?: string;
87
+ now?: () => number;
88
+ }
89
+ export interface AuthRouteOptions {
90
+ basePath?: string;
91
+ start?: string;
92
+ login?: string;
93
+ callback?: string;
94
+ logout?: string;
95
+ session?: string;
96
+ signedOut?: string;
97
+ error?: string;
98
+ /** Names accepted by AuthConfigInput.routes; preferred for new integrations. */
99
+ startPath?: string;
100
+ loginPath?: string;
101
+ callbackPath?: string;
102
+ logoutPath?: string;
103
+ sessionPath?: string;
104
+ signedOutPath?: string;
105
+ errorPath?: string;
106
+ cookiePath?: string;
107
+ }
108
+ export interface NormalizedAuthRoutes {
109
+ basePath: string;
110
+ start: string;
111
+ login: string;
112
+ callback: string;
113
+ logout: string;
114
+ session: string;
115
+ signedOut: string;
116
+ error: string;
117
+ }
118
+ export interface AuthUiOptions {
119
+ locale?: "ru" | "en";
120
+ productName?: string;
121
+ logoUrl?: string;
122
+ supportText?: string;
123
+ text?: Partial<{
124
+ continueLogin: string;
125
+ startTitle: string;
126
+ startBody: string;
127
+ signedOutTitle: string;
128
+ signedOutBody: string;
129
+ signInAgain: string;
130
+ errorTitle: string;
131
+ tryAgain: string;
132
+ reference: string;
133
+ }>;
134
+ }
135
+ export interface BrowserLoginLink {
136
+ href: string;
137
+ /** Always escapes a containing iframe during browser navigation. */
138
+ target: "_top";
139
+ rel: "noopener noreferrer";
140
+ }
141
+ export interface VvpAuth {
142
+ config: AuthConfig;
143
+ router: Router;
144
+ authenticate: RequestHandler;
145
+ requireAuth(options: AccessBehavior): RequestHandler;
146
+ requireRealmRole(role: string, options: AccessBehavior): RequestHandler[];
147
+ requireClientRole(role: string, options: AccessBehavior & {
148
+ clientId?: string;
149
+ }): RequestHandler[];
150
+ requireAnyClientRole(roles: readonly string[], options: AccessBehavior & {
151
+ clientId?: string;
152
+ }): RequestHandler[];
153
+ createLoginUrl(returnTo?: string): string;
154
+ createLoginLink(returnTo?: string): BrowserLoginLink;
155
+ hasClientRole(actor: Actor | undefined, role: string): boolean;
156
+ hasAnyClientRole(actor: Actor | undefined, roles: readonly string[]): boolean;
157
+ routes: NormalizedAuthRoutes;
158
+ }
159
+ export declare function getActor(req: Request): Actor | undefined;
160
+ export declare function isAuthenticated(req: Request): boolean;
161
+ export declare function hasRealmRole(actor: Actor | undefined, role: string): boolean;
162
+ export declare function hasClientRole(actor: Actor | undefined, role: string, clientId: string): boolean;
163
+ export declare function hasAnyClientRole(actor: Actor | undefined, roles: readonly string[], clientId: string): boolean;
164
+ export declare function createVvpAuth(options: CreateVvpAuthOptions): Promise<VvpAuth>;
165
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,883 @@
1
+ import { randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
2
+ import { MemorySessionStore, AuthError, classifyAuthError, VvpOidcClient, safeReturnTo, toPublicSession, validateConfig, assertProductionSafeStore, transactionConsumeError, } from "@dreadkn1ght123/auth-core";
3
+ import { Router, } from "express";
4
+ export const AUTH_EXPRESS_EVENTS = {
5
+ ready: "vvp.auth.ready",
6
+ loginStarted: "vvp.auth.login.started",
7
+ callbackSucceeded: "vvp.auth.callback.succeeded",
8
+ callbackRejected: "vvp.auth.callback.rejected",
9
+ logoutSucceeded: "vvp.auth.logout.succeeded",
10
+ logoutRejected: "vvp.auth.logout.rejected",
11
+ logoutLocalSessionDeleted: "vvp.auth.logout.local_session_deleted",
12
+ logoutRedirectCreated: "vvp.auth.logout.redirect_created",
13
+ logoutProviderRejected: "vvp.auth.logout.provider_rejected",
14
+ logoutInvalidRedirect: "vvp.auth.logout.invalid_redirect",
15
+ sessionRefreshFailed: "vvp.auth.session.refresh_failed",
16
+ accessDenied: "vvp.auth.access.denied",
17
+ internalError: "vvp.auth.internal_error",
18
+ };
19
+ export class MemoryAuthTransactionStore {
20
+ #entries = new Map();
21
+ #consumed = new Map();
22
+ #now;
23
+ constructor(environment, options = {}) {
24
+ if (environment === "production") {
25
+ throw new AuthError("SESSION_STORE_UNSAFE", "MemoryAuthTransactionStore must not be used in production");
26
+ }
27
+ this.#now = options.now ?? Date.now;
28
+ }
29
+ async set(transaction, ttlSeconds) {
30
+ this.#entries.set(transaction.id, {
31
+ ...structuredClone(transaction),
32
+ expiresAt: Math.min(transaction.expiresAt, this.#now() + ttlSeconds * 1_000),
33
+ });
34
+ }
35
+ async consume(id, expectedState) {
36
+ for (const [consumedId, expiresAt] of this.#consumed) {
37
+ if (expiresAt <= this.#now())
38
+ this.#consumed.delete(consumedId);
39
+ }
40
+ const transaction = this.#entries.get(id);
41
+ if (!transaction) {
42
+ return {
43
+ status: this.#consumed.has(id) ? "already_consumed" : "not_found",
44
+ };
45
+ }
46
+ if (transaction.expiresAt <= this.#now()) {
47
+ this.#entries.delete(id);
48
+ return { status: "expired" };
49
+ }
50
+ if (!constantTimeEqual(transaction.state, expectedState)) {
51
+ return { status: "state_mismatch" };
52
+ }
53
+ this.#entries.delete(id);
54
+ this.#consumed.set(id, transaction.expiresAt);
55
+ return { status: "ok", transaction: structuredClone(transaction) };
56
+ }
57
+ }
58
+ const NOOP_LOGGER = {
59
+ debug() { },
60
+ info() { },
61
+ warn() { },
62
+ error() { },
63
+ };
64
+ const ID_PATTERN = /^[A-Za-z0-9_-]{32,128}$/;
65
+ const CORRELATION_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
66
+ function escapeHtml(value) {
67
+ return value.replace(/[&<>"']/g, (character) => ({
68
+ "&": "&amp;",
69
+ "<": "&lt;",
70
+ ">": "&gt;",
71
+ '"': "&quot;",
72
+ "'": "&#39;",
73
+ })[character]);
74
+ }
75
+ const PUBLIC_ERROR_REASONS = new Set([
76
+ "transaction_missing", "transaction_expired", "transaction_already_used",
77
+ "provider_denied", "token_validation_failed", "provider_unavailable",
78
+ "session_failed", "configuration_error", "unknown",
79
+ ]);
80
+ const ERROR_MESSAGES = {
81
+ ru: {
82
+ transaction_missing: "Не удалось найти попытку входа. Начните авторизацию заново.",
83
+ transaction_expired: "Время попытки входа истекло. Начните авторизацию заново.",
84
+ transaction_already_used: "Эта ссылка входа уже была использована. Начните новую авторизацию.",
85
+ provider_denied: "Вход был отменён или отклонён системой авторизации.",
86
+ token_validation_failed: "Не удалось проверить данные авторизации. Обратитесь к администратору.",
87
+ provider_unavailable: "Система авторизации временно недоступна. Попробуйте позже.",
88
+ session_failed: "Не удалось создать сеанс приложения. Попробуйте снова.",
89
+ configuration_error: "Сервис входа настроен неверно. Обратитесь к администратору.",
90
+ unknown: "Не удалось завершить вход.",
91
+ },
92
+ en: {
93
+ transaction_missing: "The sign-in attempt could not be found. Start again.",
94
+ transaction_expired: "The sign-in attempt expired. Start again.",
95
+ transaction_already_used: "This sign-in link was already used. Start a new sign-in.",
96
+ provider_denied: "Sign-in was cancelled or denied by the identity provider.",
97
+ token_validation_failed: "The sign-in data could not be verified. Contact an administrator.",
98
+ provider_unavailable: "The identity provider is temporarily unavailable. Try again later.",
99
+ session_failed: "The application session could not be created. Try again.",
100
+ configuration_error: "The sign-in service is misconfigured. Contact an administrator.",
101
+ unknown: "Sign-in could not be completed.",
102
+ },
103
+ };
104
+ function validateUi(options) {
105
+ const locale = options?.locale ?? "ru";
106
+ const defaults = locale === "en" ? {
107
+ continueLogin: "Continue sign-in", startTitle: "Sign in",
108
+ startBody: "Continue to the corporate sign-in service.",
109
+ signedOutTitle: "You have signed out", signedOutBody: "Your session has ended.",
110
+ signInAgain: "Sign in again", errorTitle: "Sign-in error",
111
+ tryAgain: "Try again", reference: "Reference",
112
+ } : {
113
+ continueLogin: "Продолжить вход", startTitle: "Вход в систему",
114
+ startBody: "Продолжите вход через корпоративную систему авторизации.",
115
+ signedOutTitle: "Вы вышли из системы", signedOutBody: "Сеанс работы завершён.",
116
+ signInAgain: "Войти снова", errorTitle: "Ошибка входа",
117
+ tryAgain: "Попробовать снова", reference: "Код обращения",
118
+ };
119
+ let logoUrl;
120
+ if (options?.logoUrl !== undefined) {
121
+ try {
122
+ const parsed = new URL(options.logoUrl, "https://sdk.invalid");
123
+ if (parsed.origin !== "https://sdk.invalid" || !options.logoUrl.startsWith("/") ||
124
+ options.logoUrl.startsWith("//") || parsed.username || parsed.password ||
125
+ parsed.search || parsed.hash)
126
+ throw new Error();
127
+ logoUrl = parsed.pathname;
128
+ }
129
+ catch {
130
+ throw new AuthError("CONFIG_INVALID", "ui.logoUrl must be a local absolute URL without query or fragment");
131
+ }
132
+ }
133
+ return {
134
+ locale,
135
+ productName: options?.productName ?? "VVP",
136
+ supportText: options?.supportText ?? "",
137
+ text: { ...defaults, ...options?.text },
138
+ ...(logoUrl ? { logoUrl } : {}),
139
+ };
140
+ }
141
+ function page(input) {
142
+ const { ui } = input;
143
+ const logo = ui.logoUrl
144
+ ? `<img class="logo" src="${escapeHtml(ui.logoUrl)}" alt="">`
145
+ : "";
146
+ const support = ui.supportText
147
+ ? `<p class="support">${escapeHtml(ui.supportText)}</p>`
148
+ : "";
149
+ const reference = input.correlationId
150
+ ? `<p class="reference"><small>${escapeHtml(ui.text.reference)}: <code>${escapeHtml(input.correlationId)}</code></small></p>`
151
+ : "";
152
+ const script = input.autoNavigate
153
+ ? `<script nonce="${input.nonce}">try{window.top.location.assign(${JSON.stringify(input.actionHref).replace(/</g, "\\u003c")})}catch(e){}</script>`
154
+ : "";
155
+ return `<!doctype html>
156
+ <html lang="${ui.locale}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
157
+ <title>${escapeHtml(input.title)}</title><style nonce="${input.nonce}">
158
+ *{box-sizing:border-box}body{margin:0;background:#f5f6f8;color:#17202a;font:16px/1.5 system-ui,-apple-system,sans-serif}
159
+ main{width:min(92%,32rem);margin:12vh auto;background:#fff;border:1px solid #dfe3e8;border-radius:12px;padding:2rem;box-shadow:0 8px 28px #17202a12}
160
+ .brand{display:flex;align-items:center;gap:.75rem;font-weight:700}.logo{max-width:8rem;max-height:3rem}h1{font-size:1.6rem;margin:1.5rem 0 .5rem}
161
+ .button{display:inline-block;margin-top:1rem;padding:.7rem 1rem;border-radius:6px;background:#175cd3;color:#fff;text-decoration:none;font-weight:650}
162
+ .button:focus{outline:3px solid #84adff;outline-offset:3px}.support,.reference{color:#59636e;margin-top:1.25rem}code{overflow-wrap:anywhere}
163
+ </style></head><body><main>
164
+ <div class="brand">${logo}<span>${escapeHtml(ui.productName)}</span></div>
165
+ <h1 tabindex="-1">${escapeHtml(input.title)}</h1><p>${escapeHtml(input.body)}</p>
166
+ <a class="button" href="${escapeHtml(input.actionHref)}" target="_top" rel="noopener noreferrer">${escapeHtml(input.actionText)}</a>
167
+ ${support}${reference}</main>${script}</body></html>`;
168
+ }
169
+ function opaqueId() {
170
+ return randomBytes(32).toString("base64url");
171
+ }
172
+ function parseCookies(header) {
173
+ const result = {};
174
+ for (const item of header?.split(";") ?? []) {
175
+ const separator = item.indexOf("=");
176
+ if (separator < 1)
177
+ continue;
178
+ const name = item.slice(0, separator).trim();
179
+ try {
180
+ result[name] = decodeURIComponent(item.slice(separator + 1).trim());
181
+ }
182
+ catch {
183
+ // Malformed cookies are ignored.
184
+ }
185
+ }
186
+ return result;
187
+ }
188
+ function constantTimeEqual(left, right) {
189
+ const a = Buffer.from(left);
190
+ const b = Buffer.from(right);
191
+ return a.length === b.length && timingSafeEqual(a, b);
192
+ }
193
+ function normalizePath(value, field) {
194
+ if (!value.startsWith("/") || value.includes("?") || value.includes("#") ||
195
+ value.includes("\\") || value.includes("//")) {
196
+ throw new AuthError("CONFIG_INVALID", `${field} must be a normalized absolute path`);
197
+ }
198
+ const normalized = value.length > 1 ? value.replace(/\/+$/, "") : value;
199
+ if (normalized.split("/").some((segment) => segment === "." || segment === "..")) {
200
+ throw new AuthError("CONFIG_INVALID", `${field} must not contain dot segments`);
201
+ }
202
+ return normalized;
203
+ }
204
+ function joinRoute(basePath, route, fallback) {
205
+ const value = route ?? fallback;
206
+ return normalizePath(value.startsWith("/") ? value : `${basePath}/${value}`, "auth route");
207
+ }
208
+ function normalizeRoutes(input = {}) {
209
+ const basePath = normalizePath(input.basePath ?? "/auth", "routes.basePath");
210
+ const routes = {
211
+ basePath,
212
+ start: joinRoute(basePath, input.startPath ?? input.start, "start"),
213
+ login: joinRoute(basePath, input.loginPath ?? input.login, "login"),
214
+ callback: joinRoute(basePath, input.callbackPath ?? input.callback, "callback"),
215
+ logout: joinRoute(basePath, input.logoutPath ?? input.logout, "logout"),
216
+ session: joinRoute(basePath, input.sessionPath ?? input.session, "me"),
217
+ signedOut: joinRoute(basePath, input.signedOutPath ?? input.signedOut, "signed-out"),
218
+ error: joinRoute(basePath, input.errorPath ?? input.error, "error"),
219
+ };
220
+ const paths = [
221
+ routes.start, routes.login, routes.callback, routes.logout, routes.session,
222
+ routes.signedOut, routes.error,
223
+ ];
224
+ if (new Set(paths).size !== paths.length) {
225
+ throw new AuthError("CONFIG_INVALID", "Authentication routes must be unique");
226
+ }
227
+ return routes;
228
+ }
229
+ function pathContains(cookiePath, requestPath) {
230
+ return cookiePath === "/" || requestPath === cookiePath ||
231
+ requestPath.startsWith(`${cookiePath}/`);
232
+ }
233
+ function requestLanguage(req) {
234
+ const header = req.get("accept-language")?.trim().toLowerCase();
235
+ return header?.startsWith("en") ? "en" : "ru";
236
+ }
237
+ function publicCallbackReason(error) {
238
+ const diagnostic = classifyAuthError(error);
239
+ const value = `${error instanceof AuthError ? error.code : ""} ${diagnostic.reason}`.toLowerCase();
240
+ if (value.includes("denied") || value.includes("access_denied") || value.includes("cancel")) {
241
+ return "provider_denied";
242
+ }
243
+ if (value.includes("config") || value.includes("invalid_client")) {
244
+ return "configuration_error";
245
+ }
246
+ if (value.includes("token") || value.includes("nonce") || value.includes("issuer") ||
247
+ value.includes("audience") || value.includes("signature") || value.includes("claim") ||
248
+ value.includes("subject") || value.includes("azp")) {
249
+ return "token_validation_failed";
250
+ }
251
+ if (value.includes("unavailable") || value.includes("network") ||
252
+ value.includes("timeout") || value.includes("endpoint")) {
253
+ return "provider_unavailable";
254
+ }
255
+ if (value.includes("session") || value.includes("store"))
256
+ return "session_failed";
257
+ return "unknown";
258
+ }
259
+ function asyncHandler(handler) {
260
+ return (req, res, next) => void handler(req, res, next).catch(next);
261
+ }
262
+ export function getActor(req) {
263
+ return req.vvpActor;
264
+ }
265
+ export function isAuthenticated(req) {
266
+ return Boolean(req.vvpActor);
267
+ }
268
+ export function hasRealmRole(actor, role) {
269
+ return actor?.realmRoles.includes(role.toLowerCase()) ?? false;
270
+ }
271
+ export function hasClientRole(actor, role, clientId) {
272
+ return actor?.clientRoles[clientId]?.includes(role.toLowerCase()) ?? false;
273
+ }
274
+ export function hasAnyClientRole(actor, roles, clientId) {
275
+ return roles.some((role) => hasClientRole(actor, role, clientId));
276
+ }
277
+ export async function createVvpAuth(options) {
278
+ // `routes` was originally documented as an Express option. Normalize that
279
+ // shape into the core contract before validation so production receives the
280
+ // same callback/cookie checks as every other configuration source.
281
+ const legacyRoutes = options.routes;
282
+ const configInput = legacyRoutes
283
+ ? {
284
+ ...options.config,
285
+ routes: {
286
+ ...options.config.routes,
287
+ ...(legacyRoutes.basePath !== undefined
288
+ ? { basePath: legacyRoutes.basePath } : {}),
289
+ ...(legacyRoutes.startPath ?? legacyRoutes.start
290
+ ? { startPath: legacyRoutes.startPath ?? legacyRoutes.start } : {}),
291
+ ...(legacyRoutes.loginPath ?? legacyRoutes.login
292
+ ? { loginPath: legacyRoutes.loginPath ?? legacyRoutes.login } : {}),
293
+ ...(legacyRoutes.callbackPath ?? legacyRoutes.callback
294
+ ? { callbackPath: legacyRoutes.callbackPath ?? legacyRoutes.callback } : {}),
295
+ ...(legacyRoutes.logoutPath ?? legacyRoutes.logout
296
+ ? { logoutPath: legacyRoutes.logoutPath ?? legacyRoutes.logout } : {}),
297
+ ...(legacyRoutes.sessionPath ?? legacyRoutes.session
298
+ ? { sessionPath: legacyRoutes.sessionPath ?? legacyRoutes.session } : {}),
299
+ ...(legacyRoutes.signedOutPath ?? legacyRoutes.signedOut
300
+ ? {
301
+ signedOutPath: legacyRoutes.signedOutPath ?? legacyRoutes.signedOut,
302
+ } : {}),
303
+ ...(legacyRoutes.errorPath ?? legacyRoutes.error
304
+ ? { errorPath: legacyRoutes.errorPath ?? legacyRoutes.error } : {}),
305
+ ...(legacyRoutes.cookiePath !== undefined
306
+ ? { cookiePath: legacyRoutes.cookiePath } : {}),
307
+ },
308
+ }
309
+ : options.config;
310
+ const config = validateConfig(configInput);
311
+ const returnToOptions = {
312
+ baseOrigin: config.publicOrigin?.origin ?? config.redirectUri.origin,
313
+ allowedOrigins: config.allowedReturnToOrigins,
314
+ };
315
+ const ui = validateUi({
316
+ locale: config.ui.locale,
317
+ productName: config.ui.productName,
318
+ supportText: config.ui.supportText,
319
+ ...(config.ui.logoUrl ? { logoUrl: config.ui.logoUrl } : {}),
320
+ ...options.ui,
321
+ });
322
+ const now = options.now ?? Date.now;
323
+ const logger = options.logger ?? NOOP_LOGGER;
324
+ const coreRoutes = config.routes;
325
+ const normalizedExtras = normalizeRoutes(legacyRoutes ?? {
326
+ basePath: coreRoutes.basePath,
327
+ startPath: coreRoutes.startPath,
328
+ loginPath: coreRoutes.loginPath,
329
+ callbackPath: coreRoutes.callbackPath,
330
+ logoutPath: coreRoutes.logoutPath,
331
+ sessionPath: coreRoutes.sessionPath,
332
+ signedOutPath: coreRoutes.signedOutPath,
333
+ errorPath: coreRoutes.errorPath,
334
+ });
335
+ const routes = normalizedExtras;
336
+ const cookiePath = normalizePath(options.cookiePath ?? "/", "cookiePath");
337
+ const transactionCookiePath = normalizePath(options.transactionCookiePath ??
338
+ config.routes.cookiePath, "transactionCookiePath");
339
+ if (config.redirectUri.pathname !== routes.callback) {
340
+ throw new AuthError("CONFIG_INVALID", "redirectUri pathname must match the configured callback route");
341
+ }
342
+ if (!pathContains(transactionCookiePath, routes.login) ||
343
+ !pathContains(transactionCookiePath, routes.callback)) {
344
+ throw new AuthError("CONFIG_INVALID", "transactionCookiePath must include the login and callback routes");
345
+ }
346
+ if (config.environment === "production" && !options.sessionStore) {
347
+ throw new AuthError("SESSION_STORE_UNSAFE", "A persistent sessionStore is required in production");
348
+ }
349
+ if (config.environment === "production" && !options.transactionStore) {
350
+ throw new AuthError("SESSION_STORE_UNSAFE", "A shared atomic transactionStore is required in production");
351
+ }
352
+ assertProductionSafeStore(options.sessionStore, config.environment, {
353
+ purpose: "session",
354
+ });
355
+ assertProductionSafeStore(options.transactionStore, config.environment, {
356
+ purpose: "transaction",
357
+ requireAtomicConsume: true,
358
+ });
359
+ const sessionStore = options.sessionStore ?? new MemorySessionStore(config.environment, { now });
360
+ const transactions = options.transactionStore ??
361
+ new MemoryAuthTransactionStore(config.environment, { now });
362
+ const oidc = options.oidcClient ??
363
+ (await VvpOidcClient.discover(config, sessionStore, { logger, now }));
364
+ const cookieName = options.cookieName ?? "vvp_session";
365
+ const transactionCookieName = options.transactionCookieName ?? "vvp_auth_transaction";
366
+ const transactionTtlSeconds = options.transactionTtlSeconds ?? 300;
367
+ if (!Number.isSafeInteger(transactionTtlSeconds) ||
368
+ transactionTtlSeconds < 30 ||
369
+ transactionTtlSeconds > 600) {
370
+ throw new AuthError("CONFIG_INVALID", "transactionTtlSeconds must be between 30 and 600");
371
+ }
372
+ const refreshBeforeExpirySeconds = options.refreshBeforeExpirySeconds ?? 60;
373
+ if (!Number.isSafeInteger(refreshBeforeExpirySeconds) ||
374
+ refreshBeforeExpirySeconds < 0 ||
375
+ refreshBeforeExpirySeconds > 600) {
376
+ throw new AuthError("CONFIG_INVALID", "refreshBeforeExpirySeconds must be between 0 and 600");
377
+ }
378
+ const secure = config.environment === "production";
379
+ const sessionCookie = {
380
+ httpOnly: true,
381
+ secure,
382
+ sameSite: "lax",
383
+ path: cookiePath,
384
+ maxAge: Math.min(config.sessionTtlSeconds, 7 * 24 * 60 * 60) * 1_000,
385
+ };
386
+ const transactionCookie = {
387
+ httpOnly: true,
388
+ secure,
389
+ sameSite: "lax",
390
+ path: transactionCookiePath,
391
+ maxAge: transactionTtlSeconds * 1_000,
392
+ };
393
+ const clearCookieOptions = {
394
+ httpOnly: true,
395
+ secure,
396
+ sameSite: "lax",
397
+ path: cookiePath,
398
+ };
399
+ const clearSession = (res) => res.clearCookie(cookieName, clearCookieOptions);
400
+ const clearTransaction = (res) => res.clearCookie(transactionCookieName, {
401
+ ...clearCookieOptions,
402
+ path: transactionCookiePath,
403
+ });
404
+ logger.info(AUTH_EXPRESS_EVENTS.ready, {
405
+ environment: config.environment,
406
+ secureCookies: secure,
407
+ basePath: routes.basePath,
408
+ callbackPath: routes.callback,
409
+ signedOutPath: routes.signedOut,
410
+ cookiePath,
411
+ transactionCookiePath,
412
+ });
413
+ const correlate = (req, res, next) => {
414
+ const supplied = req.get("x-correlation-id");
415
+ req.vvpCorrelationId =
416
+ supplied && CORRELATION_PATTERN.test(supplied) ? supplied : randomUUID();
417
+ res.set("x-correlation-id", req.vvpCorrelationId);
418
+ next();
419
+ };
420
+ const authenticate = asyncHandler(async (req, res, next) => {
421
+ try {
422
+ const id = parseCookies(req.headers.cookie)[cookieName];
423
+ if (!id || !ID_PATTERN.test(id)) {
424
+ next();
425
+ return;
426
+ }
427
+ let session = await sessionStore.get(id);
428
+ if (!session) {
429
+ clearSession(res);
430
+ next();
431
+ return;
432
+ }
433
+ if (session.tokens.expiresAt !== undefined &&
434
+ session.tokens.expiresAt <=
435
+ now() + refreshBeforeExpirySeconds * 1_000) {
436
+ try {
437
+ session = await oidc.refreshSession(id);
438
+ }
439
+ catch {
440
+ await sessionStore.delete(id);
441
+ clearSession(res);
442
+ logger.warn(AUTH_EXPRESS_EVENTS.sessionRefreshFailed, {
443
+ correlationId: req.vvpCorrelationId,
444
+ });
445
+ next();
446
+ return;
447
+ }
448
+ }
449
+ req.vvpActor = session.actor;
450
+ next();
451
+ }
452
+ catch {
453
+ clearSession(res);
454
+ logger.error(AUTH_EXPRESS_EVENTS.internalError, {
455
+ correlationId: req.vvpCorrelationId,
456
+ operation: "authenticate",
457
+ });
458
+ if (!res.headersSent) {
459
+ res.status(503).json({
460
+ error: "authentication_unavailable",
461
+ correlationId: req.vvpCorrelationId,
462
+ });
463
+ }
464
+ }
465
+ });
466
+ const unauthenticated = (req, res, behavior) => {
467
+ logger.info(AUTH_EXPRESS_EVENTS.accessDenied, {
468
+ correlationId: req.vvpCorrelationId,
469
+ reason: "unauthenticated",
470
+ mode: behavior.mode,
471
+ });
472
+ if (behavior.mode === "html") {
473
+ const returnTo = safeReturnTo(req.originalUrl, returnToOptions);
474
+ res.redirect(302, `${routes.start}?returnTo=${encodeURIComponent(returnTo)}`);
475
+ }
476
+ else {
477
+ res.status(401).json({
478
+ error: "authentication_required",
479
+ correlationId: req.vvpCorrelationId,
480
+ });
481
+ }
482
+ };
483
+ const requireAuth = (behavior) => (req, res, next) => {
484
+ if (req.vvpActor)
485
+ next();
486
+ else
487
+ unauthenticated(req, res, behavior);
488
+ };
489
+ const authorize = (predicate, behavior) => (req, res, next) => {
490
+ if (!req.vvpActor) {
491
+ unauthenticated(req, res, behavior);
492
+ return;
493
+ }
494
+ if (predicate(req.vvpActor)) {
495
+ next();
496
+ return;
497
+ }
498
+ logger.info(AUTH_EXPRESS_EVENTS.accessDenied, {
499
+ correlationId: req.vvpCorrelationId,
500
+ reason: "forbidden",
501
+ mode: behavior.mode,
502
+ });
503
+ if (behavior.mode === "html") {
504
+ res.status(403).type("text").send("Forbidden");
505
+ }
506
+ else {
507
+ res.status(403).json({
508
+ error: "forbidden",
509
+ correlationId: req.vvpCorrelationId,
510
+ });
511
+ }
512
+ };
513
+ const router = Router();
514
+ router.use(correlate);
515
+ const sendPage = (res, html, nonce) => {
516
+ res
517
+ .set("Cache-Control", "no-store")
518
+ .set("Pragma", "no-cache")
519
+ .set("Content-Security-Policy", `default-src 'none'; base-uri 'none'; frame-ancestors *; form-action 'self'; img-src 'self'; style-src 'nonce-${nonce}'; script-src 'nonce-${nonce}'`)
520
+ .set("Referrer-Policy", "no-referrer")
521
+ .set("X-Content-Type-Options", "nosniff")
522
+ .type("html")
523
+ .send(html);
524
+ };
525
+ const localSignedOutUrl = (() => {
526
+ const configuredPublicOrigin = config.publicOrigin;
527
+ return new URL(routes.signedOut, configuredPublicOrigin ?? config.redirectUri).href;
528
+ })();
529
+ const errorRedirect = (res, reason, correlationId) => {
530
+ const query = new URLSearchParams({ reason, correlationId });
531
+ res.redirect(302, `${routes.error}?${query.toString()}`);
532
+ };
533
+ router.get(routes.start, (req, res) => {
534
+ const returnTo = safeReturnTo(typeof req.query["returnTo"] === "string" ? req.query["returnTo"] : undefined, returnToOptions);
535
+ const href = returnTo === "/"
536
+ ? routes.login
537
+ : `${routes.login}?returnTo=${encodeURIComponent(returnTo)}`;
538
+ const nonce = randomBytes(18).toString("base64url");
539
+ sendPage(res, page({
540
+ ui,
541
+ title: ui.text.startTitle,
542
+ body: ui.text.startBody,
543
+ actionHref: href,
544
+ actionText: ui.text.continueLogin,
545
+ nonce,
546
+ autoNavigate: true,
547
+ }), nonce);
548
+ });
549
+ router.get(routes.login, asyncHandler(async (req, res) => {
550
+ const returnTo = safeReturnTo(typeof req.query["returnTo"] === "string"
551
+ ? req.query["returnTo"]
552
+ : undefined, returnToOptions);
553
+ const authorization = await oidc.createAuthorizationRequest();
554
+ const id = opaqueId();
555
+ await transactions.set({
556
+ id,
557
+ state: authorization.state,
558
+ nonce: authorization.nonce,
559
+ codeVerifier: authorization.codeVerifier,
560
+ returnTo,
561
+ expiresAt: now() + transactionTtlSeconds * 1_000,
562
+ }, transactionTtlSeconds);
563
+ res.cookie(transactionCookieName, id, transactionCookie);
564
+ logger.info(AUTH_EXPRESS_EVENTS.loginStarted, {
565
+ correlationId: req.vvpCorrelationId,
566
+ });
567
+ res.redirect(302, authorization.authorizationUrl.href);
568
+ }));
569
+ router.get(routes.callback, asyncHandler(async (req, res) => {
570
+ const state = typeof req.query["state"] === "string" ? req.query["state"] : "";
571
+ const id = parseCookies(req.headers.cookie)[transactionCookieName];
572
+ const transactionValidationReason = !id
573
+ ? "transaction_cookie_missing"
574
+ : !ID_PATTERN.test(id)
575
+ ? "transaction_cookie_malformed"
576
+ : !state
577
+ ? "state_missing"
578
+ : undefined;
579
+ const hasWellFormedTransactionReference = Boolean(id && state && ID_PATTERN.test(id));
580
+ let consumeResult;
581
+ try {
582
+ consumeResult = hasWellFormedTransactionReference
583
+ ? await transactions.consume(id, state)
584
+ : { status: "not_found" };
585
+ }
586
+ catch {
587
+ clearTransaction(res);
588
+ const consumeError = transactionConsumeError({
589
+ status: "ambiguous",
590
+ });
591
+ logger.error(AUTH_EXPRESS_EVENTS.callbackRejected, {
592
+ correlationId: req.vvpCorrelationId,
593
+ stage: consumeError.stage,
594
+ reason: consumeError.reason,
595
+ code: consumeError.code,
596
+ });
597
+ errorRedirect(res, "session_failed", req.vvpCorrelationId);
598
+ return;
599
+ }
600
+ if (consumeResult.status !== "ok") {
601
+ if (!hasWellFormedTransactionReference ||
602
+ consumeResult.status === "expired" ||
603
+ consumeResult.status === "already_consumed" ||
604
+ consumeResult.status === "ambiguous") {
605
+ clearTransaction(res);
606
+ }
607
+ const consumeError = transactionConsumeError(consumeResult);
608
+ logger.warn(AUTH_EXPRESS_EVENTS.callbackRejected, {
609
+ correlationId: req.vvpCorrelationId,
610
+ stage: "transaction_validation",
611
+ reason: transactionValidationReason ?? consumeError.reason,
612
+ code: consumeError.code,
613
+ });
614
+ const publicReason = consumeResult.status === "expired" ? "transaction_expired"
615
+ : consumeResult.status === "already_consumed"
616
+ ? "transaction_already_used"
617
+ : consumeResult.status === "ambiguous"
618
+ ? "session_failed"
619
+ : "transaction_missing";
620
+ errorRedirect(res, publicReason, req.vvpCorrelationId);
621
+ return;
622
+ }
623
+ const transaction = consumeResult.transaction;
624
+ clearTransaction(res);
625
+ try {
626
+ const callbackUrl = new URL(config.redirectUri.href);
627
+ callbackUrl.search = req.originalUrl.includes("?")
628
+ ? req.originalUrl.slice(req.originalUrl.indexOf("?"))
629
+ : "";
630
+ const session = await oidc.handleCallback({
631
+ callbackUrl,
632
+ state: transaction.state,
633
+ nonce: transaction.nonce,
634
+ codeVerifier: transaction.codeVerifier,
635
+ ...(req.vvpCorrelationId
636
+ ? { correlationId: req.vvpCorrelationId }
637
+ : {}),
638
+ });
639
+ res.cookie(cookieName, session.id, sessionCookie);
640
+ logger.info(AUTH_EXPRESS_EVENTS.callbackSucceeded, {
641
+ correlationId: req.vvpCorrelationId,
642
+ stage: "session",
643
+ reason: "created",
644
+ });
645
+ res.redirect(302, transaction.returnTo);
646
+ }
647
+ catch (error) {
648
+ clearSession(res);
649
+ const code = error instanceof AuthError ? error.code : "AUTHORIZATION_FAILED";
650
+ const diagnostic = classifyAuthError(error);
651
+ logger.warn(AUTH_EXPRESS_EVENTS.callbackRejected, {
652
+ correlationId: req.vvpCorrelationId,
653
+ stage: error instanceof AuthError ? error.stage : "callback",
654
+ reason: diagnostic.reason,
655
+ code,
656
+ ...(diagnostic.details.library
657
+ ? { library: diagnostic.details.library }
658
+ : {}),
659
+ ...(diagnostic.details.libraryCode
660
+ ? { libraryCode: diagnostic.details.libraryCode }
661
+ : {}),
662
+ ...(diagnostic.details.httpStatus
663
+ ? { httpStatus: diagnostic.details.httpStatus }
664
+ : {}),
665
+ ...(diagnostic.details.claim
666
+ ? { claim: diagnostic.details.claim }
667
+ : {}),
668
+ });
669
+ errorRedirect(res, publicCallbackReason(error), req.vvpCorrelationId);
670
+ }
671
+ }));
672
+ router.get(routes.signedOut, (_req, res) => {
673
+ const nonce = randomBytes(18).toString("base64url");
674
+ sendPage(res, page({
675
+ ui,
676
+ title: ui.text.signedOutTitle,
677
+ body: ui.text.signedOutBody,
678
+ actionHref: routes.start,
679
+ actionText: ui.text.signInAgain,
680
+ nonce,
681
+ }), nonce);
682
+ });
683
+ router.get(routes.error, (req, res) => {
684
+ const suppliedReason = typeof req.query["reason"] === "string" ? req.query["reason"] : "unknown";
685
+ const reason = PUBLIC_ERROR_REASONS.has(suppliedReason)
686
+ ? suppliedReason
687
+ : "unknown";
688
+ const suppliedCorrelationId = typeof req.query["correlationId"] === "string"
689
+ ? req.query["correlationId"]
690
+ : req.vvpCorrelationId;
691
+ const correlationId = CORRELATION_PATTERN.test(suppliedCorrelationId)
692
+ ? suppliedCorrelationId
693
+ : req.vvpCorrelationId;
694
+ const locale = ui.locale ?? requestLanguage(req);
695
+ const nonce = randomBytes(18).toString("base64url");
696
+ sendPage(res, page({
697
+ ui,
698
+ title: ui.text.errorTitle,
699
+ body: ERROR_MESSAGES[locale][reason],
700
+ actionHref: routes.start,
701
+ actionText: ui.text.tryAgain,
702
+ correlationId,
703
+ nonce,
704
+ }), nonce);
705
+ });
706
+ router.post(routes.logout, asyncHandler(async (req, res) => {
707
+ const origin = req.get("origin");
708
+ const referer = req.get("referer");
709
+ let sourceOrigin;
710
+ try {
711
+ sourceOrigin = origin ? new URL(origin).origin : referer
712
+ ? new URL(referer).origin
713
+ : undefined;
714
+ }
715
+ catch {
716
+ sourceOrigin = undefined;
717
+ }
718
+ if (sourceOrigin !== config.redirectUri.origin) {
719
+ logger.warn(AUTH_EXPRESS_EVENTS.logoutRejected, {
720
+ correlationId: req.vvpCorrelationId,
721
+ stage: "request_validation",
722
+ reason: "cross_origin",
723
+ });
724
+ res.status(403).type("text").send("Logout request was rejected.");
725
+ return;
726
+ }
727
+ const id = parseCookies(req.headers.cookie)[cookieName];
728
+ clearSession(res);
729
+ clearTransaction(res);
730
+ if (!id || !ID_PATTERN.test(id)) {
731
+ logger.info(AUTH_EXPRESS_EVENTS.logoutLocalSessionDeleted, {
732
+ correlationId: req.vvpCorrelationId,
733
+ hadSession: false,
734
+ });
735
+ logger.info(AUTH_EXPRESS_EVENTS.logoutSucceeded, {
736
+ correlationId: req.vvpCorrelationId,
737
+ hadSession: false,
738
+ stage: "local",
739
+ });
740
+ const localRedirect = localSignedOutUrl;
741
+ logger.info(AUTH_EXPRESS_EVENTS.logoutRedirectCreated, {
742
+ correlationId: req.vvpCorrelationId,
743
+ destination: "signed_out",
744
+ });
745
+ res.redirect(303, localRedirect);
746
+ return;
747
+ }
748
+ try {
749
+ const logoutUrl = await oidc.logout(id);
750
+ logger.info(AUTH_EXPRESS_EVENTS.logoutLocalSessionDeleted, {
751
+ correlationId: req.vvpCorrelationId,
752
+ hadSession: true,
753
+ });
754
+ if ((logoutUrl.protocol !== "https:" && logoutUrl.protocol !== "http:") ||
755
+ (logoutUrl.protocol !== "https:" && config.environment === "production")) {
756
+ logger.warn(AUTH_EXPRESS_EVENTS.logoutInvalidRedirect, {
757
+ correlationId: req.vvpCorrelationId,
758
+ stage: "redirect_validation",
759
+ reason: "insecure_provider_redirect",
760
+ });
761
+ logger.info(AUTH_EXPRESS_EVENTS.logoutRedirectCreated, {
762
+ correlationId: req.vvpCorrelationId,
763
+ destination: "local_recovery",
764
+ });
765
+ res.redirect(303, localSignedOutUrl);
766
+ return;
767
+ }
768
+ logger.info(AUTH_EXPRESS_EVENTS.logoutRedirectCreated, {
769
+ correlationId: req.vvpCorrelationId,
770
+ destination: "provider",
771
+ });
772
+ logger.info(AUTH_EXPRESS_EVENTS.logoutSucceeded, {
773
+ correlationId: req.vvpCorrelationId,
774
+ hadSession: true,
775
+ stage: "local_and_redirect",
776
+ });
777
+ res.redirect(303, logoutUrl.href);
778
+ }
779
+ catch {
780
+ await sessionStore.delete(id);
781
+ logger.info(AUTH_EXPRESS_EVENTS.logoutLocalSessionDeleted, {
782
+ correlationId: req.vvpCorrelationId,
783
+ hadSession: true,
784
+ });
785
+ logger.warn(AUTH_EXPRESS_EVENTS.logoutProviderRejected, {
786
+ correlationId: req.vvpCorrelationId,
787
+ stage: "provider_redirect",
788
+ reason: "redirect_creation_failed",
789
+ });
790
+ logger.info(AUTH_EXPRESS_EVENTS.logoutRedirectCreated, {
791
+ correlationId: req.vvpCorrelationId,
792
+ destination: "local_recovery",
793
+ });
794
+ res.redirect(303, localSignedOutUrl);
795
+ }
796
+ }));
797
+ router.get(routes.session, (_req, res, next) => {
798
+ res.set("cache-control", "no-store");
799
+ res.set("pragma", "no-cache");
800
+ next();
801
+ }, authenticate, asyncHandler(async (req, res) => {
802
+ if (!req.vvpActor) {
803
+ res.status(401).json({
804
+ error: "authentication_required",
805
+ correlationId: req.vvpCorrelationId,
806
+ });
807
+ return;
808
+ }
809
+ const id = parseCookies(req.headers.cookie)[cookieName];
810
+ const session = id ? await sessionStore.get(id) : null;
811
+ if (!session) {
812
+ res.status(401).json({
813
+ error: "authentication_required",
814
+ correlationId: req.vvpCorrelationId,
815
+ });
816
+ return;
817
+ }
818
+ res.json(toPublicSession(session));
819
+ }));
820
+ const safeAuthError = (_error, req, res, _next) => {
821
+ logger.error(AUTH_EXPRESS_EVENTS.internalError, {
822
+ correlationId: req.vvpCorrelationId,
823
+ operation: "auth_route",
824
+ });
825
+ if (!res.headersSent) {
826
+ if (req.method === "GET" && req.path !== routes.session) {
827
+ errorRedirect(res, req.path === routes.callback ? "unknown" : "provider_unavailable", req.vvpCorrelationId);
828
+ }
829
+ else {
830
+ res.status(500).json({
831
+ error: "authentication_unavailable",
832
+ correlationId: req.vvpCorrelationId,
833
+ });
834
+ }
835
+ }
836
+ };
837
+ router.use(safeAuthError);
838
+ const applicationAuthenticate = (req, res, next) => {
839
+ correlate(req, res, (error) => {
840
+ if (error) {
841
+ next(error);
842
+ return;
843
+ }
844
+ authenticate(req, res, next);
845
+ });
846
+ };
847
+ return {
848
+ config,
849
+ router,
850
+ authenticate: applicationAuthenticate,
851
+ requireAuth,
852
+ requireRealmRole: (role, behavior) => [
853
+ requireAuth(behavior),
854
+ authorize((actor) => hasRealmRole(actor, role), behavior),
855
+ ],
856
+ requireClientRole: (role, behavior) => [
857
+ requireAuth(behavior),
858
+ authorize((actor) => hasClientRole(actor, role, behavior.clientId ?? config.clientId), behavior),
859
+ ],
860
+ requireAnyClientRole: (roles, behavior) => [
861
+ requireAuth(behavior),
862
+ authorize((actor) => hasAnyClientRole(actor, roles, behavior.clientId ?? config.clientId), behavior),
863
+ ],
864
+ createLoginUrl: (returnTo) => {
865
+ const safe = safeReturnTo(returnTo, returnToOptions);
866
+ return safe === "/" ? routes.start :
867
+ `${routes.start}?returnTo=${encodeURIComponent(safe)}`;
868
+ },
869
+ createLoginLink: (returnTo) => ({
870
+ href: (() => {
871
+ const safe = safeReturnTo(returnTo, returnToOptions);
872
+ return safe === "/" ? routes.start :
873
+ `${routes.start}?returnTo=${encodeURIComponent(safe)}`;
874
+ })(),
875
+ target: "_top",
876
+ rel: "noopener noreferrer",
877
+ }),
878
+ hasClientRole: (actor, role) => hasClientRole(actor, role, config.clientId),
879
+ hasAnyClientRole: (actor, roles) => hasAnyClientRole(actor, roles, config.clientId),
880
+ routes,
881
+ };
882
+ }
883
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@dreadkn1ght123/auth-express",
3
+ "version": "0.2.0",
4
+ "description": "Secure Express integration for VVP authentication",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "engines": {
19
+ "node": ">=20"
20
+ },
21
+ "dependencies": {
22
+ "express": "^5.1.0",
23
+ "@dreadkn1ght123/auth-types": "^0.2.0",
24
+ "@dreadkn1ght123/auth-core": "^0.2.0"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public",
28
+ "provenance": true
29
+ },
30
+ "devDependencies": {
31
+ "@types/express": "^5.0.3",
32
+ "@types/node": "^22.15.0",
33
+ "@types/supertest": "^6.0.3",
34
+ "supertest": "^7.1.4"
35
+ },
36
+ "scripts": {
37
+ "build": "tsc -b",
38
+ "typecheck": "tsc -b --pretty false && tsc -p tsconfig.test.json --noEmit --pretty false",
39
+ "test": "vitest run"
40
+ }
41
+ }