{"_id":"@dreadkn1ght123/auth-express","_rev":"2-387536e2947dc745afd5a0faf8190e17","name":"@dreadkn1ght123/auth-express","dist-tags":{"latest":"0.3.0"},"versions":{"0.2.0":{"name":"@dreadkn1ght123/auth-express","version":"0.2.0","_id":"@dreadkn1ght123/auth-express@0.2.0","maintainers":[{"name":"dreadkn1ght123","email":"dreadkn1ght123@gmail.com"}],"dist":{"shasum":"6ef028a0189accd4b7ea564b279019258f10781d","tarball":"https://registry.npmjs.org/@dreadkn1ght123/auth-express/-/auth-express-0.2.0.tgz","fileCount":6,"integrity":"sha512-r1MsfCnO6g/NYo0ULG3KLuCIKfqRL2J2luwbO5Ltxf+GDbXZX/J7wk9mdCVIBPSDZ0VPk4tIIriScFAlQ2h7lQ==","signatures":[{"sig":"MEUCIQDuBYxvJHXqTXm7RDGt5Od2mQObmxLxThva3glKQxVaCgIgWOXiuCS0M3hzfwX1RzqiK9QzbUODJAfPki2IvNlCEEg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":90104},"main":"./dist/index.js","type":"module","_from":"file:/home/runner/workspace/vvp-iam/release/dreadkn1ght123-auth-express-0.2.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"tsc -b","typecheck":"tsc -b --pretty false && tsc -p tsconfig.test.json --noEmit --pretty false"},"_npmUser":{"name":"dreadkn1ght123","email":"dreadkn1ght123@gmail.com"},"_resolved":"/home/runner/workspace/vvp-iam/release/dreadkn1ght123-auth-express-0.2.0.tgz","_integrity":"sha512-r1MsfCnO6g/NYo0ULG3KLuCIKfqRL2J2luwbO5Ltxf+GDbXZX/J7wk9mdCVIBPSDZ0VPk4tIIriScFAlQ2h7lQ==","_npmVersion":"11.6.2","description":"Secure Express integration for VVP authentication","directories":{},"_nodeVersion":"24.13.0","dependencies":{"express":"^5.1.0","@dreadkn1ght123/auth-core":"^0.2.0","@dreadkn1ght123/auth-types":"^0.2.0"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"supertest":"^7.1.4","@types/node":"^22.15.0","@types/express":"^5.0.3","@types/supertest":"^6.0.3"},"_npmOperationalInternal":{"tmp":"tmp/auth-express_0.2.0_1788174737194_0.8241436229046801","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"_id":"@dreadkn1ght123/auth-express@0.3.0","dist":{"shasum":"29cc7c442da78dd9f4b152e2e74783effa9b9e05","tarball":"https://registry.npmjs.org/@dreadkn1ght123/auth-express/-/auth-express-0.3.0.tgz","fileCount":6,"integrity":"sha512-G6TQOJ7q28onE0G1nUwgeaKpN0J1JVgJs7MoKkLtbNz134x5j9Gsm7hHx8/dI41nTEp4wwHDwqxRlPpHVvZQyA==","signatures":[{"sig":"MEUCIQC8zJi0Q93jxTI6VGTBe4nn+CIItF0MEOwFonScpddp6QIge6sU7CriFPjp+D3yGpz18S6W8tswI1/0PZD4LsrCzyE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQClNSSxDoWeCxRewcwqX1odNh7j6siO74ykewxWaGG6AQIhANMXFDd6SiCtyeJYDPzlgdtRbSyXAHV7z7m7oXekEtdk"}],"unpackedSize":101063},"main":"./dist/index.js","name":"@dreadkn1ght123/auth-express","type":"module","_from":"file:/home/runner/workspace/vvp-iam/release/dreadkn1ght123-auth-express-0.3.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"tsc -b","typecheck":"tsc -b --pretty false && tsc -p tsconfig.test.json --noEmit --pretty false"},"version":"0.3.0","_npmUser":{"name":"dreadkn1ght123","email":"dreadkn1ght123@gmail.com"},"_resolved":"/home/runner/workspace/vvp-iam/release/dreadkn1ght123-auth-express-0.3.0.tgz","_integrity":"sha512-G6TQOJ7q28onE0G1nUwgeaKpN0J1JVgJs7MoKkLtbNz134x5j9Gsm7hHx8/dI41nTEp4wwHDwqxRlPpHVvZQyA==","_npmVersion":"11.6.2","description":"Secure Express integration for VVP authentication","directories":{},"maintainers":[{"name":"dreadkn1ght123","email":"dreadkn1ght123@gmail.com"}],"_nodeVersion":"24.13.0","dependencies":{"express":"^5.1.0","@dreadkn1ght123/auth-core":"^0.3.0","@dreadkn1ght123/auth-types":"^0.3.0"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"supertest":"^7.1.4","@types/node":"^22.15.0","@types/express":"^5.0.3","@types/supertest":"^6.0.3"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/auth-express_0.3.0_1790107915978_0.16052462288591807"}}},"time":{"created":"2026-08-31T11:12:17.027Z","modified":"2026-09-22T20:11:56.228Z","0.2.0":"2026-08-31T11:12:17.338Z","0.3.0":"2026-09-22T20:11:56.059Z"},"description":"Secure Express integration for VVP authentication","maintainers":[{"name":"dreadkn1ght123","email":"dreadkn1ght123@gmail.com"}],"readme":"# @dreadkn1ght123/auth-express\n\nExpress adapter for `@dreadkn1ght123/auth-core`. Mount `auth.router`, then mount\n`auth.authenticate` before protected application routes. Authorization behavior\nis explicit: pass `{ mode: \"html\" }` for login redirects or `{ mode: \"api\" }`\nfor JSON 401/403 responses.\n\nThe logger retains stable event names, including `vvp.auth.login.started`,\n`vvp.auth.callback.succeeded`, `vvp.auth.callback.rejected`,\n`vvp.auth.logout.succeeded`, `vvp.auth.logout.rejected`,\n`vvp.auth.session.refresh_failed`, and `vvp.auth.access.denied`. Rejections add\nstable `stage`, `reason`, error/cause codes, and correlation ID where known.\nStartup emits `vvp.auth.ready` only after configuration, discovery, and\nstore checks succeed. Unexpected adapter errors use `vvp.auth.internal_error`.\nThese events are diagnostic metadata, never an audit record of provider logout\ncompletion.\n\nProduction requires shared implementations of `SessionStore` and\n`AuthTransactionStore`; transaction `consume` must be atomic.\n`MemorySessionStore` and `MemoryAuthTransactionStore` are process-local\ndevelopment/test utilities and are rejected by this adapter in production.\n\n`AuthTransactionStore.consume(id, expectedState)` must compare the expected\nstate and atomically return `{ outcome: \"consumed\", transaction }` or a safe\nfailure outcome while deleting only a matching, unexpired record. A mismatch\nmust not consume a valid transaction. Distributed stores may return\n`ambiguous` when they cannot safely distinguish a missing record from an\nalready-consumed record; never infer or log transaction IDs or state values.\n\n## SDK-owned browser flow\n\nThe adapter owns the complete single-provider UX. Applications must not render\nan SSO chooser, local login form, callback error page, or logout landing page.\n\n| Default route | Method | Purpose |\n|---|---:|---|\n| `/auth/start` | GET | Safe normal entry; preserves validated `returnTo` and provides iframe fallback |\n| `/auth/login` | GET | Internal transaction creation and redirect to IAM; retained for compatibility |\n| `/auth/callback` | GET | Validates callback, creates session, then returns or redirects to SDK error UI |\n| `/auth/logout` | POST | Deletes local session and starts provider logout |\n| `/auth/signed-out` | GET | Non-automatic signed-out page with explicit “sign in again” action |\n| `/auth/error` | GET | Allowlisted safe error and retry page |\n| `/auth/me` | GET | Token-free public session or 401 |\n\nApplications must expose **Sign out of all services** through the React\nadapter's logout API. Its top-level POST to `/auth/logout` deletes the local\nserver session and starts Keycloak RP-Initiated Logout; Keycloak then returns\nthe browser to `/auth/signed-out`. Clearing only application state is not a\nlogout, and consumers must not construct provider logout URLs directly. The\nsigned-out page never automatically starts SSO, so signing in again remains an\nexplicit user action.\n\n`requireAuth({ mode: \"html\" })` sends an unauthenticated browser to\n`/auth/start`, not directly to `/auth/login`. `/auth/login` remains the internal\ntransaction endpoint; consumers must not hardcode it as their normal entry.\nAPI mode still returns JSON 401/403.\n\n## M2M bearer APIs\n\nBrowser-session middleware and machine-to-machine middleware are intentionally\nseparate. Use the `bearer*` APIs for routes called with OAuth access tokens:\n\n```ts\napp.get(\n  \"/api/reports\",\n  ...auth.requireBearerClientRole(\"reports_reader\"),\n  handler,\n);\napp.post(\"/api/reports\", ...auth.requireBearerScope(\"reports.write\"), handler);\n```\n\n`bearerAuthenticate` requires exactly one bounded Bearer JWT on every request;\nit does not read or accept session cookies. It validates the token through the\nOIDC client's discovered Keycloak JWKS validator before mapping an actor.\n`requireBearerClientRole`, `requireBearerAnyClientRole`,\n`requireBearerScope`, and `requireBearerAnyScope` return safe JSON 401/403\nresponses. OAuth scopes are read from the validated standard `scope` claim.\n\nOverride routes centrally when needed:\n\n```ts\nconst auth = await createVvpAuth({\n  config,\n  sessionStore,\n  transactionStore,\n  routes: {\n    basePath: \"/identity\",\n    start: \"start\",\n    login: \"login\",\n    callback: \"complete\",\n    logout: \"logout\",\n    signedOut: \"signed-out\",\n    error: \"error\",\n  },\n});\n```\n\nProduction `config.publicOrigin` derives exact callback and post-logout\nredirects from these routes. Register both resulting URIs in Keycloak. The\ntransaction-cookie path follows `basePath`; an explicit cookie path must\ncontain login and callback routes or startup fails with `CONFIG_INVALID`.\n\nFor server-rendered navigation use `auth.createLoginUrl(returnTo)` or\n`auth.createLoginLink(returnTo)`: both target the SDK start route.\n`createLoginLink` supplies `target: \"_top\"` and safe `rel`. Login must be a\nbrowser navigation, never `fetch`/XHR. Invalid external `returnTo` values are\nrejected or normalized by SDK policy; never concatenate one manually.\n\nInstance role helpers use configured `clientId`, avoiding a stale hardcoded ID\nafter a rename. Prefer `auth.hasClientRole(actor, role)` and\n`auth.requireClientRole(role, behavior)` without `behavior.clientId`;\nmodule-level helpers and the optional `clientId` behavior field remain\navailable for intentional cross-client policies.\n\nFor protected APIs, authentication must first establish exact issuer, JWKS\nsignature, token lifetime/`exp`, and an `aud` identifying that API. Endpoint\nmiddleware then requires its product-local Keycloak client roles/scopes.\n`azp`/OAuth `client_id` may be logged safely for audit correlation but must\nnever select access through a local client allowlist or\n`*_ALLOWED_CLIENT_IDS`.\n\nSDK pages are Russian by default with English locale support, responsive,\nkeyboard accessible, independent of app CSS/JavaScript, and protected by a\nstrict CSP. Optional `ui` branding (`productName`, local `logoUrl`,\n`supportText`, allowlisted text overrides) is escaped and must contain no HTML\nor remote script dependency. Error pages accept only public allowlisted reasons,\ncontain a safe retry to `/auth/start` and optional correlation ID, and never\nexpose provider/exception text. The signed-out page never automatically starts\nSSO, preventing an immediate logout/login loop.\n\nPublic error reasons are limited to `transaction_missing`,\n`transaction_expired`, `transaction_already_used`, `provider_denied`,\n`token_validation_failed`, `provider_unavailable`, `session_failed`,\n`configuration_error`, and `unknown`. They select user text only; detailed\ncallback stage/reason remains in safe structured logs and is never copied into\nthe browser URL.\nSee the workspace [`UPGRADE.md`](../../UPGRADE.md) and\n[`docs/sdk-usage.md`](../../docs/sdk-usage.md) for the `0.3.0` migration.","readmeFilename":"README.md"}