{"_id":"@alphinex/auth","_rev":"2-9690de8ceff3e91bc7f2c1ee0a817ca6","name":"@alphinex/auth","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@alphinex/auth","version":"1.0.0","license":"UNLICENSED","_id":"@alphinex/auth@1.0.0","maintainers":[{"name":"anasaliqureshi","email":"anas@alphinex.com"}],"dist":{"shasum":"e8ff7116179bc695dafc5822b4902516813b7124","tarball":"https://registry.npmjs.org/@alphinex/auth/-/auth-1.0.0.tgz","fileCount":5,"integrity":"sha512-9m9FB9YoccnH78prKWMaD9EkqS67bbZ9U1aoKgdSOwOFsXopEuA6vqVRKPfYryexwqne7OxwPqwhWkxq7arUUw==","signatures":[{"sig":"MEUCICh/j5iXc0tYufC50VU3Fsynr7eZLuL/J52q5Zu7J7hYAiEA72iLIXCh6iDhLA+CvtNXVzsRXgeukhuPDLoFGvi4T88=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":17350},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json"},"private":false,"scripts":{"dev":"vite build --watch","lint":"eslint .","test":"vitest run","build":"vite build","clean":"rimraf dist .turbo","typecheck":"tsc --noEmit"},"_npmUser":{"name":"anasaliqureshi","email":"anas@alphinex.com"},"description":"Auth state/session primitives: AuthProvider, useAuth, route guards.","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","dependencies":{"@alphinex/api":"1.0.0","@alphinex/core":"1.0.0"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","jsdom":"^25.0.1","react":"^19.0.0","eslint":"^9.17.0","vitest":"^2.1.8","react-dom":"^19.0.0","typescript":"^5.7.2","@types/react":"^19.0.2","@types/react-dom":"^19.0.2","@alphinex/testing":"1.0.0","@vitejs/plugin-react":"^4.3.4","@alphinex/build-config":"0.0.0","@testing-library/react":"^16.1.0","@alphinex/eslint-config":"0.0.0","@testing-library/jest-dom":"^6.6.3","@alphinex/typescript-config":"0.0.0","@testing-library/user-event":"^14.5.2"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0"},"peerDependenciesMeta":{"react":{"optional":false},"react-dom":{"optional":false}},"_npmOperationalInternal":{"tmp":"tmp/auth_1.0.0_1785852479304_0.38513502530546706","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@alphinex/auth","version":"1.0.1","private":false,"description":"Auth state/session primitives: AuthProvider, useAuth, route guards.","license":"UNLICENSED","type":"module","sideEffects":false,"main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json"},"dependencies":{"@alphinex/core":"1.0.0","@alphinex/api":"1.1.0"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0"},"peerDependenciesMeta":{"react":{"optional":false},"react-dom":{"optional":false}},"devDependencies":{"typescript":"^5.7.2","vite":"^6.0.5","vitest":"^2.1.8","react":"^19.0.0","react-dom":"^19.0.0","@types/react":"^19.0.2","@types/react-dom":"^19.0.2","@testing-library/react":"^16.1.0","@testing-library/jest-dom":"^6.6.3","@testing-library/user-event":"^14.5.2","@vitejs/plugin-react":"^4.3.4","jsdom":"^25.0.1","eslint":"^9.17.0","@alphinex/build-config":"0.0.0","@alphinex/eslint-config":"0.0.0","@alphinex/testing":"1.0.2","@alphinex/typescript-config":"0.0.0"},"scripts":{"build":"vite build","dev":"vite build --watch","lint":"eslint .","typecheck":"tsc --noEmit","test":"vitest run","clean":"rimraf dist .turbo"},"_nodeVersion":"22.14.0","_id":"@alphinex/auth@1.0.1","dist":{"integrity":"sha512-ZBrHMGr+BM/teFnDpFpKLv91DsTt15YbbyekRsmUuZzGpsYLFjW1WIusxRiMX8kYZ5Ko70irDIIOszvwLNONPQ==","shasum":"fa570160c87b30b5127a3bcad7069fc4a84dc57e","tarball":"https://registry.npmjs.org/@alphinex/auth/-/auth-1.0.1.tgz","fileCount":5,"unpackedSize":21691,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDBfbzqsIYMqS/kPwUTw2SN7rXA6Nd+fYNN8dsyxqJl+wIgae3vThIm62uogUHQbJ3QeYuo0jBheq11WKTgKveAC+Y="}]},"_npmUser":{"name":"anasaliqureshi","email":"anas@alphinex.com"},"directories":{},"maintainers":[{"name":"anasaliqureshi","email":"anas@alphinex.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/auth_1.0.1_1786525482169_0.23421109059613254"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-04T14:07:59.163Z","modified":"2026-08-12T09:04:42.504Z","1.0.0":"2026-08-04T14:07:59.451Z","1.0.1":"2026-08-12T09:04:42.347Z"},"license":"UNLICENSED","description":"Auth state/session primitives: AuthProvider, useAuth, route guards.","maintainers":[{"name":"anasaliqureshi","email":"anas@alphinex.com"}],"readme":"# @alphinex/auth\n\nBackend-agnostic auth state and session primitives: an `AuthProvider`/`useAuth` context driven by\na pluggable `AuthAdapter`, a `RequireAuth` route guard, and swappable session storage strategies.\nThis package doesn't know about Sanctum, CSRF, or bearer tokens — that lives in\n`@alphinex/api-laravel` (or any backend adapter you write); `auth` just needs an `AuthAdapter`.\n\n## `AuthProvider` / `useAuth`\n\n`AuthProvider` takes an `adapter: AuthAdapter<TUser, TCredentials>` — an object with\n`fetchSession()`, `login(credentials)`, and `logout()` — and calls `fetchSession()` once on mount\nto resolve the initial `status` (`\"idle\"` → `\"loading\"` → `\"authenticated\"`/`\"unauthenticated\"`).\n`useAuth()` reads `status`, `user`, `error`, and exposes `login`, `logout`, and `refresh`:\n\n```tsx\nimport { AuthProvider, useAuth, type AuthAdapter } from \"@alphinex/auth\";\nimport { ApiError } from \"@alphinex/api\";\n\ninterface Credentials {\n  email: string;\n  password: string;\n}\n\nconst authAdapter: AuthAdapter<AppUser, Credentials> = {\n  fetchSession: async () => {\n    try {\n      return await apiClient.get<AppUser>(\"/user\");\n    } catch (error) {\n      if (error instanceof ApiError && error.status === 401) return null;\n      throw error;\n    }\n  },\n  login: (credentials) => apiClient.post<AppUser>(\"/login\", credentials),\n  logout: () => apiClient.post(\"/logout\"),\n};\n\nfunction Root() {\n  return (\n    <AuthProvider adapter={authAdapter}>\n      <App />\n    </AuthProvider>\n  );\n}\n\nfunction LoginForm() {\n  const { login, error } = useAuth<AppUser, Credentials>();\n  const fieldErrors = error instanceof ApiError ? error.fieldErrors : undefined;\n\n  async function handleSubmit(email: string, password: string) {\n    await login({ email, password });\n  }\n  // ...\n}\n```\n\nPass `storage` to persist the last-known user across reloads (see session storage below), and\n`subscribeSessionExpired` to wire a backend-emitted session-expiry signal — e.g.\n`@alphinex/api-laravel`'s `events.on(\"session-expired\", ...)` — to an immediate local sign-out,\nwithout this package depending on `api-laravel` directly:\n\n```tsx\nimport { createLaravelApiClient } from \"@alphinex/api-laravel\";\n\nconst { client, events } = createLaravelApiClient({ baseUrl: \"https://api.example.com\" });\n\n<AuthProvider\n  adapter={authAdapter}\n  subscribeSessionExpired={(handler) => events.on(\"session-expired\", () => handler())}\n>\n  <App />\n</AuthProvider>;\n```\n\n`useAuth()` throws if called outside an `AuthProvider`.\n\n## `RequireAuth`\n\nA route guard that renders `children` only once `useAuth()` resolves to `\"authenticated\"`. Renders\n`loading` while `status` is `\"idle\"`/`\"loading\"`, `fallback` while `\"unauthenticated\"` (both default\nto nothing), and calls `onUnauthenticated` as a side effect when the user becomes unauthenticated —\nkept as a callback rather than a hard router dependency so you can `navigate(\"/login\")` with\nwhichever router you use:\n\n```tsx\nimport { RequireAuth } from \"@alphinex/auth\";\nimport { useNavigate } from \"react-router-dom\";\n\nfunction ProtectedRoute() {\n  const navigate = useNavigate();\n  return (\n    <RequireAuth\n      loading={<p>Checking session…</p>}\n      fallback={<LoginForm />}\n      onUnauthenticated={() => navigate(\"/login\")}\n    >\n      <Dashboard />\n    </RequireAuth>\n  );\n}\n```\n\n## Session storage (`createInMemorySessionStorage`, `createLocalStorageSessionStorage`)\n\n`AuthProvider`'s `storage` option implements the `SessionStorage<TUser>` contract\n(`getUser()`/`setUser()`). It defaults to `createInMemorySessionStorage()` — nothing persists\nacross a reload. Use `createLocalStorageSessionStorage(key?)` to avoid an auth-status flash on\nreload; it's purely an optimistic-UI cache, `fetchSession()` remains the source of truth:\n\n```tsx\nimport { AuthProvider, createLocalStorageSessionStorage } from \"@alphinex/auth\";\n\n<AuthProvider adapter={authAdapter} storage={createLocalStorageSessionStorage(\"myapp.auth.user\")}>\n  <App />\n</AuthProvider>;\n```\n\n## `AuthAdapter` / `AuthStatus`\n\nThe contract the whole package is built around. `AuthStatus` is\n`\"idle\" | \"loading\" | \"authenticated\" | \"unauthenticated\"`. `AuthAdapter<TUser, TCredentials>`\nrequires `fetchSession(): Promise<TUser | null>`, `login(credentials): Promise<TUser>`, and\n`logout(): Promise<void>` — implement it against whatever backend you use (Sanctum via\n`@alphinex/api-laravel`, a different API, or a mock for tests).\n\nSee [documentation/ARCHITECTURE.md](../../documentation/ARCHITECTURE.md) for the full package contract, dependency rules, and roadmap placement.\n","readmeFilename":""}