{"_id":"@brandwacht/sso-auth-react","_rev":"6-346c0e2cfc8cede94531ade5ba4094b4","name":"@brandwacht/sso-auth-react","dist-tags":{"latest":"0.1.0"},"versions":{"0.0.1":{"name":"@brandwacht/sso-auth-react","version":"0.0.1","_id":"@brandwacht/sso-auth-react@0.0.1","maintainers":[{"name":"bwh-ciprian","email":"ciprian.dragoste@brandwachthuren.nl"}],"dist":{"shasum":"cc45ab5f89f95dcb79097bc7fbb324d3a4785f48","tarball":"https://registry.npmjs.org/@brandwacht/sso-auth-react/-/sso-auth-react-0.0.1.tgz","fileCount":9,"integrity":"sha512-/CeZ6fiJ9hCXdOIzPwA9P/j6BkneH9atWaNp6NeDwEadquPZ+8FzPivBTkZY2pnepVSX2IiiINUA+AutavCT6g==","signatures":[{"sig":"MEUCICkdk/xWr4uKsx0/1+W4rd1Jo17yz0+bb3TrLVaOOCa9AiEAt+JWY6hjhBbzHvFYc2SYLSTPXsP0ReQcpChfQtayZvo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":249580},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./styles.css":"./dist/styles.css"},"scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc -p tsconfig.json --noEmit","publish:npm":"npm run build && npm publish --access public"},"_npmUser":{"name":"bwh-ciprian","email":"ciprian.dragoste@brandwachthuren.nl"},"_npmVersion":"11.6.2","description":"BWH SSO authentication library for React apps","directories":{},"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.0","react":"^18.3.1","react-dom":"^18.3.1","typescript":"~5.6.0","@types/react":"^18.3.12","@types/react-dom":"^18.3.1","react-router-dom":"^6.27.0"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0","react-router-dom":"^6.0.0 || ^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/sso-auth-react_0.0.1_1777379614719_0.39627254136143475","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@brandwacht/sso-auth-react","version":"0.0.2","_id":"@brandwacht/sso-auth-react@0.0.2","maintainers":[{"name":"bwh-ciprian","email":"ciprian.dragoste@brandwachthuren.nl"}],"dist":{"shasum":"09b95b60d1c293c8a79e68a14b21daa1ffcffc6c","tarball":"https://registry.npmjs.org/@brandwacht/sso-auth-react/-/sso-auth-react-0.0.2.tgz","fileCount":9,"integrity":"sha512-i1QYltjHh2eXyYRKtR6oge+EsscWWcczf/WJMyxT1jaMvi882kXan8X90txRpVIyO/e73908I2mIKIfjXCht/Q==","signatures":[{"sig":"MEUCIQDUZAQm/aWexbMIHGVQ9+iL1boMhvIOdRRK8q0ggMw6rAIgGORkXDdrrZBuV6e5XldghJzvbBtMKrI/9LztkuQxXpY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":251152},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./styles.css":"./dist/styles.css"},"scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc -p tsconfig.json --noEmit","publish:npm":"npm run build && npm publish --access public"},"_npmUser":{"name":"bwh-ciprian","email":"ciprian.dragoste@brandwachthuren.nl"},"_npmVersion":"10.8.2","description":"BWH SSO authentication library for React apps","directories":{},"_nodeVersion":"20.19.6","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.0","react":"^18.3.1","react-dom":"^18.3.1","typescript":"~5.6.0","@types/react":"^18.3.12","@types/react-dom":"^18.3.1","react-router-dom":"^6.27.0"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0","react-router-dom":"^6.0.0 || ^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/sso-auth-react_0.0.2_1782991825677_0.22083789276798882","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"@brandwacht/sso-auth-react","version":"0.0.3","_id":"@brandwacht/sso-auth-react@0.0.3","maintainers":[{"name":"bwh-ciprian","email":"ciprian.dragoste@brandwachthuren.nl"}],"dist":{"shasum":"646ac3c6ebb921b7f7f2dc55bf2d85a765c1bd2a","tarball":"https://registry.npmjs.org/@brandwacht/sso-auth-react/-/sso-auth-react-0.0.3.tgz","fileCount":9,"integrity":"sha512-fp+UHW+CMUJRfp+b71S9TIH14noSUh6j5EbTIbi3gXvfyYOF59iGENDUtuYePs57vyDzdglsCxZQEYoCnBYTEA==","signatures":[{"sig":"MEQCICV4lsihqnJ9UGNktFZes5gp2wKaKXKnc/0kTqsC00BKAiA/2MGxNjYop8oa5kJE0bWuhtP95YRSByAzL8I3kcJBzQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":251548},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./styles.css":"./dist/styles.css"},"scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc -p tsconfig.json --noEmit","publish:npm":"npm run build && npm publish --access public"},"_npmUser":{"name":"bwh-ciprian","email":"ciprian.dragoste@brandwachthuren.nl"},"_npmVersion":"10.8.2","description":"BWH SSO authentication library for React apps","directories":{},"_nodeVersion":"20.19.6","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.0","react":"^18.3.1","react-dom":"^18.3.1","typescript":"~5.6.0","@types/react":"^18.3.12","@types/react-dom":"^18.3.1","react-router-dom":"^6.27.0"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0","react-router-dom":"^6.0.0 || ^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/sso-auth-react_0.0.3_1783333626648_0.5910418363030194","host":"s3://npm-registry-packages-npm-production"}},"0.0.4":{"name":"@brandwacht/sso-auth-react","version":"0.0.4","_id":"@brandwacht/sso-auth-react@0.0.4","maintainers":[{"name":"bwh-ciprian","email":"ciprian.dragoste@brandwachthuren.nl"}],"dist":{"shasum":"b0ca6082f1354dc77f3f1e8599d220d39ae194ae","tarball":"https://registry.npmjs.org/@brandwacht/sso-auth-react/-/sso-auth-react-0.0.4.tgz","fileCount":9,"integrity":"sha512-cDFmhCoogxgEAf4ZV5qh1yMYQv81MVdcn/WRiUY9K08IkGK6uAtLFGLWWusiWJ2q337BhPUL6hy46oChX7mmyA==","signatures":[{"sig":"MEUCIQCEIUSdc1HgA8zrtC0I6JChpWdzSZm1ZaqIrmVu0kkp9QIgbskQjTdSIcn8yQGsf5fRaLgvDN8tKJu43BHZvXNCsQ8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":255396},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./styles.css":"./dist/styles.css"},"scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc -p tsconfig.json --noEmit","publish:npm":"npm run build && npm publish --access public"},"_npmUser":{"name":"bwh-ciprian","email":"ciprian.dragoste@brandwachthuren.nl"},"_npmVersion":"10.8.2","description":"BWH SSO authentication library for React apps","directories":{},"_nodeVersion":"20.19.6","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.0","react":"^18.3.1","react-dom":"^18.3.1","typescript":"~5.6.0","@types/react":"^18.3.12","@types/react-dom":"^18.3.1","react-router-dom":"^6.27.0"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0","react-router-dom":"^6.0.0 || ^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/sso-auth-react_0.0.4_1783672889512_0.3298109015898756","host":"s3://npm-registry-packages-npm-production"}},"0.0.5":{"name":"@brandwacht/sso-auth-react","version":"0.0.5","_id":"@brandwacht/sso-auth-react@0.0.5","maintainers":[{"name":"bwh-ciprian","email":"ciprian.dragoste@brandwachthuren.nl"}],"dist":{"shasum":"93ad4b6f023e85e3b2b43c7c4796284cd7c1d9fc","tarball":"https://registry.npmjs.org/@brandwacht/sso-auth-react/-/sso-auth-react-0.0.5.tgz","fileCount":9,"integrity":"sha512-gvo9Cgl/f4SJ8mE8vNE0RXnrp43wwji4SbZ/GYKYo22WSWqRrZStCmS91WsnxBLIa+oVtbGdqAwqcDYJgGurcQ==","signatures":[{"sig":"MEYCIQDeY9/m1JqDyYRvQjTXNEflhUrf3dBMnaoLhETJOnjXlQIhAOhZWi4idLaju2PrXh5by1uBdbXPEIXPEsxnQpIktBhf","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":263274},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./styles.css":"./dist/styles.css"},"scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc -p tsconfig.json --noEmit","publish:npm":"npm run build && npm publish --access public"},"_npmUser":{"name":"bwh-ciprian","email":"ciprian.dragoste@brandwachthuren.nl"},"_npmVersion":"10.8.2","description":"BWH SSO authentication library for React apps","directories":{},"_nodeVersion":"20.19.6","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.0","react":"^18.3.1","react-dom":"^18.3.1","typescript":"~5.6.0","@types/react":"^18.3.12","@types/react-dom":"^18.3.1","react-router-dom":"^6.27.0"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0","react-router-dom":"^6.0.0 || ^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/sso-auth-react_0.0.5_1783679006549_0.6179173569918592","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@brandwacht/sso-auth-react","version":"0.1.0","description":"BWH SSO authentication library for React apps","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./styles.css":"./dist/styles.css"},"scripts":{"build":"tsup","dev":"tsup --watch","typecheck":"tsc -p tsconfig.json --noEmit","publish:npm":"npm run build && npm publish --access public"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0","react-router":"^7.0.0 || ^8.0.0"},"devDependencies":{"@types/react":"^19.2.0","@types/react-dom":"^19.2.0","react":"^19.2.7","react-dom":"^19.2.7","react-router":"^8.3.0","tsup":"^8.3.0","typescript":"~5.6.0"},"_id":"@brandwacht/sso-auth-react@0.1.0","_nodeVersion":"20.19.6","_npmVersion":"10.8.2","dist":{"integrity":"sha512-GX4ClxJwI1Fz8GyWwoT80rRbxAkY/bXN/Jw45vh2JoCPAYnbtWqZmpFDoKIW1KawN7/7tSDjht7nfu78r95LgA==","shasum":"2a67748df07c397a8aeb79f236af49ce8d9d1417","tarball":"https://registry.npmjs.org/@brandwacht/sso-auth-react/-/sso-auth-react-0.1.0.tgz","fileCount":9,"unpackedSize":263733,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIAhtTwIn8w4S3jENlLAsrrmGcnK+xVXf9cvMmIriEHbsAiEAoGECFm/g/+CfJ5FqnpS9IUW6sApxbvcaKLZpDsQT9kk="}]},"_npmUser":{"name":"bwh-ciprian","email":"ciprian.dragoste@brandwachthuren.nl"},"directories":{},"maintainers":[{"name":"bwh-ciprian","email":"ciprian.dragoste@brandwachthuren.nl"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sso-auth-react_0.1.0_1785157978058_0.9851275188406479"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-28T12:33:34.665Z","modified":"2026-07-27T13:12:58.602Z","0.0.1":"2026-04-28T12:33:34.954Z","0.0.2":"2026-07-02T11:30:25.821Z","0.0.3":"2026-07-06T10:27:06.775Z","0.0.4":"2026-07-10T08:41:29.662Z","0.0.5":"2026-07-10T10:23:26.731Z","0.1.0":"2026-07-27T13:12:58.288Z"},"description":"BWH SSO authentication library for React apps","maintainers":[{"name":"bwh-ciprian","email":"ciprian.dragoste@brandwachthuren.nl"}],"readme":"# @brandwacht/sso-auth-react\n\nDrop-in SSO authentication library for React apps in the Brandwacht Huren B.V. ecosystem.\n\nReact port of [`@brandwacht/sso-auth`](../sso-auth) (Angular). Same SSO portal, same backend contract, same `sessionStorage` keys — so a React app and the existing Angular apps share one SSO session and silently log each other in.\n\n## Installation\n\n```bash\nnpm install @brandwacht/sso-auth-react react-router\n```\n\nPeer dependencies: `react ^18 || ^19`, `react-dom ^18 || ^19`, `react-router ^7 || ^8`.\n\n> **Upgrading from `0.0.x`?** This package now imports from `react-router` instead of\n> `react-router-dom`, which React Router v8 discontinued. Replace `react-router-dom` with\n> `react-router` in your app and rewrite your `from 'react-router-dom'` imports — in v7 the\n> former is a re-export shim over the latter, so nothing else changes. React Router v6 is no\n> longer supported (`useSearchParams` does not exist in the v6 core package). Note that\n> `react-router` `>=7.12.0 <8.3.0` is affected by\n> [GHSA-qwww-vcr4-c8h2](https://github.com/advisories/GHSA-qwww-vcr4-c8h2) (high), so prefer\n> `>=8.3.0`; that requires React `>=19.2.7`.\n\nImport the stylesheet once at your app entry:\n\n```ts\nimport '@brandwacht/sso-auth-react/styles.css';\n```\n\n## Quick start\n\n### 1. Wrap your app\n\n```tsx\n// main.tsx\nimport { BrowserRouter, Routes, Route } from 'react-router';\nimport {\n  BwhSsoAuthProvider,\n  BwhSsoLoginPage,\n  RequireBwhAuth,\n  type BwhSsoAuthConfig,\n} from '@brandwacht/sso-auth-react';\nimport '@brandwacht/sso-auth-react/styles.css';\nimport { Dashboard } from './Dashboard';\n\nconst ssoConfig: BwhSsoAuthConfig = {\n  ssoAuthPortalUri: import.meta.env.VITE_SSO_PORTAL_URI,\n  apiBaseUrl: import.meta.env.VITE_API_BASE_URL,\n  appBaseUrl: import.meta.env.VITE_APP_BASE_URL,\n  appId: 'my-react-app',\n  resolveLanding: () => '/dashboard',\n};\n\nexport function App() {\n  return (\n    <BwhSsoAuthProvider config={ssoConfig}>\n      <BrowserRouter>\n        <Routes>\n          <Route path=\"/login\" element={<BwhSsoLoginPage />} />\n          <Route\n            element={\n              <RequireBwhAuth>\n                <AppLayout />\n              </RequireBwhAuth>\n            }\n          >\n            <Route path=\"/dashboard\" element={<Dashboard />} />\n            <Route path=\"*\" element={<Navigate to=\"/dashboard\" replace />} />\n          </Route>\n        </Routes>\n      </BrowserRouter>\n    </BwhSsoAuthProvider>\n  );\n}\n```\n\n> Define `ssoConfig` at module scope (or wrap in `useMemo`) — passing a fresh\n> object every render will recreate every callback the library exposes.\n\n> **The `loginRoute` *is* the SSO callback — there is no separate `/callback` route.**\n> The library sends the portal a `redirectUri` built as `appBaseUrl + loginRoute`\n> (string concatenation), and `<BwhSsoLoginPage>` mounted at that route reads the\n> returned `?sso_code=…`. So the route you mount the page on, the `loginRoute` you\n> configure, and the redirect URI whitelisted in the portal must all be the **same\n> URL**. To use a path like `/auth/callback`, set `loginRoute: '/auth/callback'`,\n> mount `<BwhSsoLoginPage>` there, and whitelist `https://your-app/auth/callback`.\n>\n> Two gotchas that produce a portal `Invalid or disallowed target URL`:\n> - **No trailing slash on `appBaseUrl`.** Because the URI is concatenated raw,\n>   `appBaseUrl: 'https://my-app.bwh.nl/'` + `loginRoute: '/login'` yields\n>   `https://my-app.bwh.nl//login` (double slash), which won't match the whitelist.\n>   `appBaseUrl` must have no trailing slash; `loginRoute` must have a leading slash.\n> - **Whitelist the exact emitted string.** It must match the portal entry\n>   byte-for-byte. To see exactly what's sent, URL-decode the `redirectUri` query\n>   param in the address bar during the redirect.\n>\n> The interactive and silent (cross-app) flows send an identical `redirectUri`\n> (silent just adds `&prompt=none`), so a single whitelisted URI covers both.\n\n### 2. Read auth state and call your API\n\n```tsx\nimport { useBwhSsoAuth, useBwhAuthFetch } from '@brandwacht/sso-auth-react';\n\nfunction Header() {\n  const { user, logout } = useBwhSsoAuth();\n  return (\n    <header>\n      <span>{user?.email}</span>\n      <button onClick={logout}>Sign out</button>\n    </header>\n  );\n}\n\nfunction ProfileCard() {\n  const authFetch = useBwhAuthFetch();\n  const [me, setMe] = useState<Me | null>(null);\n  useEffect(() => {\n    authFetch('/api/me').then((r) => r.json()).then(setMe);\n  }, [authFetch]);\n  // ...\n}\n```\n\nFor non-React code paths (e.g. configuring an axios instance at module load):\n\n```ts\nimport axios from 'axios';\nimport { getStoredBwhAuthToken } from '@brandwacht/sso-auth-react';\n\nconst api = axios.create({ baseURL: '/api' });\napi.interceptors.request.use((config) => {\n  const token = getStoredBwhAuthToken();\n  if (token) config.headers.Authorization = `Bearer ${token}`;\n  return config;\n});\n```\n\n## Multi-app SSO across Angular + React\n\nThis library is wire-compatible with the Angular [`@brandwacht/sso-auth`](../sso-auth) library:\n\n| Concern              | Shared contract                                |\n| -------------------- | ---------------------------------------------- |\n| SSO portal flow      | redirect → `?sso_code=…` → `POST /api/sso/exchange` → `POST {api}/auth/sso` |\n| Auth storage         | `sessionStorage['BWH_SSO_AUTH_STATE']`         |\n| Silent SSO flag      | `sessionStorage['bwh_sso_silent_attempted']`   |\n| Return-URL handoff   | `sessionStorage['bwh_sso_return_url']`         |\n| Cross-app logout     | parent-domain cookie `bwh_sso=1`               |\n| UI prefs             | `localStorage['bwh.ui.lang']`, `bwh.ui.theme.dark` |\n\nA user logged in to any Angular app will be silently authenticated in your React app on first visit, and vice versa. Make sure all apps use the **same** `ssoAuthPortalUri` and that each app's `appId` + `appBaseUrl` is whitelisted in the portal.\n\nCross-app logout works via the `bwh_sso=1` cookie the portal sets on its **parent domain** (e.g. `.bwh.nl`): on tab refocus, an authenticated app that no longer sees the cookie drops its session and returns to `/login`. Apps hosted **outside** that domain (e.g. `crm.brandwachthuren.nl` with the portal on `oauth-portal.bwh.nl`) can never see the cookie, so the check is automatically skipped there — they keep silent single sign-on but opt out of logout propagation. Set `crossAppLogout: true | false` in the config to force either behavior instead of the domain auto-detection.\n\n## API\n\n### `<BwhSsoAuthProvider config={...}>`\n\nTop-level provider that wires up auth state, i18n, and theme.\n\n### `useBwhSsoAuth()`\n\n```ts\n{\n  token: string | null;\n  user: SsoAuthUser | null;\n  isAuthenticated: boolean;\n  startSsoLogin(): void;\n  startSilentSsoLogin(): void;\n  ssoLogin(code: string): Promise<{ token; user }>;\n  logout(): void;\n  updateStoredUser(user: SsoAuthUser): void;\n}\n```\n\n### `useBwhAuthFetch()`\n\nReturns a `fetch`-compatible function that injects `Authorization: Bearer <token>` when authenticated.\n\n### `<RequireBwhAuth>`\n\nRoute guard for `react-router`. Renders children when authenticated, otherwise navigates to the configured `loginRoute` with `?returnUrl=…` preserved.\n\n### `<BwhSsoLoginPage>`\n\nThe full pre-built login page. Handles the SSO callback, silent SSO, and renders the dark/light themed UI with EN/NL switcher.\n\n### `useBwhI18n()` / `useBwhTranslate()`\n\n```ts\nconst { lang, setLanguage, t, registerTranslations } = useBwhI18n();\n// or, just the translator:\nconst t = useBwhTranslate();\n```\n\nUse `registerTranslations('nl', { 'My key': 'Mijn vertaling' })` to add app-specific strings; they override the built-in dictionary.\n\n### `useBwhTheme()`\n\n```ts\nconst { isDark, toggle, setDark } = useBwhTheme();\n```\n\nThe theme provider toggles `class=\"dark\"` on `<html>`. Pair with Tailwind's `darkMode: 'class'` if you use Tailwind.\n\n### `getStoredBwhAuthToken(storageKey?)`\n\nRead the persisted JWT directly. For module-level setup outside React.\n\n### `BwhHttpError`\n\nThrown by the internal SSO HTTP helpers. Has `{ status, error }` matching the shape `humanizeBwhAuthError` reads.\n\n### `humanizeBwhAuthError(err, t)`\n\nConvert a thrown error from `ssoLogin()` into a user-readable string. Mirrors the Angular library's error handling exactly.\n\n## Configuration reference\n\n```ts\ninterface BwhSsoAuthConfig {\n  ssoAuthPortalUri: string;        // e.g. 'https://oauth-portal.bwh.nl'\n  apiBaseUrl: string;              // e.g. '/api'\n  appBaseUrl: string;              // public origin, NO trailing slash, e.g. 'https://my-app.bwh.nl'\n  appId: string;                   // unique id registered in the SSO portal\n  storageKey?: string;             // default 'BWH_SSO_AUTH_STATE'\n  loginRoute?: string;             // default '/login'; also the SSO callback. Sent to the\n                                   // portal as redirectUri = appBaseUrl + loginRoute, so it\n                                   // must match both the mounted route and the portal whitelist\n  ssoCodeParam?: string;           // default 'sso_code'\n  ssoEndpoint?: string;            // default '/auth/sso'\n  crossAppLogout?: boolean;        // default: auto — the tab-refocus logout check runs only\n                                   // when this app is hosted under the portal's parent domain\n                                   // (where the `bwh_sso=1` cookie is visible). Set to force.\n  resolveLanding: (user: SsoAuthUser) => string | Promise<string>;\n}\n```\n\n## Development\n\n```bash\nnpm install\nnpm run typecheck\nnpm run build       # tsup → dist/{index.js,index.cjs,index.d.ts,styles.css}\n```\n","readmeFilename":"README.md"}