@seamless-auth/react 0.12.0 → 0.14.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/CHANGELOG.md CHANGED
@@ -1,5 +1,83 @@
1
1
  # @seamless-auth/react
2
2
 
3
+ ## 0.14.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 419025e: Support the Next.js App Router.
8
+ - Every module in `@seamless-auth/react` now ships with a `'use client'` directive, so `AuthProvider` renders straight from a server layout. The build fails if the directive goes missing from any emitted file.
9
+ - **Breaking:** `AuthRoutes` moved to `@seamless-auth/react/routes`, and `react-router-dom` is now an optional peer. Change `import { AuthRoutes } from '@seamless-auth/react'` to `import { AuthRoutes } from '@seamless-auth/react/routes'`. Applications that do not render the bundled screens no longer need react-router installed.
10
+ - Fixed a hydration mismatch for returning users. `hasSignedInBefore` was read from `localStorage` on the client's first render, which the server could not match. The store now exposes `getServerState()`, and the provider hydrates from it.
11
+ - `AuthProvider` and `createAuthSession` accept `initialSession`, a session the server already resolved (or `null`). The first paint renders it settled instead of loading, and the session then revalidates in the background.
12
+ - `refreshSession({ background: true })` revalidates without reporting `loading`.
13
+
14
+ - 0dfe622: Support signing in through a legacy identity provider during a migration cutover (fells-code/seamless-auth-api#337).
15
+ - `getOAuthErrorCode` recognises `oauth_provider_retired` (the user's organization no longer signs in with that provider) and `oauth_invalid_id_token`.
16
+ - The bundled OAuth callback sends the user to passkey enrollment when the sign-in response carries `nextStep: 'enroll_passkey'`, and the passkey screen then continues to the `returnTo` the flow asked for instead of always going home.
17
+ - The callback shows a specific message for both new codes.
18
+
19
+ Requires `@seamless-auth/types` 0.25.0.
20
+
21
+ ### Patch Changes
22
+
23
+ - Updated dependencies [419025e]
24
+ - Updated dependencies [0dfe622]
25
+ - @seamless-auth/client@0.2.0
26
+
27
+ ## 0.13.0
28
+
29
+ ### Minor Changes
30
+
31
+ - 93a35b7: Put the platform behind ports, and add a bearer transport with token custody to the client core.
32
+
33
+ The client spoke one contract: cookies to a server adapter at `/auth`, with the browser's WebAuthn
34
+ API and `window.location` reached directly. A native binding has none of those, so the pieces that
35
+ differ by platform are now ports a binding supplies, with the browser implementations as the
36
+ defaults. A web application configures nothing and behaves exactly as before.
37
+ - `transport` on `createSeamlessAuthClient` (and `AuthProvider`): cookie transport is unchanged;
38
+ `{ mode: 'bearer', tokenStorage }` makes the client hold the auth API's own tokens. Every request
39
+ carries `x-seamless-auth-transport: bearer`; pre-auth routes carry the ephemeral token that
40
+ `/login` or `/registration/register` returned, kept in memory only; signed-in routes carry the
41
+ access token; the pair a sign-in returns is written through a `TokenStoragePort`; a 401 on a
42
+ signed-in route triggers one `POST /refresh` and one retry, with at most one refresh in flight
43
+ because the auth API revokes the chain on a replayed refresh token. Which routes take which
44
+ token is one table, mirroring the server adapter's, rather than an annotation at each of the
45
+ forty call sites.
46
+ - `PasskeyPort` (`isSupported`, `isPlatformAuthenticatorAvailable`, `create`, `get`) replaces the
47
+ direct SimpleWebAuthn calls at the four ceremony sites. `createBrowserPasskeyPort()` is the
48
+ default. A port reports an authenticator refusal with `PasskeyCeremonyError`, or any error with a
49
+ DOMException `name` and a string `code`, which the client turns into the same result a browser
50
+ failure gave. The PRF helpers no longer depend on SimpleWebAuthn at runtime.
51
+ - `OAuthRedirectPort` replaces `window.location.assign` in the built-in provider buttons.
52
+ `createBrowserOAuthRedirect()` is the default; a port that receives the callback itself resolves
53
+ with `code` and `state`, and the buttons finish the login on the spot.
54
+ - `TokenStoragePort` with `createMemoryTokenStorage()`.
55
+ - `client.authorizedFetch(input, init)` and `useAuthorizedFetch()`: a fetch for the application's
56
+ own API that carries the session the way the transport does (cookies, or the access token with
57
+ one refresh-and-retry on a 401). A path resolves on `apiHost`. This is what a native app uses to
58
+ call routes behind `requireAuth`.
59
+ - `createAuthSession` accepts the client options (or a ready-made `client`) and exposes the client
60
+ it drives as `session.client`. `useAuthClient()` returns that same instance rather than building a
61
+ second one, which bearer transport needs: the client holds the sign-in in flight.
62
+ - `AuthProvider` gains `transport` and `ports` props and exposes `client` and `ports` on the
63
+ context. `usePasskeySupport()` reads the passkey port.
64
+
65
+ Tracks fells-code/seamless-auth-react#124, #125, #127 and #128.
66
+
67
+ ### Patch Changes
68
+
69
+ - f8f0dfe: Split the framework-agnostic core into `@seamless-auth/client`, and make this repository an npm workspace that publishes both packages.
70
+
71
+ `@seamless-auth/react` was one package holding two layers: the headless client, session store, and result types that any binding needs, and the React provider, hooks, and screens on top. A React Native binding is next, and it must share the first layer rather than copy it, since the session state machine is the worst place for two implementations to drift (#64).
72
+
73
+ `@seamless-auth/client` now carries `createSeamlessAuthClient`, `createAuthSession`, `SessionStoragePort` and its implementations, `createFetchWithAuth`, the error and result types, the PRF helpers, role matching, and the wire type aliases. `@seamless-auth/react` depends on it and re-exports the same public surface it did before, so an application installing `@seamless-auth/react` sees no change in what it imports or how it behaves.
74
+
75
+ Packaging only: no runtime behaviour changes in either package.
76
+
77
+ - Updated dependencies [93a35b7]
78
+ - Updated dependencies [f8f0dfe]
79
+ - @seamless-auth/client@0.1.0
80
+
3
81
  ## 0.12.0
4
82
 
5
83
  ### Minor Changes
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
  ## What It Exports
12
12
 
13
13
  - `AuthProvider`
14
- - `AuthRoutes`
14
+ - `AuthRoutes`, from `@seamless-auth/react/routes`
15
15
  - `useAuth()`
16
16
  - `createSeamlessAuthClient()`
17
17
  - `useAuthClient()`
@@ -29,6 +29,9 @@
29
29
  npm install @seamless-auth/react
30
30
  ```
31
31
 
32
+ `react-router-dom` is an optional peer. Install it only if you render the bundled screens from
33
+ `@seamless-auth/react/routes`; `AuthProvider`, the hooks, and the headless client do not need it.
34
+
32
35
  ## Releases
33
36
 
34
37
  Published versions are listed in [CHANGELOG.md](./CHANGELOG.md) and GitHub Releases. Releases are
@@ -42,7 +45,7 @@ You can use this package in three ways:
42
45
 
43
46
  1. `AuthProvider` + `useAuth()` for auth state and core auth actions
44
47
  2. `createSeamlessAuthClient()` or `useAuthClient()` to build fully custom login and registration screens
45
- 3. `AuthRoutes` when you want the built-in login, OTP, magic-link, and passkey screens
48
+ 3. `AuthRoutes` (from `@seamless-auth/react/routes`) when you want the built-in login, OTP, magic-link, and passkey screens
46
49
 
47
50
  Most apps will use `AuthProvider` either way.
48
51
 
@@ -82,7 +85,8 @@ function Dashboard() {
82
85
  ### Use built-in auth routes with `AuthRoutes`
83
86
 
84
87
  ```tsx
85
- import { AuthRoutes, useAuth } from '@seamless-auth/react';
88
+ import { useAuth } from '@seamless-auth/react';
89
+ import { AuthRoutes } from '@seamless-auth/react/routes';
86
90
  import { Route, Routes } from 'react-router-dom';
87
91
 
88
92
  function AppRoutes() {
@@ -102,6 +106,74 @@ function AppRoutes() {
102
106
 
103
107
  You are still responsible for your app’s route protection and redirects.
104
108
 
109
+ `AuthRoutes` moved to its own entry in 0.14.0. Earlier versions exported it from
110
+ `@seamless-auth/react`; update the import to `@seamless-auth/react/routes`.
111
+
112
+ ## Next.js
113
+
114
+ The package works in the Next.js App Router. Every module ships with a `'use client'` directive, so
115
+ `AuthProvider` can be rendered straight from a server layout, and nothing touches `window` or
116
+ storage while rendering on the server.
117
+
118
+ ```tsx
119
+ // app/layout.tsx
120
+ import { AuthProvider } from '@seamless-auth/react';
121
+
122
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
123
+ return (
124
+ <html lang="en">
125
+ <body>
126
+ <AuthProvider apiHost={process.env.NEXT_PUBLIC_API_HOST!}>
127
+ {children}
128
+ </AuthProvider>
129
+ </body>
130
+ </html>
131
+ );
132
+ }
133
+ ```
134
+
135
+ Things to know:
136
+
137
+ - **Use custom UI, not `AuthRoutes`.** The bundled screens route with react-router, which a Next.js
138
+ app does not have. Build the screens with `useAuth()`, `useAuthClient()`, and the recipes under
139
+ [Custom UI Recipes](#custom-ui-recipes). `react-router-dom` does not need to be installed.
140
+ - **Props from a server component must be serialisable.** `apiHost`, `magicLinkRedirectUri`, and
141
+ `initialSession` are. `transport` and `ports` take functions, so set those from a client component
142
+ that wraps the provider.
143
+ - **Server code imports from `@seamless-auth/client`.** Everything exported here is a client
144
+ reference inside a server component, including the pure helpers. Read `hasScopedRole`,
145
+ `roleGrantsAccess`, and the types from `@seamless-auth/client` on the server.
146
+ - **Hydration is safe.** The first client render matches the server's: `hasSignedInBefore` is
147
+ `false` on both and turns `true` straight after hydration when the browser remembers a sign-in.
148
+
149
+ ### Rendering a known session on the first paint
150
+
151
+ Without help the provider starts in `loading` and learns the session from `/users/me` after
152
+ hydration. A server that has already resolved the session can pass it as `initialSession`, and the
153
+ first paint renders it settled:
154
+
155
+ ```tsx
156
+ <AuthProvider apiHost={apiHost} initialSession={user ? { user } : null}>
157
+ {children}
158
+ </AuthProvider>
159
+ ```
160
+
161
+ Only `user` is required. The provider still revalidates in the background, without reporting
162
+ `loading`, so credentials and organizations fill in, a revoked session signs out, and a session the
163
+ server could not see (for example an expired access cookie with a live refresh cookie) still signs
164
+ in. `initialSession` is read once, when the provider creates its session.
165
+
166
+ Resolve the user on the server with `getSeamlessUser()` from `@seamless-auth/core`, which verifies
167
+ the access cookie locally and asks the auth API directly. It needs your adapter's cookie secret, and
168
+ the session cookies have to reach the Next.js server, so serve the adapter from the same site. Do not forward the browser's cookies to
169
+ your adapter's `/auth/users/me` from the server: when the access cookie has expired the adapter
170
+ rotates the refresh token in a response the browser never receives, and the browser's next refresh
171
+ is then treated as a replay and revokes the session. A dedicated server package for Next.js is tracked in
172
+ [seamless-auth-server#170](https://github.com/fells-code/seamless-auth-server/issues/170).
173
+
174
+ `refreshSession({ background: true })` is public, for revalidating a session your UI already shows
175
+ (on window focus, for example) without flashing a loading state.
176
+
105
177
  ## `useAuth()` API
106
178
 
107
179
  `useAuth()` returns the current auth state plus the provider-backed helpers:
@@ -121,7 +193,7 @@ You are still responsible for your app’s route protection and redirects.
121
193
  listOAuthProviders(): Promise<SeamlessAuthResult<OAuthProvidersResult>>;
122
194
  startOAuthLogin(input: StartOAuthLoginInput): Promise<SeamlessAuthResult<StartOAuthLoginResult>>;
123
195
  finishOAuthLogin(input: FinishOAuthLoginInput): Promise<SeamlessAuthResult<MessageResult>>;
124
- refreshSession(): Promise<SeamlessAuthResult<CurrentUserResult>>;
196
+ refreshSession(options?: { background?: boolean }): Promise<SeamlessAuthResult<CurrentUserResult>>;
125
197
  refreshStepUpStatus(): Promise<SeamlessAuthResult<StepUpStatus>>;
126
198
  verifyStepUpWithPasskey(): Promise<SeamlessAuthResult<StepUpStatus>>;
127
199
  verifyStepUpWithPasskeyPrf(input: PasskeyPrfInput): Promise<SeamlessAuthResult<StepUpPrfData>>;
@@ -197,6 +269,47 @@ result.
197
269
  Custom UIs get the same default through `useAuthClient()`, and can still override a single send with
198
270
  `requestMagicLink(uri)`.
199
271
 
272
+ ### Platform ports and transport
273
+
274
+ Everything that differs between a browser and another platform sits behind a port on
275
+ `AuthProvider`. A web application configures nothing; the browser implementations are the
276
+ defaults. A binding for another platform (React Native is the first) supplies its own:
277
+
278
+ ```tsx
279
+ <AuthProvider
280
+ apiHost={apiHost}
281
+ transport={{ mode: 'bearer', tokenStorage: keystoreTokenStorage }}
282
+ ports={{ passkeys: nativePasskeys, oauthRedirect: inAppBrowserRedirect }}
283
+ >
284
+ <App />
285
+ </AuthProvider>
286
+ ```
287
+
288
+ - `transport` selects how the session travels. Cookie transport, the default, is the browser
289
+ contract: the server adapter holds the tokens in `httpOnly` cookies and the client never sees
290
+ them. Bearer transport is the native contract: the client holds the auth API's own tokens,
291
+ presents the one a route needs in `Authorization`, stores the pair a sign-in returns through
292
+ `tokenStorage`, and refreshes once through `POST /auth/refresh` when a request answers 401. At
293
+ most one refresh is in flight at a time, because the auth API treats a replayed refresh token as
294
+ theft and revokes the chain.
295
+ - `ports.passkeys` runs the WebAuthn ceremonies (`create`, `get`, and the two support checks).
296
+ `usePasskeySupport()` and every passkey flow go through it.
297
+ - `ports.oauthRedirect` opens the provider. The browser navigates away and the callback route
298
+ finishes the login; a port that receives the callback itself (an in-app browser session)
299
+ resolves with the `code` and `state`, and the built-in buttons finish the login on the spot.
300
+
301
+ `useAuthClient()` returns the same client instance the provider's session drives. In bearer
302
+ transport that matters: the client holds the sign-in in flight, and a second client would not see it.
303
+
304
+ `useAuthorizedFetch()` (or `client.authorizedFetch`) is a fetch for your own API that carries the
305
+ session the way the transport does: `credentials: 'include'` in cookie transport, the access token
306
+ with one refresh-and-retry on a 401 in bearer transport. A path resolves on `apiHost`.
307
+
308
+ ```ts
309
+ const authorizedFetch = useAuthorizedFetch();
310
+ const plan = await authorizedFetch('/api/plan/mine').then(r => r.json());
311
+ ```
312
+
200
313
  ### Scoped roles
201
314
 
202
315
  `hasRole(role)` remains an exact role check. Use `hasScopedRole(role)` for colon-separated scoped
@@ -1059,8 +1172,10 @@ This package assumes a Seamless Auth-compatible backend with the auth adapter mo
1059
1172
 
1060
1173
  - Requests target `${apiHost}/auth/...`
1061
1174
  - `apiHost` may be provided with or without a trailing slash
1062
- - Requests are sent with `credentials: 'include'`
1175
+ - In cookie transport (the default) requests are sent with `credentials: 'include'`; in bearer
1176
+ transport they carry `x-seamless-auth-transport: bearer` and an `Authorization` header instead
1063
1177
  - `AuthProvider` validates the current session by calling `/users/me` on load
1178
+ - Bearer transport additionally uses `POST /refresh`
1064
1179
 
1065
1180
  The built-in flows assume compatible endpoints for:
1066
1181
 
@@ -1115,7 +1230,8 @@ refuses every backup-eligible passkey, so handle the code rather than assuming t
1115
1230
 
1116
1231
  ## Notes
1117
1232
 
1118
- - This package does not create its own `<BrowserRouter>`.
1233
+ - This package does not create its own `<BrowserRouter>`, and only `@seamless-auth/react/routes`
1234
+ needs one.
1119
1235
  - It is designed to fit into your app’s existing routing tree.
1120
1236
  - The quickest path is `AuthProvider` + `AuthRoutes`.
1121
1237
  - The most flexible path is `AuthProvider` + custom UI using `useAuth()`, `useAuthClient()`, and `usePasskeySupport()`.
@@ -1,14 +1,15 @@
1
- import { CurrentUserResult, FinishOAuthLoginInput, FinishOAuthLoginResult, LoginStartResult, MessageResult, OAuthProvidersResult, OrganizationSwitchResult, PasskeyLoginData, PasskeyMetadata, PasskeyRegistrationData, RegisterPasskeyOptions, StartOAuthLoginInput, StartOAuthLoginResult, StepUpPrfData, StepUpStatus } from './client/createSeamlessAuthClient';
2
- import type { SeamlessAuthResult } from './client/result';
3
- import { PasskeyPrfInput } from './client/webauthnPrf';
4
- import { Credential, Organization, User } from './types';
1
+ import { CurrentUserResult, FinishOAuthLoginInput, FinishOAuthLoginResult, LoginStartResult, MessageResult, OAuthProvidersResult, OrganizationSwitchResult, PasskeyLoginData, PasskeyMetadata, PasskeyRegistrationData, RegisterPasskeyOptions, StartOAuthLoginInput, StartOAuthLoginResult, StepUpPrfData, StepUpStatus } from '@seamless-auth/client';
2
+ import type { SeamlessAuthResult } from '@seamless-auth/client';
3
+ import { PasskeyPrfInput } from '@seamless-auth/client';
4
+ import { type InitialSession, type RefreshSessionOptions, type OAuthRedirectPort, type PasskeyPort, type SeamlessAuthClient, type TransportOptions } from '@seamless-auth/client';
5
+ import { Credential, Organization, User } from '@seamless-auth/client';
5
6
  import React, { ReactNode } from 'react';
6
7
  export interface AuthContextType {
7
8
  user: User | null;
8
9
  logout: () => Promise<SeamlessAuthResult<MessageResult>>;
9
10
  logoutAllSessions: () => Promise<SeamlessAuthResult<MessageResult>>;
10
11
  deleteUser: () => Promise<SeamlessAuthResult<MessageResult>>;
11
- refreshSession: () => Promise<SeamlessAuthResult<CurrentUserResult>>;
12
+ refreshSession: (options?: RefreshSessionOptions) => Promise<SeamlessAuthResult<CurrentUserResult>>;
12
13
  isAuthenticated: boolean;
13
14
  hasRole: (role: string) => boolean | undefined;
14
15
  hasScopedRole: (role: string | string[]) => boolean | undefined;
@@ -34,6 +35,18 @@ export interface AuthContextType {
34
35
  verifyStepUpWithPasskeyPrf: (input: PasskeyPrfInput) => Promise<SeamlessAuthResult<StepUpPrfData>>;
35
36
  verifyStepUpWithTotp: (code: string) => Promise<SeamlessAuthResult<StepUpStatus>>;
36
37
  loading: boolean;
38
+ /** The client behind the session. `useAuthClient()` returns this same instance. */
39
+ client: SeamlessAuthClient;
40
+ /** The platform ports the built-in screens and hooks go through. */
41
+ ports: AuthPorts;
42
+ }
43
+ /**
44
+ * What a binding plugs in for its platform. The browser defaults cover a web
45
+ * application; a native binding supplies its own.
46
+ */
47
+ export interface AuthPorts {
48
+ passkeys: PasskeyPort;
49
+ oauthRedirect: OAuthRedirectPort;
37
50
  }
38
51
  /**
39
52
  * Returns the current context
@@ -49,6 +62,21 @@ interface AuthProviderProps {
49
62
  * send and a resend read it from here, so the two cannot drift apart.
50
63
  */
51
64
  magicLinkRedirectUri?: string;
65
+ /**
66
+ * How the session travels to the server adapter. Defaults to cookie
67
+ * transport, which is what a browser application wants.
68
+ */
69
+ transport?: Omit<TransportOptions, 'apiHost'>;
70
+ /** Platform ports. Each one left out falls back to the browser's. */
71
+ ports?: Partial<AuthPorts>;
72
+ /**
73
+ * The session a server already resolved for this request, or `null` when it
74
+ * found none. The first paint renders it settled instead of loading, and the
75
+ * provider then revalidates in the background, which fills in credentials and
76
+ * organizations and catches a session the server could not see. Read once,
77
+ * when the session is created.
78
+ */
79
+ initialSession?: InitialSession | null;
52
80
  }
53
81
  export declare const AuthProvider: React.FC<AuthProviderProps>;
54
82
  export {};
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Paths for the bundled auth screens.
3
+ *
4
+ * Kebab-case throughout, except for two paths that are dictated by contracts
5
+ * outside this package rather than by the naming convention:
6
+ *
7
+ * - `verifyMagicLink` is the URL the auth API builds when it emails a magic
8
+ * link, so it has to match that value exactly.
9
+ * - `oauthCallback` is registered with OAuth providers as an allowed redirect
10
+ * URI, so renaming it would break configured integrations.
11
+ */
12
+ export declare const authRoutePaths: {
13
+ readonly login: "/login";
14
+ readonly passkeyLogin: "/passkey-login";
15
+ readonly verifyPhoneOtp: "/verify-phone-otp";
16
+ readonly verifyEmailOtp: "/verify-email-otp";
17
+ readonly verifyMagicLink: "/verify-magiclink";
18
+ readonly oauthCallback: "/oauth/callback";
19
+ readonly registerPasskey: "/register-passkey";
20
+ readonly magicLinkSent: "/magic-link-sent";
21
+ };
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ import{createContext as t,useContext as e,useMemo as s,useRef as a,useSyncExternalStore as r,useEffect as i,useState as o}from"react";import{jsx as n}from"react/jsx-runtime";import{createBrowserOAuthRedirect as c,createBrowserPasskeyPort as l,createAuthSession as u}from"@seamless-auth/client";const p=t(void 0),d=()=>{const t=e(p);if(!t)throw new Error("useAuth must be used within an AuthProvider");return t},h=({children:t,apiHost:e,autoDetectPreviousSignin:o=!0,magicLinkRedirectUri:d,transport:h,ports:g,initialSession:k})=>{const y=s(()=>({passkeys:g?.passkeys??l(),oauthRedirect:g?.oauthRedirect??c()}),[g?.passkeys,g?.oauthRedirect]),{mode:m,basePath:S,tokenStorage:f,fetch:v}=h??{},b=s(()=>h?{mode:m,basePath:S,tokenStorage:f,fetch:v}:void 0,[m,S,f,v]),P=a(k),R=s(()=>u({apiHost:e,magicLinkRedirectUri:d,transport:b,passkeys:y.passkeys,detectPreviousSignIn:o,initialSession:P.current}),[e,d,b,y.passkeys,o]),w=r(R.subscribe,R.getState,R.getServerState);i(()=>{R.actions.refreshSession({background:void 0!==P.current})},[R]);const A=s(()=>({...w,...R.actions,apiHost:e,magicLinkRedirectUri:d,client:R.client,ports:y}),[w,R,e,d,y]);return n(p.Provider,{value:A,children:t})},g=()=>d().client,k=["passkey","magic_link"],y=()=>{const t=g(),[e,s]=o(null),[a,r]=o(!0);return i(()=>{let e=!0;return(async()=>{try{const{data:a,error:r}=await t.getPublicSystemConfig();e&&!r&&a?.loginMethods?.length&&s(a.loginMethods)}catch{}finally{e&&r(!1)}})(),()=>{e=!1}},[t]),{loginMethods:e,loading:a}},m=t=>Boolean(t?.some(t=>"passkey"!==t)),S=()=>{const{ports:t}=d(),[e,s]=o(!1),[a,r]=o(!0);return i(()=>{let e=!0;return(async()=>{try{const a=t.passkeys.isSupported()&&await t.passkeys.isPlatformAuthenticatorAvailable();e&&s(a)}catch{e&&s(!1)}finally{e&&r(!1)}})(),()=>{e=!1}},[t.passkeys]),{passkeySupported:e,loading:a}};export{h as A,k as F,g as a,y as b,S as c,m as h,d as u};
3
+ //# sourceMappingURL=shared-BzrBuR0z.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-BzrBuR0z.js","sources":["../../src/AuthProvider.tsx","../../src/hooks/useAuthClient.ts","../../src/hooks/useLoginMethods.ts","../../src/hooks/usePasskeySupport.ts"],"sourcesContent":[null,null,null,null],"names":["AuthContext","createContext","undefined","useAuth","context","useContext","Error","AuthProvider","children","apiHost","autoDetectPreviousSignin","magicLinkRedirectUri","transport","ports","providedPorts","initialSession","useMemo","passkeys","createBrowserPasskeyPort","oauthRedirect","createBrowserOAuthRedirect","mode","basePath","tokenStorage","fetch","fetchImpl","stableTransport","initialSessionRef","useRef","session","createAuthSession","detectPreviousSignIn","current","state","useSyncExternalStore","subscribe","getState","getServerState","useEffect","actions","refreshSession","background","value","client","_jsx","Provider","useAuthClient","FALLBACK_LOGIN_METHODS","useLoginMethods","authClient","loginMethods","setLoginMethods","useState","loading","setLoading","active","async","data","error","getPublicSystemConfig","length","read","hasNonPasskeyLoginMethod","Boolean","some","method","usePasskeySupport","passkeySupported","setPasskeySupported","supported","isSupported","isPlatformAuthenticatorAvailable","checkSupport"],"mappings":";sSA4GA,MAAMA,EAAcC,OAA2CC,GAMlDC,EAAU,KACrB,MAAMC,EAAUC,EAAWL,GAC3B,IAAKI,EACH,MAAM,IAAIE,MAAM,+CAElB,OAAOF,GA6BIG,EAA4C,EACvDC,WACAC,UACAC,4BAA2B,EAC3BC,uBACAC,YACAC,MAAOC,EACPC,qBAKA,MAAMF,EAAQG,EACZ,KAAA,CACEC,SAAUH,GAAeG,UAAYC,IACrCC,cAAeL,GAAeK,eAAiBC,MAEjD,CAACN,GAAeG,SAAUH,GAAeK,iBAGrCE,KAAEA,EAAIC,SAAEA,EAAQC,aAAEA,EAAcC,MAAOC,GAAcb,GAAa,CAAA,EAClEc,EAAkBV,EACtB,IAAOJ,EAAY,CAAES,OAAMC,WAAUC,eAAcC,MAAOC,QAAcvB,EAExE,CAACmB,EAAMC,EAAUC,EAAcE,IAM3BE,EAAoBC,EAAOb,GAE3Bc,EAAUb,EACd,IACEc,EAAkB,CAChBrB,UACAE,uBACAC,UAAWc,EACXT,SAAUJ,EAAMI,SAChBc,qBAAsBrB,EACtBK,eAAgBY,EAAkBK,UAEtC,CACEvB,EACAE,EACAe,EACAb,EAAMI,SACNP,IAOEuB,EAAQC,EACZL,EAAQM,UACRN,EAAQO,SACRP,EAAQQ,gBAcVC,EAAU,KACHT,EAAQU,QAAQC,eAAe,CAClCC,gBAA0CvC,IAA9ByB,EAAkBK,WAE/B,CAACH,IAEJ,MAAMa,EAAQ1B,EACZ,KAAA,IACKiB,KACAJ,EAAQU,QACX9B,UACAE,uBACAgC,OAAQd,EAAQc,OAChB9B,UAEF,CAACoB,EAAOJ,EAASpB,EAASE,EAAsBE,IAGlD,OAAO+B,EAAC5C,EAAY6C,SAAQ,CAACH,MAAOA,EAAKlC,SAAGA,KChOjCsC,EAAgB,IACpB3C,IAAUwC,OCINI,EAAwC,CAAC,UAAW,cAWpDC,EAAkB,KAC7B,MAAMC,EAAaH,KACZI,EAAcC,GAAmBC,EAA+B,OAChEC,EAASC,GAAcF,GAAS,GA8BvC,OA5BAd,EAAU,KACR,IAAIiB,GAAS,EAsBb,MApBaC,WACX,IACE,MAAMC,KAAEA,EAAIC,MAAEA,SAAgBT,EAAWU,wBAErCJ,IAAWG,GAASD,GAAMP,cAAcU,QAC1CT,EAAgBM,EAAKP,aAEzB,CAAE,MAIF,SACMK,GACFD,GAAW,EAEf,GAGGO,GAEE,KACLN,GAAS,IAEV,CAACN,IAEG,CAAEC,eAAcG,YAUZS,EAA4BZ,GACvCa,QAAQb,GAAcc,KAAKC,GAAqB,YAAXA,IC/D1BC,EAAoB,KAC/B,MAAMrD,MAAEA,GAAUV,KACXgE,EAAkBC,GAAuBhB,GAAS,IAClDC,EAASC,GAAcF,GAAS,GA+BvC,OA7BAd,EAAU,KACR,IAAIiB,GAAS,EAuBb,MArBqBC,WACnB,IACE,MAAMa,EACJxD,EAAMI,SAASqD,qBACRzD,EAAMI,SAASsD,mCACpBhB,GACFa,EAAoBC,EAExB,CAAE,MACId,GACFa,GAAoB,EAExB,SACMb,GACFD,GAAW,EAEf,GAGGkB,GAEE,KACLjB,GAAS,IAEV,CAAC1C,EAAMI,WAEH,CAAEkD,mBAAkBd"}
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import type { LoginMethod } from '../client/createSeamlessAuthClient';
2
+ import type { LoginMethod } from '@seamless-auth/client';
3
3
  interface AuthFallbackOptionsProps {
4
4
  identifier: string;
5
5
  onMagicLink: () => void;
@@ -1 +1,6 @@
1
- export declare const useAuthClient: () => import("../client/createSeamlessAuthClient").SeamlessAuthClient;
1
+ /**
2
+ * The client behind the provider's session. The same instance the store
3
+ * drives, not a second one: in bearer transport the client holds the sign-in
4
+ * in flight, and a second client would not see it.
5
+ */
6
+ export declare const useAuthClient: () => import("@seamless-auth/client").SeamlessAuthClient;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * A fetch for the application's own API that carries the session the way the
3
+ * provider's transport does: cookies for a web application, the access token
4
+ * (refreshed once on a 401) for a native one. A path resolves on `apiHost`.
5
+ */
6
+ export declare const useAuthorizedFetch: () => (input: string | URL, init?: RequestInit) => Promise<Response>;
@@ -1,4 +1,4 @@
1
- import type { LoginMethod } from '../client/createSeamlessAuthClient';
1
+ import type { LoginMethod } from '@seamless-auth/client';
2
2
  /**
3
3
  * Used only until the instance answers, and if it never does.
4
4
  *
package/dist/index.d.ts CHANGED
@@ -1,13 +1,14 @@
1
- import { AuthContextType, AuthProvider, useAuth } from './AuthProvider';
2
- import { AuthRoutes } from './AuthRoutes';
3
- import { createSeamlessAuthClient, CredentialUpdateResult, CreateOrganizationInput, CurrentUserResult, FinishOAuthLoginInput, FinishOAuthLoginResult, LoginInput, LoginMethod, LoginStartResult, MessageResult, OAuthProvider, OAuthProvidersResult, PublicSystemConfigResult, OrganizationMemberInput, OrganizationMemberUpdateInput, OrganizationMembersResult, OrganizationMembershipResult, OrganizationResult, OrganizationSwitchResult, OrganizationsResult, PasskeyLoginData, PasskeyAttachment, PasskeyMetadata, PasskeyRegistrationData, RegisterInput, RegisterPasskeyOptions, SeamlessAuthClient, SeamlessAuthClientOptions, StartOAuthLoginInput, StartOAuthLoginResult, StepUpMethod, StepUpStatus, StepUpPrfData, TotpEnrollmentStartResult, TotpStatus, UpdateOrganizationInput } from './client/createSeamlessAuthClient';
4
- import { getOAuthErrorCode, getPasskeyPolicyErrorCode, getWebAuthnErrorDetail, isUnauthenticated, OAuthErrorCode, PasskeyPolicyErrorCode, SeamlessAuthError, WebAuthnErrorDetail } from './client/errors';
5
- import type { SeamlessAuthResult } from './client/result';
6
- import { encodePrfSalt, extractPasskeyPrfResult, isPasskeyPrfSupported, PasskeyPrfInput, PasskeyPrfResult } from './client/webauthnPrf';
1
+ import { AuthContextType, AuthPorts, AuthProvider, useAuth } from './AuthProvider';
2
+ import { createSeamlessAuthClient, CredentialUpdateResult, CreateOrganizationInput, CurrentUserResult, FinishOAuthLoginInput, FinishOAuthLoginResult, LoginInput, LoginMethod, LoginStartResult, MessageResult, OAuthProvider, OAuthProvidersResult, PublicSystemConfigResult, OrganizationMemberInput, OrganizationMemberUpdateInput, OrganizationMembersResult, OrganizationMembershipResult, OrganizationResult, OrganizationSwitchResult, OrganizationsResult, PasskeyLoginData, PasskeyAttachment, PasskeyMetadata, PasskeyRegistrationData, RegisterInput, RegisterPasskeyOptions, SeamlessAuthClient, SeamlessAuthClientOptions, StartOAuthLoginInput, StartOAuthLoginResult, StepUpMethod, StepUpStatus, StepUpPrfData, TotpEnrollmentStartResult, TotpStatus, UpdateOrganizationInput } from '@seamless-auth/client';
3
+ import { getOAuthErrorCode, getPasskeyPolicyErrorCode, getWebAuthnErrorDetail, isUnauthenticated, OAuthErrorCode, PasskeyPolicyErrorCode, SeamlessAuthError, WebAuthnErrorDetail } from '@seamless-auth/client';
4
+ import type { SeamlessAuthResult } from '@seamless-auth/client';
5
+ import { encodePrfSalt, extractPasskeyPrfResult, isPasskeyPrfSupported, PasskeyPrfInput, PasskeyPrfResult } from '@seamless-auth/client';
7
6
  import { useAuthClient } from './hooks/useAuthClient';
7
+ import { useAuthorizedFetch } from './hooks/useAuthorizedFetch';
8
8
  import { hasNonPasskeyLoginMethod, useLoginMethods } from './hooks/useLoginMethods';
9
9
  import { usePasskeySupport } from './hooks/usePasskeySupport';
10
- import { hasScopedRole, roleGrantsAccess } from './scopedRoles';
11
- import { Credential, Organization, OrganizationMembership, User } from './types';
12
- export { AuthProvider, AuthRoutes, createSeamlessAuthClient, encodePrfSalt, extractPasskeyPrfResult, getOAuthErrorCode, getPasskeyPolicyErrorCode, getWebAuthnErrorDetail, hasNonPasskeyLoginMethod, hasScopedRole, isPasskeyPrfSupported, isUnauthenticated, roleGrantsAccess, SeamlessAuthError, useAuth, useAuthClient, useLoginMethods, usePasskeySupport, };
13
- export type { CredentialUpdateResult, AuthContextType, Credential, CreateOrganizationInput, CurrentUserResult, FinishOAuthLoginInput, FinishOAuthLoginResult, LoginInput, LoginMethod, LoginStartResult, MessageResult, OAuthErrorCode, OAuthProvider, OAuthProvidersResult, Organization, OrganizationMemberInput, OrganizationMembership, OrganizationMemberUpdateInput, OrganizationMembersResult, OrganizationMembershipResult, OrganizationResult, OrganizationSwitchResult, OrganizationsResult, PasskeyLoginData, PasskeyAttachment, PasskeyMetadata, PasskeyPolicyErrorCode, PasskeyPrfInput, PasskeyPrfResult, PasskeyRegistrationData, PublicSystemConfigResult, RegisterInput, RegisterPasskeyOptions, SeamlessAuthClient, SeamlessAuthClientOptions, SeamlessAuthResult, StartOAuthLoginInput, StartOAuthLoginResult, StepUpMethod, StepUpStatus, StepUpPrfData, TotpEnrollmentStartResult, TotpStatus, UpdateOrganizationInput, User, WebAuthnErrorDetail, };
10
+ import { hasScopedRole, roleGrantsAccess } from '@seamless-auth/client';
11
+ import { Credential, Organization, OrganizationMembership, User } from '@seamless-auth/client';
12
+ import type { InitialSession, OAuthRedirectOutcome, OAuthRedirectPort, PasskeyPort, StoredTokens, TokenStoragePort, RefreshSessionOptions, TransportOptions } from '@seamless-auth/client';
13
+ export { AuthProvider, createSeamlessAuthClient, encodePrfSalt, extractPasskeyPrfResult, getOAuthErrorCode, getPasskeyPolicyErrorCode, getWebAuthnErrorDetail, hasNonPasskeyLoginMethod, hasScopedRole, isPasskeyPrfSupported, isUnauthenticated, roleGrantsAccess, SeamlessAuthError, useAuth, useAuthClient, useAuthorizedFetch, useLoginMethods, usePasskeySupport, };
14
+ export type { CredentialUpdateResult, AuthContextType, AuthPorts, Credential, CreateOrganizationInput, CurrentUserResult, FinishOAuthLoginInput, FinishOAuthLoginResult, InitialSession, LoginInput, LoginMethod, LoginStartResult, MessageResult, OAuthErrorCode, OAuthProvider, OAuthProvidersResult, OAuthRedirectOutcome, OAuthRedirectPort, Organization, OrganizationMemberInput, OrganizationMembership, OrganizationMemberUpdateInput, OrganizationMembersResult, OrganizationMembershipResult, OrganizationResult, OrganizationSwitchResult, OrganizationsResult, PasskeyLoginData, PasskeyAttachment, PasskeyMetadata, PasskeyPolicyErrorCode, PasskeyPort, PasskeyPrfInput, PasskeyPrfResult, PasskeyRegistrationData, PublicSystemConfigResult, RefreshSessionOptions, RegisterInput, RegisterPasskeyOptions, SeamlessAuthClient, SeamlessAuthClientOptions, SeamlessAuthResult, StartOAuthLoginInput, StartOAuthLoginResult, StepUpMethod, StepUpStatus, StepUpPrfData, StoredTokens, TokenStoragePort, TotpEnrollmentStartResult, TotpStatus, TransportOptions, UpdateOrganizationInput, User, WebAuthnErrorDetail, };
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
- import{jsx as e,jsxs as t,Fragment as r}from"react/jsx-runtime";import{browserSupportsWebAuthn as n,bufferToBase64URLString as o,base64URLStringToBuffer as i,startAuthentication as a,startRegistration as s,WebAuthnError as l}from"@simplewebauthn/browser";import{createContext as c,useMemo as d,useSyncExternalStore as u,useEffect as m,useContext as _,useState as g,useRef as p,useCallback as f}from"react";import{useHref as h,useNavigate as y,useLocation as b,useSearchParams as v,Routes as k,Route as w,Navigate as x}from"react-router-dom";import P from"libphonenumber-js";class S extends Error{constructor(e,t,r,n){super(e),this.name="SeamlessAuthError",this.status=t,this.body=r,this.cause=n}}const O={oauth_missing_email:!0,oauth_email_not_verified:!0,oauth_missing_subject:!0};function N(e){if("object"==typeof e&&null!==e)return e.code}function T(e){if(!(e instanceof S))return;if("object"!=typeof e.body||null===e.body)return;const t=N(e.body)??N(e.body.details);return"string"==typeof t&&Object.prototype.hasOwnProperty.call(O,t)?t:void 0}const C={attachment_not_allowed:!0,synced_passkey_not_allowed:!0,authenticator_not_allowed:!0,prf_required:!0};function I(e){if("object"!=typeof e||null===e)return;const t=e.error;return"string"==typeof t&&Object.prototype.hasOwnProperty.call(C,t)?t:void 0}function A(e){if(e instanceof S&&"object"==typeof e.body&&null!==e.body)return I(e.body)??I(e.body.details)}function M(e){return e instanceof S&&401===e.status}function E(e){if(!(e instanceof S)||("object"!=typeof(t=e.cause)||null===t||"string"!=typeof t.name))return;var t;const{name:r,code:n,message:o}=e.cause;return{name:r,code:"string"==typeof n?n:void 0,message:"string"==typeof o?o:""}}async function z(e,t){let r;try{r=await e.json()}catch{r=void 0}return new S(function(e){if("object"!=typeof e||null===e)return;const{error:t,message:r}=e;return"string"==typeof t&&t?t:"string"==typeof r&&r?r:void 0}(r)??t,e.status,r)}async function F(e){try{return await e.json()}catch{return}}async function U(e,t){let r;try{r=await e}catch{return{data:null,error:new S(t,0)}}return r.ok?{data:await F(r),error:null}:{data:null,error:await z(r,t)}}function L(e){return{data:e,error:null}}function R(e,t=0,r,n){return{data:null,error:new S(e,t,r,n)}}function q(e){if(e instanceof ArrayBuffer)return e;const t=new Uint8Array(e.buffer,e.byteOffset,e.byteLength),r=new Uint8Array(t.byteLength);return r.set(t),r.buffer}function B(e){return"string"==typeof e?e:o(q(e))}function j(e){return"string"==typeof e?i(e):e}function J(e){return{...e.credentialId?{credentialId:e.credentialId}:{},prf:{salt:B(e.salt),...e.secondSalt?{secondSalt:B(e.secondSalt)}:{}}}}async function G(){return!!n()&&("undefined"==typeof window||!1!==window.isSecureContext)&&void 0!==globalThis.PublicKeyCredential&&"undefined"!=typeof navigator&&"function"==typeof navigator.credentials?.get}function W(e){const t=e,r=t.extensions?.prf?.eval;return r?{...t,extensions:{...t.extensions,prf:{...t.extensions?.prf,eval:{...r,...r.first?{first:j(r.first)}:{},...r.second?{second:j(r.second)}:{}}}}}:e}function $(e){const t=e.clientExtensionResults,r=t?.prf?.results?.first;if(!r)return null;const n=(i=r)instanceof ArrayBuffer?new Uint8Array(i):new Uint8Array(i.buffer,i.byteOffset,i.byteLength);var i;return{credentialId:e.id,output:n,outputBase64url:o(q(n))}}function D(e){const t=e.clientExtensionResults;return t?.prf?.results?{...e,clientExtensionResults:{...e.clientExtensionResults,prf:{...t.prf,results:void 0}}}:e}function V(e,t){const r=e.method===t?e.method:null;return"Success"===e.message&&!0===e.fresh&&null!==r?L({fresh:!0,method:r,verifiedAt:e.verifiedAt??null,expiresAt:e.expiresAt??null,maxAgeSeconds:e.maxAgeSeconds??0}):R(e.message??"Step-up authentication failed.")}function H(e){return e?{method:"POST",body:JSON.stringify(J(e))}:{method:"POST"}}function K(e,t,r){const n=R(t,0,void 0,r),o=E(n.error);return console.error(e,o?.name??"Unknown error."),n}const Y=e=>{const t=(e=>{const{authHost:t}=e;return async function(e,r){const n=`${t?.replace(/\/+$/,"")??""}/auth${e.startsWith("/")?e:`/${e}`}`,o=null!=r?.body,i={...r,credentials:"include",headers:{...o?{"Content-Type":"application/json"}:{},...r?.headers}};return fetch(n,i)}})({authHost:e.apiHost});return{getCurrentUser:()=>U(t("users/me",{method:"GET"}),"Failed to load the current user."),login:e=>U(t("/login",{method:"POST",body:JSON.stringify(e)}),"Failed to start sign-in."),loginWithPasskey:async e=>{const r=await U(t("/webAuthn/login/start",{...H(e?.prf)}),"Failed to start passkey login.");if(r.error)return r;let n,o;try{const e=await a({optionsJSON:W(r.data)});n=$(e),o=D(e)}catch(e){return K("Passkey login error.","Passkey login failed.",e)}const i=await U(t("/webAuthn/login/finish",{method:"POST",body:JSON.stringify({assertionResponse:o})}),"Failed to verify passkey.");return i.error?i:"Success"!==i.data?.message?R(i.data?.message??"Passkey login failed."):L(n?{prf:n}:{})},logout:(e={})=>U(t("all_sessions"===e.scope?"/logout/all":"/logout",{method:"DELETE"}),"Failed to sign out."),logoutAllSessions:()=>U(t("/logout/all",{method:"DELETE"}),"Failed to sign out of all sessions."),deleteUser:()=>U(t("/users/delete",{method:"DELETE"}),"Failed to delete the account."),register:e=>U(t("/registration/register",{method:"POST",body:JSON.stringify({email:e.email,...e.phone?{phone:e.phone}:{}})}),"Failed to register."),requestPhoneOtp:()=>U(t("/otp/generate-phone-otp",{method:"POST",body:JSON.stringify({})}),"Failed to send the SMS code."),verifyPhoneOtp:e=>U(t("/otp/verify-phone-otp",{method:"POST",body:JSON.stringify({verificationToken:e})}),"Failed to verify the SMS code."),requestLoginPhoneOtp:()=>U(t("/otp/generate-login-phone-otp",{method:"POST",body:JSON.stringify({})}),"Failed to send the SMS code."),verifyLoginPhoneOtp:e=>U(t("/otp/verify-login-phone-otp",{method:"POST",body:JSON.stringify({verificationToken:e})}),"Failed to verify the SMS code."),requestEmailOtp:()=>U(t("/otp/generate-email-otp",{method:"POST",body:JSON.stringify({})}),"Failed to send the email code."),verifyEmailOtp:e=>U(t("/otp/verify-email-otp",{method:"POST",body:JSON.stringify({verificationToken:e})}),"Failed to verify the email code."),requestLoginEmailOtp:()=>U(t("/otp/generate-login-email-otp",{method:"POST",body:JSON.stringify({})}),"Failed to send the email code."),verifyLoginEmailOtp:e=>U(t("/otp/verify-login-email-otp",{method:"POST",body:JSON.stringify({verificationToken:e})}),"Failed to verify the email code."),requestMagicLink:r=>{const n=r??e.magicLinkRedirectUri;return U(t("/magic-link",{method:"POST",body:JSON.stringify(n?{redirectUri:n}:{})}),"Failed to send the magic link.")},checkMagicLink:()=>U(t("/magic-link/check",{method:"GET"}),"Failed to check the magic link."),verifyMagicLink:e=>U(t(`/magic-link/verify/${encodeURIComponent(e)}`,{method:"GET"}),"Failed to verify the magic link."),listOAuthProviders:()=>U(t("/oauth/providers",{method:"GET"}),"Failed to list OAuth providers."),getPublicSystemConfig:()=>U(t("/system-config/public",{method:"GET"}),"Failed to read the public system configuration."),startOAuthLogin:e=>U(t(`/oauth/${encodeURIComponent(e.providerId)}/start`,{method:"POST",body:JSON.stringify({...e.redirectUri?{redirectUri:e.redirectUri}:{},...e.returnTo?{returnTo:e.returnTo}:{}})}),"Failed to start OAuth login."),finishOAuthLogin:e=>U(t(`/oauth/${encodeURIComponent(e.providerId)}/callback`,{method:"POST",body:JSON.stringify({code:e.code,state:e.state})}),"Failed to finish OAuth login."),registerPasskey:async e=>{const r=function(e){return"metadata"in e?e:{metadata:e}}(e),n=await U(t(function(e){const t=new URLSearchParams;e.requirePrf?t.set("requirePrf","true"):e.requestPrf&&t.set("requestPrf","true"),e.attachment&&t.set("attachment",e.attachment);const r=t.toString();return"/webAuthn/register/start"+(r?`?${r}`:"")}(r),{method:"GET"}),"Failed to fetch passkey registration challenge.");if(n.error)return n;let o;try{o=await s({optionsJSON:n.data})}catch(e){return e instanceof l?R(e.name,0,void 0,e):K("Passkey registration error.","Passkey registration failed.",e)}const i=function(e){const t=e.clientExtensionResults;return!0===t?.prf?.enabled}(o),a=await U(t("/webAuthn/register/finish",{method:"POST",body:JSON.stringify({attestationResponse:o,metadata:{...r.metadata,prfCapable:i}})}),"Verification failed.");return a.error?a:L({credentialId:o.id,prfCapable:i})},isPasskeyPrfSupported:G,getStepUpStatus:()=>U(t("/step-up/status",{method:"GET"}),"Failed to load step-up status."),verifyStepUpWithPasskey:async()=>{const e=await U(t("/step-up/webauthn/start",{method:"POST"}),"Failed to start step-up authentication.");if(e.error)return e;let r;try{r=D(await a({optionsJSON:W(e.data)}))}catch(e){return K("Step-up authentication error.","Step-up authentication failed.",e)}const n=await U(t("/step-up/webauthn/finish",{method:"POST",body:JSON.stringify({assertionResponse:r})}),"Failed to verify step-up authentication.");return n.error?n:V(n.data??{},"webauthn")},verifyStepUpWithPasskeyPrf:async e=>{const r=await U(t("/step-up/webauthn/start",{...H(e)}),"Failed to start step-up authentication.");if(r.error)return r;let n,o;try{const e=await a({optionsJSON:W(r.data)});n=$(e),o=D(e)}catch(e){return K("Step-up authentication error.","Step-up authentication failed.",e)}if(!n)return R("Passkey did not return PRF output.");const i=await U(t("/step-up/webauthn/finish",{method:"POST",body:JSON.stringify({assertionResponse:o})}),"Failed to verify step-up authentication.");if(i.error)return i;const s=V(i.data??{},"webauthn");return s.error?s:L({...s.data,credentialId:n.credentialId,prf:n})},getTotpStatus:()=>U(t("/totp/status",{method:"GET"}),"Failed to load authenticator status."),startTotpEnrollment:()=>U(t("/totp/enroll/start",{method:"POST"}),"Failed to start authenticator enrollment."),verifyTotpEnrollment:e=>U(t("/totp/enroll/verify",{method:"POST",body:JSON.stringify({code:e})}),"Failed to verify the authenticator code."),disableTotp:e=>U(t("/totp/disable",{method:"POST",body:JSON.stringify({code:e})}),"Failed to disable the authenticator."),verifyStepUpWithTotp:async e=>{const r=await U(t("/totp/verify-mfa",{method:"POST",body:JSON.stringify({code:e})}),"Failed to verify step-up authentication.");return r.error?r:V(r.data??{},"totp")},updateCredential:e=>U(t("users/credentials",{method:"POST",body:JSON.stringify(e)}),"Failed to update credential."),deleteCredential:e=>U(t("users/credentials",{method:"DELETE",body:JSON.stringify({id:e})}),"Failed to delete credential."),listOrganizations:()=>U(t("/organizations",{method:"GET"}),"Failed to list organizations."),createOrganization:e=>U(t("/organizations",{method:"POST",body:JSON.stringify(e)}),"Failed to create organization."),getOrganization:e=>U(t(`/organizations/${encodeURIComponent(e)}`,{method:"GET"}),"Failed to load organization."),updateOrganization:(e,r)=>U(t(`/organizations/${encodeURIComponent(e)}`,{method:"PATCH",body:JSON.stringify(r)}),"Failed to update organization."),switchOrganization:e=>U(t(`/organizations/${encodeURIComponent(e)}/switch`,{method:"POST"}),"Failed to switch organization."),listOrganizationMembers:e=>U(t(`/organizations/${encodeURIComponent(e)}/members`,{method:"GET"}),"Failed to list organization members."),addOrganizationMember:(e,r)=>U(t(`/organizations/${encodeURIComponent(e)}/members`,{method:"POST",body:JSON.stringify(r)}),"Failed to add organization member."),updateOrganizationMember:(e,r,n)=>U(t(`/organizations/${encodeURIComponent(e)}/members/${encodeURIComponent(r)}`,{method:"PATCH",body:JSON.stringify(n)}),"Failed to update organization member."),removeOrganizationMember:(e,r)=>U(t(`/organizations/${encodeURIComponent(e)}/members/${encodeURIComponent(r)}`,{method:"DELETE"}),"Failed to remove organization member.")}};function Z(e,t){const r=e.trim(),n=t.trim();if(!r||!n)return!1;if(r===n)return!0;if(r.endsWith(":*")){const e=r.slice(0,-2);return n===e||n.startsWith(`${e}:`)}if(!n.includes(":"))return!1;if(!r.includes(":"))return n.startsWith(`${r}:`);const o=r.split(":"),i=n.split(":"),a=o[o.length-1],s=i[i.length-1],l=o.slice(0,-1),c=i.slice(0,-1);return"write"===a&&"read"===s&&l.length===c.length&&l.every((e,t)=>e===c[t])}function Q(e,t){if(!Array.isArray(e))return!1;const r=e.filter(e=>"string"==typeof e);return(Array.isArray(t)?t:[t]).some(e=>r.some(t=>Z(t,e)))}function X(){return"undefined"==typeof localStorage?function(){const e=new Map;return{get:t=>e.get(t)??null,set:(t,r)=>{e.set(t,r)}}}():{get:e=>{try{return localStorage.getItem(e)}catch{return null}},set:(e,t)=>{try{localStorage.setItem(e,t)}catch{}}}}const ee="seamlessauth_seen",te={user:null,credentials:[],organizations:[],activeOrganization:null,stepUpStatus:null,isAuthenticated:!1};const re=c(void 0),ne=()=>{const e=_(re);if(!e)throw new Error("useAuth must be used within an AuthProvider");return e},oe=({children:t,apiHost:r,autoDetectPreviousSignin:n=!0,magicLinkRedirectUri:o})=>{const i=d(()=>function(e){const{apiHost:t,detectPreviousSignIn:r=!0}=e,n=Y({apiHost:t}),o=e.storage??X(),i=new Set;let a=!1,s=0,l={...te,loading:!0,hasSignedInBefore:r&&"true"===o.get(ee)};function c(e){const t={...l,...e};Object.keys(e).some(e=>l[e]!==t[e])&&(l=t,i.forEach(e=>e()))}function d(){o.set(ee,"true"),r&&c({hasSignedInBefore:!0})}function u(){c(te)}async function m(){const e=++s;c({loading:!0});const t=await n.getCurrentUser();return a||e!==s?t:t.error?(u(),c({loading:!1}),t):(c({user:t.data.user,credentials:t.data.credentials??[],organizations:t.data.organizations??[],activeOrganization:t.data.activeOrganization??null,isAuthenticated:!0,loading:!1}),l.hasSignedInBefore||d(),t)}async function _(e){const t=await e();return t.error||await m(),t}return{getState:()=>l,subscribe:e=>(i.add(e),()=>{i.delete(e)}),actions:{login:(e,t)=>n.login({identifier:e,passkeyAvailable:t}),handlePasskeyLogin:()=>_(()=>n.loginWithPasskey()),registerPasskey:e=>_(()=>n.registerPasskey(e)),refreshSession:m,logout:async function(){try{return await n.logout()}finally{u()}},logoutAllSessions:async function(){try{return await n.logoutAllSessions()}finally{u()}},deleteUser:async()=>{const e=await n.deleteUser();return e.error||u(),e},updateCredential:async e=>{const{data:t,error:r}=await n.updateCredential({friendlyName:e.friendlyName??null,id:e.id});if(r)return{data:null,error:r};const o=t.credential;return c({credentials:l.credentials.map(e=>e.id===o.id?{...e,...o}:e)}),{data:o,error:null}},deleteCredential:async e=>{const t=await n.deleteCredential(e);return t.error||c({credentials:l.credentials.filter(t=>t.id!==e)}),t},switchOrganization:e=>_(()=>n.switchOrganization(e)),listOAuthProviders:()=>n.listOAuthProviders(),startOAuthLogin:e=>n.startOAuthLogin(e),finishOAuthLogin:e=>_(()=>n.finishOAuthLogin(e)),refreshStepUpStatus:async()=>{const e=await n.getStepUpStatus();return c({stepUpStatus:e.error?null:e.data}),e},verifyStepUpWithPasskey:async()=>{const e=await n.verifyStepUpWithPasskey();return e.error||c({stepUpStatus:e.data}),e},verifyStepUpWithPasskeyPrf:async e=>{const t=await n.verifyStepUpWithPasskeyPrf(e);return t.error||c({stepUpStatus:{fresh:t.data.fresh,method:t.data.method,verifiedAt:t.data.verifiedAt,expiresAt:t.data.expiresAt,maxAgeSeconds:t.data.maxAgeSeconds}}),t},verifyStepUpWithTotp:async e=>{const t=await n.verifyStepUpWithTotp(e);return t.error||c({stepUpStatus:t.data}),t},hasRole:e=>l.user?.roles?.includes(e),hasScopedRole:e=>l.user?Q(l.user.roles,e):void 0,markSignedIn:d},destroy:()=>{a=!0,i.clear()}}}({apiHost:r,detectPreviousSignIn:n}),[r,n]),a=u(i.subscribe,i.getState,i.getState);m(()=>{i.actions.refreshSession()},[i]);const s=d(()=>({...a,...i.actions,apiHost:r,magicLinkRedirectUri:o}),[a,i,r,o]);return e(re.Provider,{value:s,children:t})},ie=()=>{const{apiHost:e,magicLinkRedirectUri:t}=ne();return d(()=>Y({apiHost:e,magicLinkRedirectUri:t}),[e,t])},ae=["passkey","magic_link"],se=()=>{const e=ie(),[t,r]=g(null),[n,o]=g(!0);return m(()=>{let t=!0;return(async()=>{try{const{data:n,error:o}=await e.getPublicSystemConfig();t&&!o&&n?.loginMethods?.length&&r(n.loginMethods)}catch{}finally{t&&o(!1)}})(),()=>{t=!1}},[e]),{loginMethods:t,loading:n}},le=e=>Boolean(e?.some(e=>"passkey"!==e)),ce=e=>{if("string"!=typeof e)return!1;const t=e.trim();if(t.length<3||t.length>320)return!1;const r=t.split("@");if(2!==r.length)return!1;const[n,o]=r;return!(!/^[A-Za-z0-9._%+-]+$/.test(n)||n.startsWith(".")||n.endsWith(".")||n.includes(".."))&&!(!/^[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/.test(o)||o.startsWith("-")||o.endsWith("-")||o.startsWith(".")||o.endsWith(".")||o.includes(".."))},de=e=>{const t=P(e);return t?.isValid()||!1};async function ue(){return async function(){if("undefined"==typeof window)return!1;if(window.PublicKeyCredential&&"function"==typeof window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable)try{return await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()}catch{return console.error("Error checking passkey support."),!1}return!1}()}const me=()=>{const[e,t]=g(!1),[r,n]=g(!0);return m(()=>{let e=!0;return(async()=>{try{const r=await ue();e&&t(r)}catch{e&&t(!1)}finally{e&&n(!1)}})(),()=>{e=!1}},[]),{passkeySupported:e,loading:r}},_e="/login",ge="/passkey-login",pe="/verify-phone-otp",fe="/verify-email-otp",he="/verify-magiclink",ye="/oauth/callback",be="/register-passkey",ve="/magic-link-sent";function ke(e,t){void 0===t&&(t={});var r=t.insertAt;if(e&&"undefined"!=typeof document){var n=document.head||document.getElementsByTagName("head")[0],o=document.createElement("style");o.type="text/css","top"===r&&n.firstChild?n.insertBefore(o,n.firstChild):n.appendChild(o),o.styleSheet?o.styleSheet.cssText=e:o.appendChild(document.createTextNode(e))}}var we="login-module__container___YggDx",xe="login-module__card___Ub-Py",Pe="login-module__heading___neEqb",Se="login-module__inputGroup___P6qgS",Oe="login-module__label___mx5vu",Ne="login-module__input___U07lW",Te="login-module__helperText___kDQ4-",Ce="login-module__error___40zxh",Ie="login-module__button___0kFBs",Ae="login-module__submitHint___qOS5Z",Me="login-module__toggle___1JiG1",Ee="login-module__fallbackCard___HdIk-",ze="login-module__fallbackHeader___-il2v",Fe="login-module__fallbackDescription___ea5EB",Ue="login-module__fallbackActions___AD3ni",Le="login-module__fallbackActionButton___lFgWf",Re="login-module__actionTitle___ycrMp",qe="login-module__actionSubtext___opaIV",Be="login-module__linkButton___5lsoe";ke(".login-module__container___YggDx{align-items:center;color:var(--seamless-text,#fff);display:flex;justify-content:center}.login-module__card___Ub-Py{background-color:var(--seamless-surface,#1f2937);border-radius:.5rem;box-shadow:0 4px 6px var(--seamless-shadow,rgba(0,0,0,.1));max-width:28rem;padding:2rem;width:100%}.login-module__heading___neEqb{color:var(--seamless-text,#fff);font-size:1.5rem;font-weight:700;margin-bottom:1.5rem;text-align:center}.login-module__message___qSejT{color:var(--seamless-success,#68d391);text-align:center}.login-module__inputGroup___P6qgS{margin-bottom:1rem}.login-module__label___mx5vu{color:var(--seamless-text-muted,#d1d5db);display:block}.login-module__input___U07lW{background-color:var(--seamless-surface-raised,#4b5563);border:1px solid var(--seamless-border,#d1d5db);border-radius:.375rem;margin-top:.25rem;padding:.5rem;width:100%}.login-module__input___U07lW:focus{box-shadow:0 0 0 2px var(--seamless-accent,#3b82f6);outline:none}.login-module__helperText___kDQ4-{color:var(--seamless-text-muted,#9ca3af);font-size:.75rem;margin-top:.5rem}.login-module__error___40zxh{color:var(--seamless-danger,#f87171);font-size:.875rem}.login-module__button___0kFBs{background-color:var(--seamless-accent,#2563eb);border-radius:.375rem;color:var(--seamless-accent-contrast,#fff);cursor:pointer;margin-top:1rem;padding:.5rem;transition:background-color .3s,opacity .3s;width:100%}.login-module__button___0kFBs:hover:not(:disabled){background-color:var(--seamless-accent-hover,#1d4ed8)}.login-module__button___0kFBs:disabled{cursor:not-allowed;opacity:.6}.login-module__submitHint___qOS5Z{color:var(--seamless-text-muted,#9ca3af);font-size:.75rem;margin-top:.5rem;text-align:center}.login-module__toggle___1JiG1{background:none;border:none;color:var(--seamless-accent-soft,#60a5fa);cursor:pointer;font-size:.875rem;margin-top:1.5rem;text-align:center;width:100%}.login-module__underline___gs-RS{text-decoration:underline}.login-module__phoneRow___oeNtq{display:grid;gap:.5rem;grid-template-columns:1fr 3fr;margin-top:.25rem}.login-module__phoneInput___2MPQC,.login-module__select___rRV1O{background-color:var(--seamless-surface-raised,#4b5563);border:1px solid var(--seamless-border,#d1d5db);border-radius:.375rem;padding:.5rem;width:100%}@media (max-width:500px){.login-module__phoneRow___oeNtq{grid-template-columns:1fr}}.login-module__fallbackCard___HdIk-{background:var(--seamless-surface-raised,hsla(0,0%,100%,.04));border:1px solid var(--seamless-border,hsla(0,0%,100%,.08));border-radius:12px;display:flex;flex-direction:column;gap:14px;margin-top:18px;padding:18px}.login-module__fallbackHeader___-il2v{color:var(--seamless-text,#f3f4f6);font-size:.95rem;font-weight:600}.login-module__fallbackDescription___ea5EB{color:var(--seamless-text-muted,#9ca3af);font-size:.85rem;line-height:1.4}.login-module__fallbackActions___AD3ni{display:flex;flex-direction:column;gap:10px}.login-module__fallbackActionButton___lFgWf{background:var(--seamless-surface,rgba(0,0,0,.25));border:1px solid var(--seamless-border,hsla(0,0%,100%,.08));border-radius:8px;cursor:pointer;display:flex;flex-direction:column;padding:12px 14px;text-align:left;transition:all .15s ease}.login-module__fallbackActionButton___lFgWf:hover{background:var(--seamless-surface-hover,hsla(0,0%,100%,.06));border-color:var(--seamless-border,hsla(0,0%,100%,.15))}.login-module__actionTitle___ycrMp{color:var(--seamless-text,#f9fafb);font-size:.9rem;font-weight:500}.login-module__actionSubtext___opaIV{color:var(--seamless-text-muted,#9ca3af);font-size:.75rem;margin-top:2px}.login-module__linkButton___5lsoe{background:none;border:none;color:var(--seamless-accent-soft,#60a5fa);cursor:pointer;font-size:.8rem;margin-top:6px}.login-module__linkButton___5lsoe:hover{text-decoration:underline}");const je=({identifier:r,onMagicLink:n,onEmailOtp:o,onPhoneOtp:i,onPasskeyRetry:a,loginMethods:s})=>{const l=new Set(s??["passkey","magic_link","phone_otp"]),c=l.has("magic_link")&&ce(r),d=l.has("email_otp")&&Boolean(o)&&ce(r),u=l.has("phone_otp")&&de(r),m=l.has("passkey")&&Boolean(a);return c||d||u||m?t("div",{className:Ee,children:[e("div",{className:ze,children:"Choose a sign-in method"}),e("p",{className:Fe,children:"Choose another secure sign-in method."}),t("div",{className:Ue,children:[c&&t("button",{type:"button",className:Le,onClick:n,children:[e("span",{className:Re,children:"Email Magic Link"}),e("span",{className:qe,children:"Send a secure sign-in link to your email"})]}),d&&t("button",{type:"button",className:Le,onClick:o,children:[e("span",{className:Re,children:"Email Code"}),e("span",{className:qe,children:"Receive a one-time code by email"})]}),u&&t("button",{type:"button",className:Le,onClick:i,children:[e("span",{className:Re,children:"Text Message Code"}),e("span",{className:qe,children:"Receive a one-time code via SMS"})]})]}),m&&e("button",{type:"button",className:Be,onClick:a,children:"Try passkey anyway"})]}):null},Je="seamless:oauth:provider",Ge=()=>{const{listOAuthProviders:r,startOAuthLogin:n}=ne(),o=h("/oauth/callback"),[i,a]=g([]),[s,l]=g("");if(m(()=>{let e=!0;return r().then(({data:t})=>{e&&a(t?.providers??[])}),()=>{e=!1}},[r]),0===i.length)return null;return t("div",{className:Ue,children:[i.map(r=>e("button",{type:"button",className:Le,onClick:()=>(async e=>{l(""),sessionStorage.setItem(Je,e);const{data:t,error:r}=await n({providerId:e,redirectUri:new URL(o,window.location.origin).toString()});r?l("Could not start sign-in with this provider."):window.location.assign(t.authorizationUrl)})(r.id),children:t("span",{className:Re,children:["Continue with ",r.name]})},r.id)),s&&e("p",{className:Ce,children:s})]})},We=()=>{const n=y(),{hasSignedInBefore:o,login:i,handlePasskeyLogin:a}=ne(),s=ie(),{passkeySupported:l}=me(),{loginMethods:c}=se(),[d,u]=g(""),[_,p]=g(""),[f,h]=g("register"),[b,v]=g(""),[k,w]=g(""),[x,P]=g(""),[S,O]=g(!1),[N,T]=g(null);m(()=>{o&&h("login")},[o]);const C=()=>"login"===f?ce(d)||de(d):ce(_);return e("div",{className:we,children:t("div",{className:xe,children:[e("h2",{className:Pe,children:"login"===f?"Sign In":"Create Account"}),t(r,{children:[t("form",{onSubmit:async e=>{e.preventDefault(),v(""),O(!1);try{if("login"===f){const{data:e,error:t}=await i(d,l);if(t)return void v("Failed to start sign-in. Please try again.");const r=e?.loginMethods?.length?e.loginMethods:c??ae;if(T(r),l&&r.includes("passkey")){const{error:e}=await a();return e?(O(!0),void v("Passkey sign-in could not be completed. Choose another sign-in method.")):void n("/")}return void O(!0)}"register"===f&&await(async()=>{v("");const{data:e,error:t}=await s.register({email:_});t?v("Failed to register. Please try again."):"Success"===e.message?n(fe):v("An unexpected error occurred. Try again. If the problem persists, contact support.")})()}catch{console.error("Failed to continue sign-in."),v("Failed to continue sign-in. Please try again.")}},children:["login"===f&&t("div",{className:Se,children:[e("label",{htmlFor:"identifier",className:Oe,children:"Email Address / Phone Number"}),e("input",{id:"identifier",type:"text",value:d,onChange:e=>{const t=e.target.value;u(t)},autoComplete:"off",placeholder:"Email or Phone Number",className:Ne,onBlur:()=>{if(d){const e=ce(d)||de(d);P(e?"":"Please enter a valid email or phone number")}},required:!0}),e("p",{className:Te,children:"Phone numbers must include a country code e.g. +1"}),S&&e(je,{identifier:d,onMagicLink:async()=>{const{error:e}=await s.requestMagicLink();e?v("Failed to send magic link."):n(ve,{state:{identifier:d}})},onEmailOtp:async()=>{const{error:e}=await s.requestLoginEmailOtp();e?v("Failed to send email code."):n(fe,{state:{flow:"login"}})},onPhoneOtp:async()=>{const{error:e}=await s.requestLoginPhoneOtp();e?v("Failed to send OTP."):n(pe,{state:{flow:"login"}})},onPasskeyRetry:l?a:void 0,loginMethods:N}),x&&e("p",{className:Ce,children:x})]}),"register"===f&&t("div",{className:Se,children:[e("label",{htmlFor:"email",className:Oe,children:"Email Address"}),e("input",{id:"email",type:"email",value:_,onChange:e=>{const t=e.target.value;p(t)},autoComplete:"off",className:Ne,onBlur:()=>{if(_){const e=ce(_);w(e?"":"Please enter a valid email")}},required:!0}),k&&e("p",{className:Ce,children:k})]}),e("button",{type:"submit",className:Ie,disabled:!C(),"aria-describedby":"seamless-submit-hint",children:"login"===f?"Login":"Register"}),e("p",{id:"seamless-submit-hint",role:"status",className:Ae,children:"login"===f?d?C()?"Ready to continue.":"This does not look like a complete email or phone number yet.":"Enter your email or phone number to continue.":_?C()?"Ready to continue.":"This does not look like a complete email address yet.":"Enter your email address to continue."}),b&&e("p",{className:Ce,children:b}),e("button",{type:"button",onClick:()=>h("login"===f?"register":"login"),className:Me,children:"login"===f?"Don't have an account? Create one":"Already have an account? Sign in"})]}),e(Ge,{})]})]})})};var $e={container:"passKeyLogin-module__container___IUKxF",card:"passKeyLogin-module__card___2sKM1",title:"passKeyLogin-module__title___HZxvc",button:"passKeyLogin-module__button___FvR4I"};ke(".passKeyLogin-module__container___IUKxF{align-items:center;color:var(--seamless-text,#fff);display:flex;justify-content:center;padding:1rem}.passKeyLogin-module__card___2sKM1{background-color:var(--seamless-surface,#1f2937);border-radius:1rem;box-shadow:0 4px 20px var(--seamless-shadow,rgba(0,0,0,.4));display:flex;flex-direction:column;gap:1.5rem;max-width:400px;padding:2rem;width:100%}.passKeyLogin-module__title___HZxvc{color:var(--seamless-text,#fff);font-size:1.5rem;font-weight:600;text-align:center}.passKeyLogin-module__button___FvR4I{background-color:var(--seamless-accent,#2563eb);border:1px solid var(--seamless-accent,#3b82f6);border-radius:.5rem;color:var(--seamless-accent-contrast,#fff);cursor:pointer;font-weight:500;padding:.75rem 1rem;text-align:center;transition:background-color .2s ease;width:100%}.passKeyLogin-module__button___FvR4I:hover{background-color:var(--seamless-accent-hover,#1d4ed8)}@media (max-width:480px){.passKeyLogin-module__card___2sKM1{padding:1.25rem}.passKeyLogin-module__button___FvR4I{font-size:1rem}}");const De=()=>{const r=y(),{handlePasskeyLogin:n}=ne(),[o,i]=g("");return e("div",{className:$e.container,children:t("div",{className:$e.card,children:[e("h2",{className:$e.title,children:"Login with Passkey"}),e("button",{onClick:async()=>{i("");const{error:e}=await n();e?i("Passkey sign-in could not be completed. Try another sign-in method."):r("/")},className:$e.button,children:"Use Passkey"}),o&&e("p",{className:$e.error,children:o})]})})};var Ve="registerPasskey-module__container___jqEh8",He="registerPasskey-module__card___MJAyC",Ke="registerPasskey-module__loading___Q4i1x",Ye="registerPasskey-module__spinner___V1nhd",Ze="registerPasskey-module__supported___ihTih",Qe="registerPasskey-module__title___vn2Qu",Xe="registerPasskey-module__description___AWmO-",et="registerPasskey-module__button___D3b4B",tt="registerPasskey-module__message___RiOw-",rt="registerPasskey-module__success___HP5aX",nt="registerPasskey-module__error___hGiqE",ot="registerPasskey-module__skip___1hDyU",it="registerPasskey-module__secondary___bnOB4";ke(".registerPasskey-module__container___jqEh8{align-items:center;color:var(--seamless-text,#fff);display:flex;justify-content:center;padding:1rem}.registerPasskey-module__card___MJAyC{background-color:var(--seamless-surface,#1f2937);border-radius:1rem;box-shadow:0 4px 20px var(--seamless-shadow,rgba(0,0,0,.4));max-width:400px;padding:1.5rem;width:100%}.registerPasskey-module__loading___Q4i1x{align-items:center;background-color:var(--seamless-surface,#f9fafb);border-radius:.75rem;box-shadow:0 2px 8px var(--seamless-shadow,rgba(0,0,0,.1));color:var(--seamless-text,#374151);display:flex;font-weight:500;gap:.75rem;justify-content:center;padding:1.5rem}.registerPasskey-module__spinner___V1nhd{animation:registerPasskey-module__spin___oYcsC 1s linear infinite;border-bottom:2px solid var(--seamless-accent,#3b82f6);border-left:2px solid transparent;border-radius:9999px;border-right:2px solid transparent;border-top:2px solid var(--seamless-accent,#3b82f6);height:1.5rem;width:1.5rem}@keyframes registerPasskey-module__spin___oYcsC{to{transform:rotate(1turn)}}.registerPasskey-module__supported___ihTih{background-color:var(--seamless-surface,#ecfdf5);border-radius:.75rem;box-shadow:0 2px 12px var(--seamless-shadow,rgba(0,0,0,.1));color:var(--seamless-text,#065f46);padding:1.25rem}.registerPasskey-module__title___vn2Qu{font-size:1.125rem;font-weight:600;margin-bottom:.5rem}.registerPasskey-module__description___AWmO-{margin-bottom:1rem}.registerPasskey-module__button___D3b4B{background-color:var(--seamless-accent,#059669);border:none;border-radius:.5rem;color:var(--seamless-accent-contrast,#fff);cursor:pointer;font-weight:500;padding:.75rem 1rem;transition:background-color .2s ease;width:100%}.registerPasskey-module__button___D3b4B:hover{background-color:var(--seamless-accent-hover,#047857)}.registerPasskey-module__message___RiOw-{font-size:.875rem;margin-top:1rem}.registerPasskey-module__success___HP5aX{color:var(--seamless-success,#6ee7b7)}.registerPasskey-module__error___hGiqE{color:var(--seamless-danger,#f87171)}.registerPasskey-module__skip___1hDyU{background:none;border:none;color:var(--seamless-accent-soft,#60a5fa);cursor:pointer;font-size:.875rem;margin-top:1.5rem;text-align:center;width:100%}.registerPasskey-module__skip___1hDyU:disabled{cursor:default;opacity:.6}.registerPasskey-module__secondary___bnOB4{background:none;border:1px solid var(--seamless-accent,#059669);border-radius:.5rem;color:var(--seamless-accent,#059669);cursor:pointer;font-weight:500;margin-top:.75rem;padding:.75rem 1rem;transition:background-color .2s ease;width:100%}.registerPasskey-module__secondary___bnOB4:hover:not(:disabled){background-color:var(--seamless-accent-muted,rgba(5,150,105,.1))}.registerPasskey-module__secondary___bnOB4:disabled{cursor:default;opacity:.6}");const at={attachment_not_allowed:"This application does not accept that kind of authenticator. Try the other option.",synced_passkey_not_allowed:"This passkey syncs to a password manager, and this application requires one that stays on a single device, such as a security key.",authenticator_not_allowed:"This application does not accept this authenticator.",prf_required:"This authenticator does not support a feature this application requires."};const st=()=>{const{refreshSession:r}=ne(),n=ie(),{passkeySupported:o,loading:i}=me(),{loginMethods:a,loading:s}=se(),l=y(),[c,d]=g("idle"),[u,m]=g(""),_=le(a),p=async()=>{await r(),l("/")},f=async e=>{const{platform:t,browser:o,deviceInfo:i}=function(){const e=navigator.userAgent.toLowerCase();let t="unknown",r="unknown";return/iphone|ipad|ipod/.test(e)?t="ios":/android/.test(e)?t="android":/mac os/.test(e)?t="mac":/windows/.test(e)?t="windows":/linux/.test(e)&&(t="linux"),navigator.brave?r="brave":/edg\/|edgios/.test(e)?r="edge":/opr\/|opt\/|opera/.test(e)?r="opera":/vivaldi/.test(e)?r="vivaldi":/samsungbrowser/.test(e)?r="samsung":/firefox|fxios/.test(e)?r="firefox":/chrome|crios/.test(e)?r="chrome":/safari/.test(e)&&(r="safari"),{platform:t,browser:r,deviceInfo:`${t} • ${r}`}}(),a={friendlyName:i,platform:t,browser:o,deviceInfo:i};d("loading");try{const{error:t}=await n.registerPasskey({metadata:a,attachment:e});if(t)throw t;await r(),d("success"),m("Passkey registered successfully."),l("/")}catch(e){console.error("Passkey registration failed."),d("error"),m(function(e){const t=A(e);return t?at[t]:void 0}(e)??(M(e)?"Your session expired before the passkey was saved. Sign in again to add one.":"Error registering passkey."))}};return e("div",{className:Ve,children:e("div",{className:He,children:t("div",i||s?{className:Ke,children:[e("div",{className:Ye}),e("span",{children:"Checking for Passkey Support..."})]}:o?{className:Ze,children:[e("h2",{className:Qe,children:"Secure Your Account with a Passkey"}),e("p",{className:Xe,children:"Your device supports passkeys! Register one to skip passwords forever."}),e("button",{onClick:()=>f(),disabled:"loading"===c,className:et,children:"loading"===c?"Registering...":"Register Passkey"}),e("button",{type:"button",onClick:()=>f("cross-platform"),disabled:"loading"===c,className:it,children:"Use a security key instead"}),u&&e("p",{className:`${tt} ${"success"===c?rt:nt}`,children:u}),_&&e("button",{type:"button",onClick:p,disabled:"loading"===c,className:ot,children:"Skip for now"})]}:{className:Ze,children:[e("h2",{className:Qe,children:"Passkeys are not available here"}),e("p",{className:Xe,children:_?"This device does not support passkeys. You can continue without one and add a passkey later from a device that does.":"This device does not support passkeys, and this application requires one to sign in. Try again from a device or browser that supports them."}),_&&e("button",{type:"button",onClick:p,className:et,children:"Continue"})]})})})};var lt="verifyOTP-module__container___wgoDE",ct="verifyOTP-module__card___3cs2p",dt="verifyOTP-module__title___fZ-1d",ut="verifyOTP-module__subtitle___dGSu3",mt="verifyOTP-module__error___GEtlG",_t="verifyOTP-module__success___46TjG",gt="verifyOTP-module__form___jiGG-",pt="verifyOTP-module__label___Zq5Mi",ft="verifyOTP-module__timer___smq-a",ht="verifyOTP-module__resend___PlmkG",yt="verifyOTP-module__button___8pIuO",bt="verifyOTP-module__toggle___wbzVa";ke(".verifyOTP-module__container___wgoDE{align-items:center;color:var(--seamless-text,#fff);display:flex;justify-content:center;padding:1rem}.verifyOTP-module__card___3cs2p{background-color:var(--seamless-surface,#1f2937);border-radius:.5rem;box-shadow:0 0 10px var(--seamless-shadow,rgba(0,0,0,.3));max-width:28rem;padding:1.5rem;width:100%}.verifyOTP-module__title___fZ-1d{color:var(--seamless-text,#fff);font-size:1.5rem;font-weight:700;margin-bottom:1rem;text-align:center}.verifyOTP-module__subtitle___dGSu3{color:var(--seamless-text-muted,#9ca3af);font-size:.875rem;margin-bottom:1.5rem;text-align:center}.verifyOTP-module__error___GEtlG{color:var(--seamless-danger,#f87171);margin-bottom:1rem;text-align:center}.verifyOTP-module__success___46TjG{color:var(--seamless-success,#34d399);margin-bottom:.75rem;text-align:center}.verifyOTP-module__form___jiGG-{display:flex;flex-direction:column;gap:1rem}.verifyOTP-module__label___Zq5Mi{color:var(--seamless-text-muted,#d1d5db);display:block;font-size:.875rem;margin-bottom:.25rem}.verifyOTP-module__timer___smq-a{color:var(--seamless-warning,#facc15);font-size:.875rem}.verifyOTP-module__input___yqIXM{background-color:var(--seamless-surface-raised,#374151);border:1px solid var(--seamless-border,#4b5563);border-radius:.375rem;color:var(--seamless-text,#fff);outline:none;padding:.5rem;width:100%}.verifyOTP-module__input___yqIXM:focus{box-shadow:0 0 0 2px var(--seamless-accent,#3b82f6)}.verifyOTP-module__resend___PlmkG{background:none;border:none;color:var(--seamless-accent-soft,#60a5fa);cursor:pointer;font-size:.75rem;margin-top:.25rem;text-decoration:underline}.verifyOTP-module__resend___PlmkG:hover{color:var(--seamless-accent,#3b82f6)}.verifyOTP-module__button___8pIuO{background-color:var(--seamless-accent,#2563eb);border:none;border-radius:.375rem;color:var(--seamless-accent-contrast,#fff);cursor:pointer;padding:.5rem;transition:background-color .2s;width:100%}.verifyOTP-module__button___8pIuO:hover{background-color:var(--seamless-accent-hover,#1d4ed8)}.verifyOTP-module__toggle___wbzVa{background:none;border:none;color:var(--seamless-accent-soft,#60a5fa);cursor:pointer;font-size:.875rem;margin-top:1.5rem;text-align:center;width:100%}");var vt="otpInput-module__otpContainer___MUjby",kt="otpInput-module__otpInput___3YMVm";ke(".otpInput-module__otpContainer___MUjby{display:flex;gap:8px;justify-content:center;margin-top:8px}.otpInput-module__otpInput___3YMVm{background:var(--seamless-surface-raised,#374151);border:1px solid var(--seamless-border,#4b5563);border-radius:8px;color:var(--seamless-text,#fff);font-size:1.2rem;height:48px;text-align:center;transition:border-color .15s ease,box-shadow .15s ease;width:42px}.otpInput-module__otpInput___3YMVm:focus{border-color:var(--seamless-accent,#60a5fa);box-shadow:0 0 0 2px var(--seamless-accent,rgba(96,165,250,.25));outline:none}");const wt=({length:t=6,value:r,inputMode:n="numeric",onChange:o,name:i="otp"})=>{const a=p([]),s=r.split("").concat(Array(t).fill("")).slice(0,t),l=(e,t)=>{var r;if(t.length>1)c(e,t);else if(!t||(r=t,"numeric"===n?/^\d$/.test(r):/^[a-zA-Z]$/.test(r))){const r=[...s];r[e]=t;const n=r.join("");o(n),t&&a.current[e+1]&&a.current[e+1]?.focus()}},c=(e,r)=>{let i=r;i="numeric"===n?r.replace(/\D/g,""):r.replace(/[^a-zA-Z]/g,"");const l=i.slice(0,t).split(""),c=[...s];l.forEach((r,n)=>{e+n<t&&(c[e+n]=r)}),o(c.join("")),a.current[Math.min(e+l.length,t-1)]?.focus()};return e("div",{className:vt,role:"group","aria-label":"One-time password input",onPaste:e=>{e.preventDefault(),c(0,e.clipboardData.getData("text"))},children:s.map((r,c)=>e("input",{ref:e=>a.current[c]=e,type:"text",inputMode:"numeric"===n?"numeric":"text",autoComplete:"one-time-code",name:`${i}-${c}`,maxLength:1,value:r,className:kt,onChange:e=>l(c,e.target.value),onKeyDown:e=>((e,r)=>{if("Backspace"===r.key){r.preventDefault();const t=[...s];s[e]?(t[e]="",o(t.join(""))):e>0&&(t[e-1]="",o(t.join("")),a.current[e-1]?.focus())}"ArrowLeft"===r.key&&e>0&&a.current[e-1]?.focus(),"ArrowRight"===r.key&&e<t-1&&a.current[e+1]?.focus()})(c,e),"aria-label":`Digit ${c+1}`},c))})},xt=()=>{const r=y(),n=b(),{refreshSession:o}=ne(),i="login"===n.state?.flow,[a,s]=g(""),[l,c]=g(null),[d,u]=g(""),[_,p]=g(!1),[f,h]=g(""),[v,k]=g(300),w=ie(),x=async()=>{if(p(!0),!l){const{error:e}=i?await w.verifyLoginPhoneOtp(a):await w.verifyPhoneOtp(a);if(e)u("Verification failed.");else{if(i)return await o(),void r("/");c(!0)}}p(!1)},P=async()=>{h(""),await(async()=>{u("");const{error:e}=i?await w.requestLoginPhoneOtp():await w.requestPhoneOtp();e?u("Failed to send SMS code. If this persists, refresh the page and try again."):h("Verification SMS has been resent.")})(),h("Verification SMS has been resent.")};return m(()=>{const e=setInterval(()=>{k(e=>e>0?e-1:0)},1e3);return()=>clearInterval(e)},[]),m(()=>{l&&!i&&(async()=>{const{error:e}=await w.requestEmailOtp();e?u("Failed to send Email code. If this persists, refresh the page and try registering again."):r(fe)})()},[l,i,r,w]),e("div",{className:lt,children:t("div",{className:ct,children:[e("h2",{className:dt,children:"Verify Your Phone Number"}),e("p",{className:ut,children:"Enter the code sent to your phone number."}),d&&e("p",{className:mt,children:d}),f&&e("p",{className:_t,children:f}),t("form",{onSubmit:async e=>{if(e.preventDefault(),u(""),6===a.length){p(!0);try{await x()}catch{u("Unexpected error occurred.")}finally{p(!1)}}else u("Please enter a valid code.")},className:gt,children:[t("div",{children:[t("label",{htmlFor:"phoneCode",className:pt,children:["Phone Verification Code ",(O=l,!0===O?e("span",{className:"text-green-400 ml-2",children:"✅"}):!1===O?e("span",{className:"text-red-400 ml-2",children:"❌"}):null)," -"," ",t("span",{className:ft,children:["Code expires in: ",(S=v,`${Math.floor(S/60).toString().padStart(2,"0")}:${(S%60).toString().padStart(2,"0")}`)]})]}),e(wt,{length:6,value:a,onChange:s}),e("button",{type:"button",onClick:()=>P(),className:ht,children:"Resend code to phone"})]}),e("button",{type:"submit",className:yt,children:_?"Verifying...":"Verify & Continue"}),e("button",{type:"button",onClick:()=>r("/login"),className:bt,children:"Back to login"})]})]})});var S,O},Pt=()=>{const r=y(),n=b(),{refreshSession:o}=ne(),i=ie(),{passkeySupported:a}=me(),s="login"===n.state?.flow,[l,c]=g(!1),[d,u]=g(""),[_,p]=g(300),[f,h]=g(""),[v,k]=g("");return m(()=>{const e=setInterval(()=>{p(e=>e>0?e-1:0)},1e3);return()=>clearInterval(e)},[]),e("div",{className:lt,children:t("div",{className:ct,children:[e("h2",{className:dt,children:"Verify Your Email"}),e("p",{className:ut,children:"We sent you a verification email. Enter the code below."}),f&&e("p",{className:mt,children:f}),v&&e("p",{className:_t,children:v}),t("form",{onSubmit:async e=>{if(e.preventDefault(),h(""),6===d.length){c(!0);try{const{error:e}=s?await i.verifyLoginEmailOtp(d):await i.verifyEmailOtp(d);if(e)return void h("Verification failed.");if(s)return await o(),void r("/");a?r(be):(await o(),r("/"))}catch{console.error("Email OTP verification failed."),h("Verification failed.")}finally{c(!1)}}else h("Please enter a valid code.")},className:gt,children:[t("div",{children:[t("label",{htmlFor:"emailCode",className:pt,children:["Email Verification Code",t("span",{className:ft,children:[" ","— Code expires in ",(w=_,`${Math.floor(w/60).toString().padStart(2,"0")}:${(w%60).toString().padStart(2,"0")}`)]})]}),e(wt,{length:6,value:d,onChange:u,inputMode:"text"}),e("button",{type:"button",onClick:async()=>{h(""),k("");const{error:e}=s?await i.requestLoginEmailOtp():await i.requestEmailOtp();e?h("Failed to send Email code. If this persists, refresh the page and try again."):k("Verification email has been resent.")},className:ht,children:"Resend code to email"})]}),e("button",{type:"submit",className:yt,disabled:l,children:l?"Verifying...":"Verify & Continue"}),e("button",{type:"button",onClick:()=>r("/login"),className:bt,children:"Back to login"})]})]})});var w};var St={container:"verifyMagiclink-module__container___qrSXf",card:"verifyMagiclink-module__card___niKgr",title:"verifyMagiclink-module__title___SumxD",verificationContent:"verifyMagiclink-module__verificationContent___NdsS0",spinner:"verifyMagiclink-module__spinner___lnW1k",successIcon:"verifyMagiclink-module__successIcon___XjFEM",checkIcon:"verifyMagiclink-module__checkIcon___I0JOP",successText:"verifyMagiclink-module__successText___lO5Qq"};ke(".verifyMagiclink-module__container___qrSXf{align-items:center;color:var(--seamless-text,#fff);display:flex;justify-content:center;padding:1rem}.verifyMagiclink-module__card___niKgr{background-color:var(--seamless-surface,#1f2937);border-radius:.5rem;box-shadow:0 0 10px var(--seamless-shadow,rgba(0,0,0,.3));max-width:28rem;padding:1.5rem;width:100%}.verifyMagiclink-module__title___SumxD{color:var(--seamless-text,#fff);font-size:1.5rem;font-weight:700;margin-bottom:1rem;text-align:center}.verifyMagiclink-module__verificationContent___NdsS0{align-items:center;display:flex;flex-direction:column;gap:14px;margin:20px 0}.verifyMagiclink-module__spinner___lnW1k{animation:verifyMagiclink-module__spin___2lGzj .9s linear infinite;border:3px solid var(--seamless-border,hsla(0,0%,100%,.15));border-radius:50%;border-top-color:var(--seamless-accent,#60a5fa);height:28px;width:28px}.verifyMagiclink-module__successIcon___XjFEM{align-items:center;background:rgba(16,185,129,.15);border-radius:50%;display:flex;height:36px;justify-content:center;width:36px}.verifyMagiclink-module__checkIcon___I0JOP{animation:verifyMagiclink-module__checkPop___-3qJu .25s ease-out;color:var(--seamless-success,#34d399);height:22px;width:22px}.verifyMagiclink-module__successText___lO5Qq{color:var(--seamless-success,#34d399);font-size:.9rem}@keyframes verifyMagiclink-module__checkPop___-3qJu{0%{opacity:0;transform:scale(.6)}80%{transform:scale(1.15)}to{opacity:1;transform:scale(1)}}@keyframes verifyMagiclink-module__spin___2lGzj{to{transform:rotate(1turn)}}");const Ot=()=>{const[r]=v(),n=y(),o=r.get("token"),[i,a]=g(""),[s,l]=g(""),c=ie(),{refreshSession:d}=ne();return m(()=>{let e=!0,t=null,r=null;return(async()=>{if(!o)return e&&a("Missing token for verification."),void console.error("No magic-link token found.");const{error:i}=await c.verifyMagicLink(o);if(i)return console.error("Failed to verify token"),void(e&&a("Failed to verify token"));await d(),e&&(t=new BroadcastChannel("seamless-auth"),t.postMessage({type:"MAGIC_LINK_AUTH_SUCCESS"}),l("You have been verified on the device and browser that initiated this request"),r=setTimeout(()=>{e&&n("/")},900))})(),()=>{e=!1,t?.close(),r&&clearTimeout(r)}},[o,c,n,d]),e("div",{className:St.container,children:t("div",{className:St.card,children:[e("h1",{className:St.title,children:"Verifying your login"}),t("div",{className:St.verificationContent,children:[!s&&!i&&e("div",{className:St.spinner}),s&&e("div",{className:St.successIcon,children:e("svg",{viewBox:"0 0 24 24",className:St.checkIcon,fill:"none",stroke:"currentColor",strokeWidth:"2",children:e("path",{d:"M5 13l4 4L19 7"})})}),!s&&!i&&e("p",{className:St.helperText,children:"Please wait while we securely verify your sign-in link."}),s&&e("p",{className:St.successText,children:"Login verified. Redirecting…"}),i&&e("p",{className:St.error,children:i})]})]})})};const Nt={oauth_missing_email:"Your provider account did not share an email address. Add an email to that account and make it visible, then try again.",oauth_email_not_verified:"The email address on your provider account is not verified. Verify it with your provider, then try again.",oauth_missing_subject:"Your provider did not return a usable account identifier. Try again, or sign in with a different method."},Tt=()=>{const{finishOAuthLogin:n}=ne(),o=y(),[i]=v(),[a,s]=g(""),l=p(!1);return m(()=>{if(l.current)return;l.current=!0;const e=i.get("code"),t=i.get("state"),r=sessionStorage.getItem(Je);e&&t&&r?n({providerId:r,code:e,state:t}).then(({data:e,error:t})=>{if(t){const e=T(t);return void s(e?Nt[e]:"We could not complete sign-in. Please try again.")}sessionStorage.removeItem(Je),o(function(e){if(!e)return null;try{const t=new URL(e,window.location.origin);return t.origin!==window.location.origin?null:`${t.pathname}${t.search}${t.hash}`}catch{return null}}(e?.returnTo)??"/")}):s("This sign-in link is missing required information.")},[n,o,i]),t("div",{className:St.container,children:[e("h2",{className:St.title,children:a?"Sign-in failed":"Completing sign-in..."}),a&&t(r,{children:[e("p",{children:a}),e("button",{type:"button",onClick:()=>o("/login"),children:"Back to login"})]})]})};var Ct="magiclink-module__container___45zcA",It="magiclink-module__card___Au9oN",At="magiclink-module__iconWrapper___k5ndr",Mt="magiclink-module__mailIcon___Pwb7q",Et="magiclink-module__iconRing___bdLvf",zt="magiclink-module__heading___b9cre",Ft="magiclink-module__message___kiVFj",Ut="magiclink-module__identifier___JtcFM",Lt="magiclink-module__helperText___7Mlfh",Rt="magiclink-module__actions___64I-3",qt="magiclink-module__secondaryButton___BntAI",Bt="magiclink-module__cooldown___Ib4rm",jt="magiclink-module__linkButton___maJiv";ke(".magiclink-module__container___45zcA{align-items:center;color:var(--seamless-text,#fff);display:flex;justify-content:center}.magiclink-module__card___Au9oN{background-color:var(--seamless-surface,#1f2937);border-radius:.75rem;box-shadow:0 10px 25px var(--seamless-shadow,rgba(0,0,0,.25));max-width:420px;padding:2.5rem 2rem;text-align:center;width:100%}.magiclink-module__iconWrapper___k5ndr{display:flex;justify-content:center;margin-bottom:1.25rem;position:relative}.magiclink-module__mailIcon___Pwb7q{color:var(--seamless-accent-soft,#a5b4fc);height:38px;width:38px;z-index:2}.magiclink-module__iconRing___bdLvf{animation:magiclink-module__pulse___YGm-I 2s infinite;background:rgba(99,102,241,.15);border-radius:50%;height:72px;position:absolute;width:72px}@keyframes magiclink-module__pulse___YGm-I{0%{opacity:.8;transform:scale(.6)}70%{opacity:0;transform:scale(1.4)}to{opacity:0}}.magiclink-module__heading___b9cre{font-size:1.55rem;font-weight:600;margin-bottom:.75rem}.magiclink-module__message___kiVFj{color:var(--seamless-text-muted,#d1d5db);font-size:.95rem;line-height:1.4;margin-bottom:1rem}.magiclink-module__identifier___JtcFM{background:var(--seamless-surface-raised,hsla(0,0%,100%,.05));border-radius:6px;display:inline-block;font-size:1rem;font-weight:600;margin-bottom:1rem;padding:.45rem .6rem}.magiclink-module__helperText___7Mlfh{color:var(--seamless-text-muted,#9ca3af);font-size:.85rem;line-height:1.4;margin-bottom:.6rem}.magiclink-module__actions___64I-3{display:flex;flex-direction:column;gap:.6rem;margin-top:1.6rem}.magiclink-module__secondaryButton___BntAI{background:var(--seamless-surface,#111827);border:1px solid var(--seamless-border,#374151);border-radius:8px;color:var(--seamless-text,#fff);cursor:pointer;font-weight:500;padding:.65rem}.magiclink-module__secondaryButton___BntAI:hover:not(:disabled){background:var(--seamless-surface-hover,#1f2937)}.magiclink-module__secondaryButton___BntAI:disabled{cursor:not-allowed;opacity:.5}.magiclink-module__cooldown___Ib4rm{color:var(--seamless-text-muted,#9ca3af);font-size:.8rem}.magiclink-module__linkButton___maJiv{background:none;border:none;color:var(--seamless-accent-soft,#a5b4fc);cursor:pointer;font-size:.85rem;margin-top:.4rem}.magiclink-module__linkButton___maJiv:hover{text-decoration:underline}");const Jt=()=>{const{refreshSession:r}=ne(),n=y(),o=b(),i=ie(),a=o.state?.identifier,[s,l]=g(30);m(()=>{const e=setInterval(()=>{l(e=>e>0?e-1:0)},1e3);return()=>clearInterval(e)},[]);const c=f(async()=>{const{data:e,error:t}=await i.checkMagicLink();return!t&&"Success"===e?.message},[i]);return m(()=>{const e=new BroadcastChannel("seamless-auth");return e.onmessage=async e=>{"MAGIC_LINK_AUTH_SUCCESS"===e.data?.type&&await c()&&(await r(),n("/"))},()=>{e.close()}},[c,n,r]),m(()=>{const e=setInterval(async()=>{try{await c()&&(await r(),n("/"))}catch{}},5e3);return()=>clearInterval(e)},[c,r,n]),e("div",{className:Ct,children:t("div",{className:It,children:[t("div",{className:At,children:[e("div",{className:Et}),t("svg",{className:Mt,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.8",children:[e("path",{d:"M4 6h16v12H4z"}),e("path",{d:"m4 6 8 6 8-6"})]})]}),e("h2",{className:zt,children:"Check your email"}),e("p",{className:Ft,children:"If an account exists for this address, we sent a secure sign-in link."}),a&&e("div",{className:Ut,children:a}),e("p",{className:Lt,children:"Open the email and click the link to finish signing in."}),e("p",{className:Lt,children:"Didn’t receive anything? Check your spam folder or try creating a new account."}),t("div",{className:Rt,children:[e("button",{className:qt,disabled:s>0,onClick:async()=>{s>0||(await i.requestMagicLink(),l(30))},children:"Resend link"}),s>0&&t("div",{className:Bt,children:["Available in ",s,"s"]}),e("button",{className:jt,onClick:()=>n("/login"),children:"Change email or phone"})]})]})})},Gt=()=>t(k,{children:[e(w,{path:_e,element:e(We,{})}),e(w,{path:ge,element:e(De,{})}),e(w,{path:pe,element:e(xt,{})}),e(w,{path:fe,element:e(Pt,{})}),e(w,{path:he,element:e(Ot,{})}),e(w,{path:ye,element:e(Tt,{})}),e(w,{path:be,element:e(st,{})}),e(w,{path:ve,element:e(Jt,{})}),e(w,{path:"*",element:e(x,{to:_e,replace:!0})})]});export{oe as AuthProvider,Gt as AuthRoutes,S as SeamlessAuthError,Y as createSeamlessAuthClient,B as encodePrfSalt,$ as extractPasskeyPrfResult,T as getOAuthErrorCode,A as getPasskeyPolicyErrorCode,E as getWebAuthnErrorDetail,le as hasNonPasskeyLoginMethod,Q as hasScopedRole,G as isPasskeyPrfSupported,M as isUnauthenticated,Z as roleGrantsAccess,ne as useAuth,ie as useAuthClient,se as useLoginMethods,me as usePasskeySupport};
1
+ 'use client';
2
+ import{u as e}from"./chunks/shared-BzrBuR0z.js";export{A as AuthProvider,h as hasNonPasskeyLoginMethod,a as useAuthClient,b as useLoginMethods,c as usePasskeySupport}from"./chunks/shared-BzrBuR0z.js";export{SeamlessAuthError,createSeamlessAuthClient,encodePrfSalt,extractPasskeyPrfResult,getOAuthErrorCode,getPasskeyPolicyErrorCode,getWebAuthnErrorDetail,hasScopedRole,isPasskeyPrfSupported,isUnauthenticated,roleGrantsAccess}from"@seamless-auth/client";import"react";import"react/jsx-runtime";const s=()=>e().client.authorizedFetch;export{e as useAuth,s as useAuthorizedFetch};
2
3
  //# sourceMappingURL=index.js.map