@seamless-auth/react 0.11.0 → 0.13.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 +82 -0
- package/README.md +83 -2
- package/dist/AuthProvider.d.ts +25 -4
- package/dist/components/AuthFallbackOptions.d.ts +1 -1
- package/dist/hooks/useAuthClient.d.ts +6 -1
- package/dist/hooks/useAuthorizedFetch.d.ts +6 -0
- package/dist/hooks/useLoginMethods.d.ts +1 -1
- package/dist/index.d.ts +11 -9
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +7 -63
- package/LICENSE.md +0 -26
- package/dist/client/createSeamlessAuthClient.d.ts +0 -189
- package/dist/client/errors.d.ts +0 -80
- package/dist/client/result.d.ts +0 -36
- package/dist/client/webauthnPrf.d.ts +0 -27
- package/dist/client/webauthnSupport.d.ts +0 -10
- package/dist/fetchWithAuth.d.ts +0 -5
- package/dist/scopedRoles.d.ts +0 -2
- package/dist/session/createAuthSession.d.ts +0 -60
- package/dist/session/storage.d.ts +0 -17
- package/dist/types.d.ts +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,87 @@
|
|
|
1
1
|
# @seamless-auth/react
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 93a35b7: Put the platform behind ports, and add a bearer transport with token custody to the client core.
|
|
8
|
+
|
|
9
|
+
The client spoke one contract: cookies to a server adapter at `/auth`, with the browser's WebAuthn
|
|
10
|
+
API and `window.location` reached directly. A native binding has none of those, so the pieces that
|
|
11
|
+
differ by platform are now ports a binding supplies, with the browser implementations as the
|
|
12
|
+
defaults. A web application configures nothing and behaves exactly as before.
|
|
13
|
+
- `transport` on `createSeamlessAuthClient` (and `AuthProvider`): cookie transport is unchanged;
|
|
14
|
+
`{ mode: 'bearer', tokenStorage }` makes the client hold the auth API's own tokens. Every request
|
|
15
|
+
carries `x-seamless-auth-transport: bearer`; pre-auth routes carry the ephemeral token that
|
|
16
|
+
`/login` or `/registration/register` returned, kept in memory only; signed-in routes carry the
|
|
17
|
+
access token; the pair a sign-in returns is written through a `TokenStoragePort`; a 401 on a
|
|
18
|
+
signed-in route triggers one `POST /refresh` and one retry, with at most one refresh in flight
|
|
19
|
+
because the auth API revokes the chain on a replayed refresh token. Which routes take which
|
|
20
|
+
token is one table, mirroring the server adapter's, rather than an annotation at each of the
|
|
21
|
+
forty call sites.
|
|
22
|
+
- `PasskeyPort` (`isSupported`, `isPlatformAuthenticatorAvailable`, `create`, `get`) replaces the
|
|
23
|
+
direct SimpleWebAuthn calls at the four ceremony sites. `createBrowserPasskeyPort()` is the
|
|
24
|
+
default. A port reports an authenticator refusal with `PasskeyCeremonyError`, or any error with a
|
|
25
|
+
DOMException `name` and a string `code`, which the client turns into the same result a browser
|
|
26
|
+
failure gave. The PRF helpers no longer depend on SimpleWebAuthn at runtime.
|
|
27
|
+
- `OAuthRedirectPort` replaces `window.location.assign` in the built-in provider buttons.
|
|
28
|
+
`createBrowserOAuthRedirect()` is the default; a port that receives the callback itself resolves
|
|
29
|
+
with `code` and `state`, and the buttons finish the login on the spot.
|
|
30
|
+
- `TokenStoragePort` with `createMemoryTokenStorage()`.
|
|
31
|
+
- `client.authorizedFetch(input, init)` and `useAuthorizedFetch()`: a fetch for the application's
|
|
32
|
+
own API that carries the session the way the transport does (cookies, or the access token with
|
|
33
|
+
one refresh-and-retry on a 401). A path resolves on `apiHost`. This is what a native app uses to
|
|
34
|
+
call routes behind `requireAuth`.
|
|
35
|
+
- `createAuthSession` accepts the client options (or a ready-made `client`) and exposes the client
|
|
36
|
+
it drives as `session.client`. `useAuthClient()` returns that same instance rather than building a
|
|
37
|
+
second one, which bearer transport needs: the client holds the sign-in in flight.
|
|
38
|
+
- `AuthProvider` gains `transport` and `ports` props and exposes `client` and `ports` on the
|
|
39
|
+
context. `usePasskeySupport()` reads the passkey port.
|
|
40
|
+
|
|
41
|
+
Tracks fells-code/seamless-auth-react#124, #125, #127 and #128.
|
|
42
|
+
|
|
43
|
+
### Patch Changes
|
|
44
|
+
|
|
45
|
+
- f8f0dfe: Split the framework-agnostic core into `@seamless-auth/client`, and make this repository an npm workspace that publishes both packages.
|
|
46
|
+
|
|
47
|
+
`@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).
|
|
48
|
+
|
|
49
|
+
`@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.
|
|
50
|
+
|
|
51
|
+
Packaging only: no runtime behaviour changes in either package.
|
|
52
|
+
|
|
53
|
+
- Updated dependencies [93a35b7]
|
|
54
|
+
- Updated dependencies [f8f0dfe]
|
|
55
|
+
- @seamless-auth/client@0.1.0
|
|
56
|
+
|
|
57
|
+
## 0.12.0
|
|
58
|
+
|
|
59
|
+
### Minor Changes
|
|
60
|
+
|
|
61
|
+
- f59be34: Add a passkey while signed in, and say what a 401 at enrollment means.
|
|
62
|
+
|
|
63
|
+
`registerPasskey` is now on `useAuth()` and on the framework-agnostic session actions,
|
|
64
|
+
not only on the client. The bundled UI already told users they could "add a passkey later
|
|
65
|
+
from a device that does", and nothing in the package implemented that: `credentials` could
|
|
66
|
+
be listed and deleted but never added. The context version refreshes the session
|
|
67
|
+
afterwards, so a settings screen renders the new passkey without a reload.
|
|
68
|
+
|
|
69
|
+
Enrollment now requires a signed-in session, which is a coordinated change with
|
|
70
|
+
`seamless-auth-api` and the server adapters. Upgrade all three together: the auth API and
|
|
71
|
+
the adapter have no safe release order between them, and enrollment answers `401` until
|
|
72
|
+
both land. The signup flow already satisfies it, because
|
|
73
|
+
verifying the email OTP issues a session before the passkey screen is reached, so nothing
|
|
74
|
+
in the bundled views moves. An application that called `registerPasskey()` before
|
|
75
|
+
verifying an address has to move that call after it.
|
|
76
|
+
|
|
77
|
+
A 401 from enrollment now reads "Your session expired before the passkey was saved" rather
|
|
78
|
+
than the generic "Error registering passkey." It is the session rather than anything about
|
|
79
|
+
the authenticator, and the generic wording invited the user to retry with the same expired
|
|
80
|
+
one. `isUnauthenticated(error)` is exported for callers rendering their own screens.
|
|
81
|
+
|
|
82
|
+
Corrects a stale README example that still passed a `token` to `registerPasskey`, a field
|
|
83
|
+
removed when the wire contract moved to `@seamless-auth/types`.
|
|
84
|
+
|
|
3
85
|
## 0.11.0
|
|
4
86
|
|
|
5
87
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -197,6 +197,47 @@ result.
|
|
|
197
197
|
Custom UIs get the same default through `useAuthClient()`, and can still override a single send with
|
|
198
198
|
`requestMagicLink(uri)`.
|
|
199
199
|
|
|
200
|
+
### Platform ports and transport
|
|
201
|
+
|
|
202
|
+
Everything that differs between a browser and another platform sits behind a port on
|
|
203
|
+
`AuthProvider`. A web application configures nothing; the browser implementations are the
|
|
204
|
+
defaults. A binding for another platform (React Native is the first) supplies its own:
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
<AuthProvider
|
|
208
|
+
apiHost={apiHost}
|
|
209
|
+
transport={{ mode: 'bearer', tokenStorage: keystoreTokenStorage }}
|
|
210
|
+
ports={{ passkeys: nativePasskeys, oauthRedirect: inAppBrowserRedirect }}
|
|
211
|
+
>
|
|
212
|
+
<App />
|
|
213
|
+
</AuthProvider>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
- `transport` selects how the session travels. Cookie transport, the default, is the browser
|
|
217
|
+
contract: the server adapter holds the tokens in `httpOnly` cookies and the client never sees
|
|
218
|
+
them. Bearer transport is the native contract: the client holds the auth API's own tokens,
|
|
219
|
+
presents the one a route needs in `Authorization`, stores the pair a sign-in returns through
|
|
220
|
+
`tokenStorage`, and refreshes once through `POST /auth/refresh` when a request answers 401. At
|
|
221
|
+
most one refresh is in flight at a time, because the auth API treats a replayed refresh token as
|
|
222
|
+
theft and revokes the chain.
|
|
223
|
+
- `ports.passkeys` runs the WebAuthn ceremonies (`create`, `get`, and the two support checks).
|
|
224
|
+
`usePasskeySupport()` and every passkey flow go through it.
|
|
225
|
+
- `ports.oauthRedirect` opens the provider. The browser navigates away and the callback route
|
|
226
|
+
finishes the login; a port that receives the callback itself (an in-app browser session)
|
|
227
|
+
resolves with the `code` and `state`, and the built-in buttons finish the login on the spot.
|
|
228
|
+
|
|
229
|
+
`useAuthClient()` returns the same client instance the provider's session drives. In bearer
|
|
230
|
+
transport that matters: the client holds the sign-in in flight, and a second client would not see it.
|
|
231
|
+
|
|
232
|
+
`useAuthorizedFetch()` (or `client.authorizedFetch`) is a fetch for your own API that carries the
|
|
233
|
+
session the way the transport does: `credentials: 'include'` in cookie transport, the access token
|
|
234
|
+
with one refresh-and-retry on a 401 in bearer transport. A path resolves on `apiHost`.
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
const authorizedFetch = useAuthorizedFetch();
|
|
238
|
+
const plan = await authorizedFetch('/api/plan/mine').then(r => r.json());
|
|
239
|
+
```
|
|
240
|
+
|
|
200
241
|
### Scoped roles
|
|
201
242
|
|
|
202
243
|
`hasRole(role)` remains an exact role check. Use `hasScopedRole(role)` for colon-separated scoped
|
|
@@ -612,7 +653,7 @@ on it:
|
|
|
612
653
|
```ts
|
|
613
654
|
import { getPasskeyPolicyErrorCode } from '@seamless-auth/react';
|
|
614
655
|
|
|
615
|
-
const { error } = await authClient.registerPasskey({
|
|
656
|
+
const { error } = await authClient.registerPasskey({ metadata });
|
|
616
657
|
|
|
617
658
|
switch (getPasskeyPolicyErrorCode(error)) {
|
|
618
659
|
case 'attachment_not_allowed':
|
|
@@ -760,6 +801,10 @@ function CustomRegistration() {
|
|
|
760
801
|
}
|
|
761
802
|
```
|
|
762
803
|
|
|
804
|
+
Enrolment takes the signed-in session, so it comes after the step that establishes one.
|
|
805
|
+
Verifying the email OTP signs the user in, which is why the bundled flow offers a passkey
|
|
806
|
+
on the screen after it rather than before.
|
|
807
|
+
|
|
763
808
|
To offer a passkey right after registering, call `registerPasskey()` before `refreshSession()`:
|
|
764
809
|
|
|
765
810
|
```ts
|
|
@@ -775,6 +820,40 @@ if (!error) {
|
|
|
775
820
|
}
|
|
776
821
|
```
|
|
777
822
|
|
|
823
|
+
### Adding a passkey from a settings screen
|
|
824
|
+
|
|
825
|
+
The same call adds a passkey to an account that already has one, or gives one to a user
|
|
826
|
+
who declined at signup. Use it from `useAuth()` rather than the client directly: that
|
|
827
|
+
version refreshes the session afterwards, so `credentials` includes the new passkey
|
|
828
|
+
without a reload.
|
|
829
|
+
|
|
830
|
+
```tsx
|
|
831
|
+
function AddPasskey() {
|
|
832
|
+
const { registerPasskey, credentials } = useAuth();
|
|
833
|
+
|
|
834
|
+
const add = async () => {
|
|
835
|
+
const { error } = await registerPasskey({
|
|
836
|
+
friendlyName: 'My laptop',
|
|
837
|
+
platform: 'macOS',
|
|
838
|
+
browser: 'Chrome',
|
|
839
|
+
deviceInfo: navigator.userAgent,
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
if (error) {
|
|
843
|
+
// A 401 means the session expired rather than anything about the
|
|
844
|
+
// authenticator. `isUnauthenticated(error)` tells the two apart.
|
|
845
|
+
}
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
return (
|
|
849
|
+
<>
|
|
850
|
+
<p>{credentials.length} passkeys</p>
|
|
851
|
+
<button onClick={add}>Add a passkey</button>
|
|
852
|
+
</>
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
```
|
|
856
|
+
|
|
778
857
|
### OTP and magic-link continuation
|
|
779
858
|
|
|
780
859
|
> **The request helpers take no identifier.** `requestMagicLink()`, `requestLoginEmailOtp()`, and
|
|
@@ -1021,8 +1100,10 @@ This package assumes a Seamless Auth-compatible backend with the auth adapter mo
|
|
|
1021
1100
|
|
|
1022
1101
|
- Requests target `${apiHost}/auth/...`
|
|
1023
1102
|
- `apiHost` may be provided with or without a trailing slash
|
|
1024
|
-
-
|
|
1103
|
+
- In cookie transport (the default) requests are sent with `credentials: 'include'`; in bearer
|
|
1104
|
+
transport they carry `x-seamless-auth-transport: bearer` and an `Authorization` header instead
|
|
1025
1105
|
- `AuthProvider` validates the current session by calling `/users/me` on load
|
|
1106
|
+
- Bearer transport additionally uses `POST /refresh`
|
|
1026
1107
|
|
|
1027
1108
|
The built-in flows assume compatible endpoints for:
|
|
1028
1109
|
|
package/dist/AuthProvider.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { CurrentUserResult, FinishOAuthLoginInput, FinishOAuthLoginResult, LoginStartResult, MessageResult, OAuthProvidersResult, OrganizationSwitchResult, PasskeyLoginData, StartOAuthLoginInput, StartOAuthLoginResult, StepUpPrfData, StepUpStatus } from '
|
|
2
|
-
import type { SeamlessAuthResult } from '
|
|
3
|
-
import { PasskeyPrfInput } from '
|
|
4
|
-
import {
|
|
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 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;
|
|
@@ -28,11 +29,24 @@ export interface AuthContextType {
|
|
|
28
29
|
deleteCredential: (credentialId: string) => Promise<SeamlessAuthResult<MessageResult>>;
|
|
29
30
|
login: (identifier: string, passkeyAvailable: boolean) => Promise<SeamlessAuthResult<LoginStartResult>>;
|
|
30
31
|
handlePasskeyLogin: () => Promise<SeamlessAuthResult<PasskeyLoginData>>;
|
|
32
|
+
registerPasskey: (input: PasskeyMetadata | RegisterPasskeyOptions) => Promise<SeamlessAuthResult<PasskeyRegistrationData>>;
|
|
31
33
|
refreshStepUpStatus: () => Promise<SeamlessAuthResult<StepUpStatus>>;
|
|
32
34
|
verifyStepUpWithPasskey: () => Promise<SeamlessAuthResult<StepUpStatus>>;
|
|
33
35
|
verifyStepUpWithPasskeyPrf: (input: PasskeyPrfInput) => Promise<SeamlessAuthResult<StepUpPrfData>>;
|
|
34
36
|
verifyStepUpWithTotp: (code: string) => Promise<SeamlessAuthResult<StepUpStatus>>;
|
|
35
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;
|
|
36
50
|
}
|
|
37
51
|
/**
|
|
38
52
|
* Returns the current context
|
|
@@ -48,6 +62,13 @@ interface AuthProviderProps {
|
|
|
48
62
|
* send and a resend read it from here, so the two cannot drift apart.
|
|
49
63
|
*/
|
|
50
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>;
|
|
51
72
|
}
|
|
52
73
|
export declare const AuthProvider: React.FC<AuthProviderProps>;
|
|
53
74
|
export {};
|
|
@@ -1 +1,6 @@
|
|
|
1
|
-
|
|
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>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { AuthContextType, AuthProvider, useAuth } from './AuthProvider';
|
|
1
|
+
import { AuthContextType, AuthPorts, AuthProvider, useAuth } from './AuthProvider';
|
|
2
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 '
|
|
4
|
-
import { getOAuthErrorCode, getPasskeyPolicyErrorCode, getWebAuthnErrorDetail, OAuthErrorCode, PasskeyPolicyErrorCode, SeamlessAuthError, WebAuthnErrorDetail } from '
|
|
5
|
-
import type { SeamlessAuthResult } from '
|
|
6
|
-
import { encodePrfSalt, extractPasskeyPrfResult, isPasskeyPrfSupported, PasskeyPrfInput, PasskeyPrfResult } from '
|
|
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 '@seamless-auth/client';
|
|
4
|
+
import { getOAuthErrorCode, getPasskeyPolicyErrorCode, getWebAuthnErrorDetail, isUnauthenticated, OAuthErrorCode, PasskeyPolicyErrorCode, SeamlessAuthError, WebAuthnErrorDetail } from '@seamless-auth/client';
|
|
5
|
+
import type { SeamlessAuthResult } from '@seamless-auth/client';
|
|
6
|
+
import { encodePrfSalt, extractPasskeyPrfResult, isPasskeyPrfSupported, PasskeyPrfInput, PasskeyPrfResult } from '@seamless-auth/client';
|
|
7
7
|
import { useAuthClient } from './hooks/useAuthClient';
|
|
8
|
+
import { useAuthorizedFetch } from './hooks/useAuthorizedFetch';
|
|
8
9
|
import { hasNonPasskeyLoginMethod, useLoginMethods } from './hooks/useLoginMethods';
|
|
9
10
|
import { usePasskeySupport } from './hooks/usePasskeySupport';
|
|
10
|
-
import { hasScopedRole, roleGrantsAccess } from '
|
|
11
|
-
import { Credential, Organization, OrganizationMembership, User } from '
|
|
12
|
-
|
|
13
|
-
export
|
|
11
|
+
import { hasScopedRole, roleGrantsAccess } from '@seamless-auth/client';
|
|
12
|
+
import { Credential, Organization, OrganizationMembership, User } from '@seamless-auth/client';
|
|
13
|
+
import type { OAuthRedirectOutcome, OAuthRedirectPort, PasskeyPort, StoredTokens, TokenStoragePort, TransportOptions } from '@seamless-auth/client';
|
|
14
|
+
export { AuthProvider, AuthRoutes, createSeamlessAuthClient, encodePrfSalt, extractPasskeyPrfResult, getOAuthErrorCode, getPasskeyPolicyErrorCode, getWebAuthnErrorDetail, hasNonPasskeyLoginMethod, hasScopedRole, isPasskeyPrfSupported, isUnauthenticated, roleGrantsAccess, SeamlessAuthError, useAuth, useAuthClient, useAuthorizedFetch, useLoginMethods, usePasskeySupport, };
|
|
15
|
+
export type { CredentialUpdateResult, AuthContextType, AuthPorts, Credential, CreateOrganizationInput, CurrentUserResult, FinishOAuthLoginInput, FinishOAuthLoginResult, 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, 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,2 @@
|
|
|
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 S from"libphonenumber-js";class P 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 P))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 P&&"object"==typeof e.body&&null!==e.body)return I(e.body)??I(e.body.details)}function M(e){if(!(e instanceof P)||("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 E(e,t){let r;try{r=await e.json()}catch{r=void 0}return new P(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 z(e){try{return await e.json()}catch{return}}async function F(e,t){let r;try{r=await e}catch{return{data:null,error:new P(t,0)}}return r.ok?{data:await z(r),error:null}:{data:null,error:await E(r,t)}}function U(e){return{data:e,error:null}}function L(e,t=0,r,n){return{data:null,error:new P(e,t,r,n)}}function R(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 q(e){return"string"==typeof e?e:o(R(e))}function B(e){return"string"==typeof e?i(e):e}function j(e){return{...e.credentialId?{credentialId:e.credentialId}:{},prf:{salt:q(e.salt),...e.secondSalt?{secondSalt:q(e.secondSalt)}:{}}}}async function J(){return!!n()&&("undefined"==typeof window||!1!==window.isSecureContext)&&void 0!==globalThis.PublicKeyCredential&&"undefined"!=typeof navigator&&"function"==typeof navigator.credentials?.get}function G(e){const t=e,r=t.extensions?.prf?.eval;return r?{...t,extensions:{...t.extensions,prf:{...t.extensions?.prf,eval:{...r,...r.first?{first:B(r.first)}:{},...r.second?{second:B(r.second)}:{}}}}}:e}function W(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(R(n))}}function $(e){const t=e.clientExtensionResults;return t?.prf?.results?{...e,clientExtensionResults:{...e.clientExtensionResults,prf:{...t.prf,results:void 0}}}:e}function D(e,t){const r=e.method===t?e.method:null;return"Success"===e.message&&!0===e.fresh&&null!==r?U({fresh:!0,method:r,verifiedAt:e.verifiedAt??null,expiresAt:e.expiresAt??null,maxAgeSeconds:e.maxAgeSeconds??0}):L(e.message??"Step-up authentication failed.")}function V(e){return e?{method:"POST",body:JSON.stringify(j(e))}:{method:"POST"}}function H(e,t,r){const n=L(t,0,void 0,r),o=M(n.error);return console.error(e,o?.name??"Unknown error."),n}const K=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:()=>F(t("users/me",{method:"GET"}),"Failed to load the current user."),login:e=>F(t("/login",{method:"POST",body:JSON.stringify(e)}),"Failed to start sign-in."),loginWithPasskey:async e=>{const r=await F(t("/webAuthn/login/start",{...V(e?.prf)}),"Failed to start passkey login.");if(r.error)return r;let n,o;try{const e=await a({optionsJSON:G(r.data)});n=W(e),o=$(e)}catch(e){return H("Passkey login error.","Passkey login failed.",e)}const i=await F(t("/webAuthn/login/finish",{method:"POST",body:JSON.stringify({assertionResponse:o})}),"Failed to verify passkey.");return i.error?i:"Success"!==i.data?.message?L(i.data?.message??"Passkey login failed."):U(n?{prf:n}:{})},logout:(e={})=>F(t("all_sessions"===e.scope?"/logout/all":"/logout",{method:"DELETE"}),"Failed to sign out."),logoutAllSessions:()=>F(t("/logout/all",{method:"DELETE"}),"Failed to sign out of all sessions."),deleteUser:()=>F(t("/users/delete",{method:"DELETE"}),"Failed to delete the account."),register:e=>F(t("/registration/register",{method:"POST",body:JSON.stringify({email:e.email,...e.phone?{phone:e.phone}:{}})}),"Failed to register."),requestPhoneOtp:()=>F(t("/otp/generate-phone-otp",{method:"POST",body:JSON.stringify({})}),"Failed to send the SMS code."),verifyPhoneOtp:e=>F(t("/otp/verify-phone-otp",{method:"POST",body:JSON.stringify({verificationToken:e})}),"Failed to verify the SMS code."),requestLoginPhoneOtp:()=>F(t("/otp/generate-login-phone-otp",{method:"POST",body:JSON.stringify({})}),"Failed to send the SMS code."),verifyLoginPhoneOtp:e=>F(t("/otp/verify-login-phone-otp",{method:"POST",body:JSON.stringify({verificationToken:e})}),"Failed to verify the SMS code."),requestEmailOtp:()=>F(t("/otp/generate-email-otp",{method:"POST",body:JSON.stringify({})}),"Failed to send the email code."),verifyEmailOtp:e=>F(t("/otp/verify-email-otp",{method:"POST",body:JSON.stringify({verificationToken:e})}),"Failed to verify the email code."),requestLoginEmailOtp:()=>F(t("/otp/generate-login-email-otp",{method:"POST",body:JSON.stringify({})}),"Failed to send the email code."),verifyLoginEmailOtp:e=>F(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 F(t("/magic-link",{method:"POST",body:JSON.stringify(n?{redirectUri:n}:{})}),"Failed to send the magic link.")},checkMagicLink:()=>F(t("/magic-link/check",{method:"GET"}),"Failed to check the magic link."),verifyMagicLink:e=>F(t(`/magic-link/verify/${encodeURIComponent(e)}`,{method:"GET"}),"Failed to verify the magic link."),listOAuthProviders:()=>F(t("/oauth/providers",{method:"GET"}),"Failed to list OAuth providers."),getPublicSystemConfig:()=>F(t("/system-config/public",{method:"GET"}),"Failed to read the public system configuration."),startOAuthLogin:e=>F(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=>F(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 F(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?L(e.name,0,void 0,e):H("Passkey registration error.","Passkey registration failed.",e)}const i=function(e){const t=e.clientExtensionResults;return!0===t?.prf?.enabled}(o),a=await F(t("/webAuthn/register/finish",{method:"POST",body:JSON.stringify({attestationResponse:o,metadata:{...r.metadata,prfCapable:i}})}),"Verification failed.");return a.error?a:U({credentialId:o.id,prfCapable:i})},isPasskeyPrfSupported:J,getStepUpStatus:()=>F(t("/step-up/status",{method:"GET"}),"Failed to load step-up status."),verifyStepUpWithPasskey:async()=>{const e=await F(t("/step-up/webauthn/start",{method:"POST"}),"Failed to start step-up authentication.");if(e.error)return e;let r;try{r=$(await a({optionsJSON:G(e.data)}))}catch(e){return H("Step-up authentication error.","Step-up authentication failed.",e)}const n=await F(t("/step-up/webauthn/finish",{method:"POST",body:JSON.stringify({assertionResponse:r})}),"Failed to verify step-up authentication.");return n.error?n:D(n.data??{},"webauthn")},verifyStepUpWithPasskeyPrf:async e=>{const r=await F(t("/step-up/webauthn/start",{...V(e)}),"Failed to start step-up authentication.");if(r.error)return r;let n,o;try{const e=await a({optionsJSON:G(r.data)});n=W(e),o=$(e)}catch(e){return H("Step-up authentication error.","Step-up authentication failed.",e)}if(!n)return L("Passkey did not return PRF output.");const i=await F(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=D(i.data??{},"webauthn");return s.error?s:U({...s.data,credentialId:n.credentialId,prf:n})},getTotpStatus:()=>F(t("/totp/status",{method:"GET"}),"Failed to load authenticator status."),startTotpEnrollment:()=>F(t("/totp/enroll/start",{method:"POST"}),"Failed to start authenticator enrollment."),verifyTotpEnrollment:e=>F(t("/totp/enroll/verify",{method:"POST",body:JSON.stringify({code:e})}),"Failed to verify the authenticator code."),disableTotp:e=>F(t("/totp/disable",{method:"POST",body:JSON.stringify({code:e})}),"Failed to disable the authenticator."),verifyStepUpWithTotp:async e=>{const r=await F(t("/totp/verify-mfa",{method:"POST",body:JSON.stringify({code:e})}),"Failed to verify step-up authentication.");return r.error?r:D(r.data??{},"totp")},updateCredential:e=>F(t("users/credentials",{method:"POST",body:JSON.stringify(e)}),"Failed to update credential."),deleteCredential:e=>F(t("users/credentials",{method:"DELETE",body:JSON.stringify({id:e})}),"Failed to delete credential."),listOrganizations:()=>F(t("/organizations",{method:"GET"}),"Failed to list organizations."),createOrganization:e=>F(t("/organizations",{method:"POST",body:JSON.stringify(e)}),"Failed to create organization."),getOrganization:e=>F(t(`/organizations/${encodeURIComponent(e)}`,{method:"GET"}),"Failed to load organization."),updateOrganization:(e,r)=>F(t(`/organizations/${encodeURIComponent(e)}`,{method:"PATCH",body:JSON.stringify(r)}),"Failed to update organization."),switchOrganization:e=>F(t(`/organizations/${encodeURIComponent(e)}/switch`,{method:"POST"}),"Failed to switch organization."),listOrganizationMembers:e=>F(t(`/organizations/${encodeURIComponent(e)}/members`,{method:"GET"}),"Failed to list organization members."),addOrganizationMember:(e,r)=>F(t(`/organizations/${encodeURIComponent(e)}/members`,{method:"POST",body:JSON.stringify(r)}),"Failed to add organization member."),updateOrganizationMember:(e,r,n)=>F(t(`/organizations/${encodeURIComponent(e)}/members/${encodeURIComponent(r)}`,{method:"PATCH",body:JSON.stringify(n)}),"Failed to update organization member."),removeOrganizationMember:(e,r)=>F(t(`/organizations/${encodeURIComponent(e)}/members/${encodeURIComponent(r)}`,{method:"DELETE"}),"Failed to remove organization member.")}};function Y(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 Z(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=>Y(t,e)))}function Q(){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 X="seamlessauth_seen",ee={user:null,credentials:[],organizations:[],activeOrganization:null,stepUpStatus:null,isAuthenticated:!1};const te=c(void 0),re=()=>{const e=_(te);if(!e)throw new Error("useAuth must be used within an AuthProvider");return e},ne=({children:t,apiHost:r,autoDetectPreviousSignin:n=!0,magicLinkRedirectUri:o})=>{const i=d(()=>function(e){const{apiHost:t,detectPreviousSignIn:r=!0}=e,n=K({apiHost:t}),o=e.storage??Q(),i=new Set;let a=!1,s=0,l={...ee,loading:!0,hasSignedInBefore:r&&"true"===o.get(X)};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(X,"true"),r&&c({hasSignedInBefore:!0})}function u(){c(ee)}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()),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?Z(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(te.Provider,{value:s,children:t})},oe=()=>{const{apiHost:e,magicLinkRedirectUri:t}=re();return d(()=>K({apiHost:e,magicLinkRedirectUri:t}),[e,t])},ie=["passkey","magic_link"],ae=()=>{const e=oe(),[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}},se=e=>Boolean(e?.some(e=>"passkey"!==e)),le=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(".."))},ce=e=>{const t=S(e);return t?.isValid()||!1};async function de(){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 ue=()=>{const[e,t]=g(!1),[r,n]=g(!0);return m(()=>{let e=!0;return(async()=>{try{const r=await de();e&&t(r)}catch{e&&t(!1)}finally{e&&n(!1)}})(),()=>{e=!1}},[]),{passkeySupported:e,loading:r}},me="/login",_e="/passkey-login",ge="/verify-phone-otp",pe="/verify-email-otp",fe="/verify-magiclink",he="/oauth/callback",ye="/register-passkey",be="/magic-link-sent";function ve(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 ke="login-module__container___YggDx",we="login-module__card___Ub-Py",xe="login-module__heading___neEqb",Se="login-module__inputGroup___P6qgS",Pe="login-module__label___mx5vu",Oe="login-module__input___U07lW",Ne="login-module__helperText___kDQ4-",Te="login-module__error___40zxh",Ce="login-module__button___0kFBs",Ie="login-module__submitHint___qOS5Z",Ae="login-module__toggle___1JiG1",Me="login-module__fallbackCard___HdIk-",Ee="login-module__fallbackHeader___-il2v",ze="login-module__fallbackDescription___ea5EB",Fe="login-module__fallbackActions___AD3ni",Ue="login-module__fallbackActionButton___lFgWf",Le="login-module__actionTitle___ycrMp",Re="login-module__actionSubtext___opaIV",qe="login-module__linkButton___5lsoe";ve(".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 Be=({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")&&le(r),d=l.has("email_otp")&&Boolean(o)&&le(r),u=l.has("phone_otp")&&ce(r),m=l.has("passkey")&&Boolean(a);return c||d||u||m?t("div",{className:Me,children:[e("div",{className:Ee,children:"Choose a sign-in method"}),e("p",{className:ze,children:"Choose another secure sign-in method."}),t("div",{className:Fe,children:[c&&t("button",{type:"button",className:Ue,onClick:n,children:[e("span",{className:Le,children:"Email Magic Link"}),e("span",{className:Re,children:"Send a secure sign-in link to your email"})]}),d&&t("button",{type:"button",className:Ue,onClick:o,children:[e("span",{className:Le,children:"Email Code"}),e("span",{className:Re,children:"Receive a one-time code by email"})]}),u&&t("button",{type:"button",className:Ue,onClick:i,children:[e("span",{className:Le,children:"Text Message Code"}),e("span",{className:Re,children:"Receive a one-time code via SMS"})]})]}),m&&e("button",{type:"button",className:qe,onClick:a,children:"Try passkey anyway"})]}):null},je="seamless:oauth:provider",Je=()=>{const{listOAuthProviders:r,startOAuthLogin:n}=re(),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:Fe,children:[i.map(r=>e("button",{type:"button",className:Ue,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:Le,children:["Continue with ",r.name]})},r.id)),s&&e("p",{className:Te,children:s})]})},Ge=()=>{const n=y(),{hasSignedInBefore:o,login:i,handlePasskeyLogin:a}=re(),s=oe(),{passkeySupported:l}=ue(),{loginMethods:c}=ae(),[d,u]=g(""),[_,p]=g(""),[f,h]=g("register"),[b,v]=g(""),[k,w]=g(""),[x,S]=g(""),[P,O]=g(!1),[N,T]=g(null);m(()=>{o&&h("login")},[o]);const C=()=>"login"===f?le(d)||ce(d):le(_);return e("div",{className:ke,children:t("div",{className:we,children:[e("h2",{className:xe,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??ie;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(pe):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:Pe,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:Oe,onBlur:()=>{if(d){const e=le(d)||ce(d);S(e?"":"Please enter a valid email or phone number")}},required:!0}),e("p",{className:Ne,children:"Phone numbers must include a country code e.g. +1"}),P&&e(Be,{identifier:d,onMagicLink:async()=>{const{error:e}=await s.requestMagicLink();e?v("Failed to send magic link."):n(be,{state:{identifier:d}})},onEmailOtp:async()=>{const{error:e}=await s.requestLoginEmailOtp();e?v("Failed to send email code."):n(pe,{state:{flow:"login"}})},onPhoneOtp:async()=>{const{error:e}=await s.requestLoginPhoneOtp();e?v("Failed to send OTP."):n(ge,{state:{flow:"login"}})},onPasskeyRetry:l?a:void 0,loginMethods:N}),x&&e("p",{className:Te,children:x})]}),"register"===f&&t("div",{className:Se,children:[e("label",{htmlFor:"email",className:Pe,children:"Email Address"}),e("input",{id:"email",type:"email",value:_,onChange:e=>{const t=e.target.value;p(t)},autoComplete:"off",className:Oe,onBlur:()=>{if(_){const e=le(_);w(e?"":"Please enter a valid email")}},required:!0}),k&&e("p",{className:Te,children:k})]}),e("button",{type:"submit",className:Ce,disabled:!C(),"aria-describedby":"seamless-submit-hint",children:"login"===f?"Login":"Register"}),e("p",{id:"seamless-submit-hint",role:"status",className:Ie,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:Te,children:b}),e("button",{type:"button",onClick:()=>h("login"===f?"register":"login"),className:Ae,children:"login"===f?"Don't have an account? Create one":"Already have an account? Sign in"})]}),e(Je,{})]})]})})};var We={container:"passKeyLogin-module__container___IUKxF",card:"passKeyLogin-module__card___2sKM1",title:"passKeyLogin-module__title___HZxvc",button:"passKeyLogin-module__button___FvR4I"};ve(".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 $e=()=>{const r=y(),{handlePasskeyLogin:n}=re(),[o,i]=g("");return e("div",{className:We.container,children:t("div",{className:We.card,children:[e("h2",{className:We.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:We.button,children:"Use Passkey"}),o&&e("p",{className:We.error,children:o})]})})};var De="registerPasskey-module__container___jqEh8",Ve="registerPasskey-module__card___MJAyC",He="registerPasskey-module__loading___Q4i1x",Ke="registerPasskey-module__spinner___V1nhd",Ye="registerPasskey-module__supported___ihTih",Ze="registerPasskey-module__title___vn2Qu",Qe="registerPasskey-module__description___AWmO-",Xe="registerPasskey-module__button___D3b4B",et="registerPasskey-module__message___RiOw-",tt="registerPasskey-module__success___HP5aX",rt="registerPasskey-module__error___hGiqE",nt="registerPasskey-module__skip___1hDyU",ot="registerPasskey-module__secondary___bnOB4";ve(".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 it={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 at=()=>{const{refreshSession:r}=re(),n=oe(),{passkeySupported:o,loading:i}=ue(),{loginMethods:a,loading:s}=ae(),l=y(),[c,d]=g("idle"),[u,m]=g(""),_=se(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?it[t]:void 0}(e)??"Error registering passkey.")}};return e("div",{className:De,children:e("div",{className:Ve,children:t("div",i||s?{className:He,children:[e("div",{className:Ke}),e("span",{children:"Checking for Passkey Support..."})]}:o?{className:Ye,children:[e("h2",{className:Ze,children:"Secure Your Account with a Passkey"}),e("p",{className:Qe,children:"Your device supports passkeys! Register one to skip passwords forever."}),e("button",{onClick:()=>f(),disabled:"loading"===c,className:Xe,children:"loading"===c?"Registering...":"Register Passkey"}),e("button",{type:"button",onClick:()=>f("cross-platform"),disabled:"loading"===c,className:ot,children:"Use a security key instead"}),u&&e("p",{className:`${et} ${"success"===c?tt:rt}`,children:u}),_&&e("button",{type:"button",onClick:p,disabled:"loading"===c,className:nt,children:"Skip for now"})]}:{className:Ye,children:[e("h2",{className:Ze,children:"Passkeys are not available here"}),e("p",{className:Qe,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:Xe,children:"Continue"})]})})})};var st="verifyOTP-module__container___wgoDE",lt="verifyOTP-module__card___3cs2p",ct="verifyOTP-module__title___fZ-1d",dt="verifyOTP-module__subtitle___dGSu3",ut="verifyOTP-module__error___GEtlG",mt="verifyOTP-module__success___46TjG",_t="verifyOTP-module__form___jiGG-",gt="verifyOTP-module__label___Zq5Mi",pt="verifyOTP-module__timer___smq-a",ft="verifyOTP-module__resend___PlmkG",ht="verifyOTP-module__button___8pIuO",yt="verifyOTP-module__toggle___wbzVa";ve(".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 bt="otpInput-module__otpContainer___MUjby",vt="otpInput-module__otpInput___3YMVm";ve(".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 kt=({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:bt,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:vt,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))})},wt=()=>{const r=y(),n=b(),{refreshSession:o}=re(),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=oe(),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)},S=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(pe)})()},[l,i,r,w]),e("div",{className:st,children:t("div",{className:lt,children:[e("h2",{className:ct,children:"Verify Your Phone Number"}),e("p",{className:dt,children:"Enter the code sent to your phone number."}),d&&e("p",{className:ut,children:d}),f&&e("p",{className:mt,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:_t,children:[t("div",{children:[t("label",{htmlFor:"phoneCode",className:gt,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:pt,children:["Code expires in: ",(P=v,`${Math.floor(P/60).toString().padStart(2,"0")}:${(P%60).toString().padStart(2,"0")}`)]})]}),e(kt,{length:6,value:a,onChange:s}),e("button",{type:"button",onClick:()=>S(),className:ft,children:"Resend code to phone"})]}),e("button",{type:"submit",className:ht,children:_?"Verifying...":"Verify & Continue"}),e("button",{type:"button",onClick:()=>r("/login"),className:yt,children:"Back to login"})]})]})});var P,O},xt=()=>{const r=y(),n=b(),{refreshSession:o}=re(),i=oe(),{passkeySupported:a}=ue(),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:st,children:t("div",{className:lt,children:[e("h2",{className:ct,children:"Verify Your Email"}),e("p",{className:dt,children:"We sent you a verification email. Enter the code below."}),f&&e("p",{className:ut,children:f}),v&&e("p",{className:mt,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(ye):(await o(),r("/"))}catch{console.error("Email OTP verification failed."),h("Verification failed.")}finally{c(!1)}}else h("Please enter a valid code.")},className:_t,children:[t("div",{children:[t("label",{htmlFor:"emailCode",className:gt,children:["Email Verification Code",t("span",{className:pt,children:[" ","— Code expires in ",(w=_,`${Math.floor(w/60).toString().padStart(2,"0")}:${(w%60).toString().padStart(2,"0")}`)]})]}),e(kt,{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:ft,children:"Resend code to email"})]}),e("button",{type:"submit",className:ht,disabled:l,children:l?"Verifying...":"Verify & Continue"}),e("button",{type:"button",onClick:()=>r("/login"),className:yt,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"};ve(".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 Pt=()=>{const[r]=v(),n=y(),o=r.get("token"),[i,a]=g(""),[s,l]=g(""),c=oe(),{refreshSession:d}=re();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 Ot={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."},Nt=()=>{const{finishOAuthLogin:n}=re(),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?Ot[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 Tt="magiclink-module__container___45zcA",Ct="magiclink-module__card___Au9oN",It="magiclink-module__iconWrapper___k5ndr",At="magiclink-module__mailIcon___Pwb7q",Mt="magiclink-module__iconRing___bdLvf",Et="magiclink-module__heading___b9cre",zt="magiclink-module__message___kiVFj",Ft="magiclink-module__identifier___JtcFM",Ut="magiclink-module__helperText___7Mlfh",Lt="magiclink-module__actions___64I-3",Rt="magiclink-module__secondaryButton___BntAI",qt="magiclink-module__cooldown___Ib4rm",Bt="magiclink-module__linkButton___maJiv";ve(".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}=re(),n=y(),o=b(),i=oe(),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:Tt,children:t("div",{className:Ct,children:[t("div",{className:It,children:[e("div",{className:Mt}),t("svg",{className:At,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:Et,children:"Check your email"}),e("p",{className:zt,children:"If an account exists for this address, we sent a secure sign-in link."}),a&&e("div",{className:Ft,children:a}),e("p",{className:Ut,children:"Open the email and click the link to finish signing in."}),e("p",{className:Ut,children:"Didn’t receive anything? Check your spam folder or try creating a new account."}),t("div",{className:Lt,children:[e("button",{className:Rt,disabled:s>0,onClick:async()=>{s>0||(await i.requestMagicLink(),l(30))},children:"Resend link"}),s>0&&t("div",{className:qt,children:["Available in ",s,"s"]}),e("button",{className:Bt,onClick:()=>n("/login"),children:"Change email or phone"})]})]})})},Jt=()=>t(k,{children:[e(w,{path:me,element:e(Ge,{})}),e(w,{path:_e,element:e($e,{})}),e(w,{path:ge,element:e(wt,{})}),e(w,{path:pe,element:e(xt,{})}),e(w,{path:fe,element:e(Pt,{})}),e(w,{path:he,element:e(Nt,{})}),e(w,{path:ye,element:e(at,{})}),e(w,{path:be,element:e(jt,{})}),e(w,{path:"*",element:e(x,{to:me,replace:!0})})]});export{ne as AuthProvider,Jt as AuthRoutes,P as SeamlessAuthError,K as createSeamlessAuthClient,q as encodePrfSalt,W as extractPasskeyPrfResult,T as getOAuthErrorCode,A as getPasskeyPolicyErrorCode,M as getWebAuthnErrorDetail,se as hasNonPasskeyLoginMethod,Z as hasScopedRole,J as isPasskeyPrfSupported,Y as roleGrantsAccess,re as useAuth,oe as useAuthClient,ae as useLoginMethods,ue as usePasskeySupport};
|
|
1
|
+
import{jsx as e,jsxs as r,Fragment as a}from"react/jsx-runtime";import{createBrowserOAuthRedirect as o,createBrowserPasskeyPort as t,createAuthSession as i,isUnauthenticated as s,getPasskeyPolicyErrorCode as n,getOAuthErrorCode as l}from"@seamless-auth/client";export{SeamlessAuthError,createSeamlessAuthClient,encodePrfSalt,extractPasskeyPrfResult,getOAuthErrorCode,getPasskeyPolicyErrorCode,getWebAuthnErrorDetail,hasScopedRole,isPasskeyPrfSupported,isUnauthenticated,roleGrantsAccess}from"@seamless-auth/client";import{createContext as c,useContext as d,useMemo as _,useSyncExternalStore as m,useEffect as u,useState as g,useRef as p,useCallback as h}from"react";import{useHref as f,useNavigate as v,useLocation as y,useSearchParams as b,Routes as k,Route as x,Navigate as w}from"react-router-dom";import P from"libphonenumber-js";const N=c(void 0),C=()=>{const e=d(N);if(!e)throw new Error("useAuth must be used within an AuthProvider");return e},S=({children:r,apiHost:a,autoDetectPreviousSignin:s=!0,magicLinkRedirectUri:n,transport:l,ports:c})=>{const d=_(()=>({passkeys:c?.passkeys??t(),oauthRedirect:c?.oauthRedirect??o()}),[c?.passkeys,c?.oauthRedirect]),{mode:g,basePath:p,tokenStorage:h,fetch:f}=l??{},v=_(()=>l?{mode:g,basePath:p,tokenStorage:h,fetch:f}:void 0,[g,p,h,f]),y=_(()=>i({apiHost:a,magicLinkRedirectUri:n,transport:v,passkeys:d.passkeys,detectPreviousSignIn:s}),[a,n,v,d.passkeys,s]),b=m(y.subscribe,y.getState,y.getState);u(()=>{y.actions.refreshSession()},[y]);const k=_(()=>({...b,...y.actions,apiHost:a,magicLinkRedirectUri:n,client:y.client,ports:d}),[b,y,a,n,d]);return e(N.Provider,{value:k,children:r})},M=()=>C().client,I=["passkey","magic_link"],T=()=>{const e=M(),[r,a]=g(null),[o,t]=g(!0);return u(()=>{let r=!0;return(async()=>{try{const{data:o,error:t}=await e.getPublicSystemConfig();r&&!t&&o?.loginMethods?.length&&a(o.loginMethods)}catch{}finally{r&&t(!1)}})(),()=>{r=!1}},[e]),{loginMethods:r,loading:o}},O=e=>Boolean(e?.some(e=>"passkey"!==e)),A=()=>{const{ports:e}=C(),[r,a]=g(!1),[o,t]=g(!0);return u(()=>{let r=!0;return(async()=>{try{const o=e.passkeys.isSupported()&&await e.passkeys.isPlatformAuthenticatorAvailable();r&&a(o)}catch{r&&a(!1)}finally{r&&t(!1)}})(),()=>{r=!1}},[e.passkeys]),{passkeySupported:r,loading:o}},z="/login",E="/passkey-login",B="/verify-phone-otp",L="/verify-email-otp",q="/verify-magiclink",R="/oauth/callback",F="/register-passkey",j="/magic-link-sent";function D(e,r){void 0===r&&(r={});var a=r.insertAt;if(e&&"undefined"!=typeof document){var o=document.head||document.getElementsByTagName("head")[0],t=document.createElement("style");t.type="text/css","top"===a&&o.firstChild?o.insertBefore(t,o.firstChild):o.appendChild(t),t.styleSheet?t.styleSheet.cssText=e:t.appendChild(document.createTextNode(e))}}var V="login-module__container___YggDx",G="login-module__card___Ub-Py",U="login-module__heading___neEqb",W="login-module__inputGroup___P6qgS",K="login-module__label___mx5vu",Y="login-module__input___U07lW",$="login-module__helperText___kDQ4-",H="login-module__error___40zxh",J="login-module__button___0kFBs",Z="login-module__submitHint___qOS5Z",Q="login-module__toggle___1JiG1",X="login-module__fallbackCard___HdIk-",ee="login-module__fallbackHeader___-il2v",re="login-module__fallbackDescription___ea5EB",ae="login-module__fallbackActions___AD3ni",oe="login-module__fallbackActionButton___lFgWf",te="login-module__actionTitle___ycrMp",ie="login-module__actionSubtext___opaIV",se="login-module__linkButton___5lsoe";D(".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 ne=e=>{if("string"!=typeof e)return!1;const r=e.trim();if(r.length<3||r.length>320)return!1;const a=r.split("@");if(2!==a.length)return!1;const[o,t]=a;return!(!/^[A-Za-z0-9._%+-]+$/.test(o)||o.startsWith(".")||o.endsWith(".")||o.includes(".."))&&!(!/^[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/.test(t)||t.startsWith("-")||t.endsWith("-")||t.startsWith(".")||t.endsWith(".")||t.includes(".."))},le=e=>{const r=P(e);return r?.isValid()||!1};const ce=({identifier:a,onMagicLink:o,onEmailOtp:t,onPhoneOtp:i,onPasskeyRetry:s,loginMethods:n})=>{const l=new Set(n??["passkey","magic_link","phone_otp"]),c=l.has("magic_link")&&ne(a),d=l.has("email_otp")&&Boolean(t)&&ne(a),_=l.has("phone_otp")&&le(a),m=l.has("passkey")&&Boolean(s);return c||d||_||m?r("div",{className:X,children:[e("div",{className:ee,children:"Choose a sign-in method"}),e("p",{className:re,children:"Choose another secure sign-in method."}),r("div",{className:ae,children:[c&&r("button",{type:"button",className:oe,onClick:o,children:[e("span",{className:te,children:"Email Magic Link"}),e("span",{className:ie,children:"Send a secure sign-in link to your email"})]}),d&&r("button",{type:"button",className:oe,onClick:t,children:[e("span",{className:te,children:"Email Code"}),e("span",{className:ie,children:"Receive a one-time code by email"})]}),_&&r("button",{type:"button",className:oe,onClick:i,children:[e("span",{className:te,children:"Text Message Code"}),e("span",{className:ie,children:"Receive a one-time code via SMS"})]})]}),m&&e("button",{type:"button",className:se,onClick:s,children:"Try passkey anyway"})]}):null},de="seamless:oauth:provider",_e=()=>{const{listOAuthProviders:a,startOAuthLogin:o,finishOAuthLogin:t,ports:i}=C(),s=f("/oauth/callback"),[n,l]=g([]),[c,d]=g("");if(u(()=>{let e=!0;return a().then(({data:r})=>{e&&l(r?.providers??[])}),()=>{e=!1}},[a]),0===n.length)return null;return r("div",{className:ae,children:[n.map(a=>e("button",{type:"button",className:oe,onClick:()=>(async e=>{d(""),sessionStorage.setItem(de,e);const r=new URL(s,window.location.origin).toString(),{data:a,error:n}=await o({providerId:e,redirectUri:r});if(n)return void d("Could not start sign-in with this provider.");const l=await i.oauthRedirect.open(a.authorizationUrl,r);"callback"===l.type&&(await t({providerId:e,code:l.code,state:l.state})).error&&d("Could not finish sign-in with this provider.")})(a.id),children:r("span",{className:te,children:["Continue with ",a.name]})},a.id)),c&&e("p",{className:H,children:c})]})},me=()=>{const o=v(),{hasSignedInBefore:t,login:i,handlePasskeyLogin:s}=C(),n=M(),{passkeySupported:l}=A(),{loginMethods:c}=T(),[d,_]=g(""),[m,p]=g(""),[h,f]=g("register"),[y,b]=g(""),[k,x]=g(""),[w,P]=g(""),[N,S]=g(!1),[O,z]=g(null);u(()=>{t&&f("login")},[t]);const E=()=>"login"===h?ne(d)||le(d):ne(m);return e("div",{className:V,children:r("div",{className:G,children:[e("h2",{className:U,children:"login"===h?"Sign In":"Create Account"}),r(a,{children:[r("form",{onSubmit:async e=>{e.preventDefault(),b(""),S(!1);try{if("login"===h){const{data:e,error:r}=await i(d,l);if(r)return void b("Failed to start sign-in. Please try again.");const a=e?.loginMethods?.length?e.loginMethods:c??I;if(z(a),l&&a.includes("passkey")){const{error:e}=await s();return e?(S(!0),void b("Passkey sign-in could not be completed. Choose another sign-in method.")):void o("/")}return void S(!0)}"register"===h&&await(async()=>{b("");const{data:e,error:r}=await n.register({email:m});r?b("Failed to register. Please try again."):"Success"===e.message?o(L):b("An unexpected error occurred. Try again. If the problem persists, contact support.")})()}catch{console.error("Failed to continue sign-in."),b("Failed to continue sign-in. Please try again.")}},children:["login"===h&&r("div",{className:W,children:[e("label",{htmlFor:"identifier",className:K,children:"Email Address / Phone Number"}),e("input",{id:"identifier",type:"text",value:d,onChange:e=>{const r=e.target.value;_(r)},autoComplete:"off",placeholder:"Email or Phone Number",className:Y,onBlur:()=>{if(d){const e=ne(d)||le(d);P(e?"":"Please enter a valid email or phone number")}},required:!0}),e("p",{className:$,children:"Phone numbers must include a country code e.g. +1"}),N&&e(ce,{identifier:d,onMagicLink:async()=>{const{error:e}=await n.requestMagicLink();e?b("Failed to send magic link."):o(j,{state:{identifier:d}})},onEmailOtp:async()=>{const{error:e}=await n.requestLoginEmailOtp();e?b("Failed to send email code."):o(L,{state:{flow:"login"}})},onPhoneOtp:async()=>{const{error:e}=await n.requestLoginPhoneOtp();e?b("Failed to send OTP."):o(B,{state:{flow:"login"}})},onPasskeyRetry:l?s:void 0,loginMethods:O}),w&&e("p",{className:H,children:w})]}),"register"===h&&r("div",{className:W,children:[e("label",{htmlFor:"email",className:K,children:"Email Address"}),e("input",{id:"email",type:"email",value:m,onChange:e=>{const r=e.target.value;p(r)},autoComplete:"off",className:Y,onBlur:()=>{if(m){const e=ne(m);x(e?"":"Please enter a valid email")}},required:!0}),k&&e("p",{className:H,children:k})]}),e("button",{type:"submit",className:J,disabled:!E(),"aria-describedby":"seamless-submit-hint",children:"login"===h?"Login":"Register"}),e("p",{id:"seamless-submit-hint",role:"status",className:Z,children:"login"===h?d?E()?"Ready to continue.":"This does not look like a complete email or phone number yet.":"Enter your email or phone number to continue.":m?E()?"Ready to continue.":"This does not look like a complete email address yet.":"Enter your email address to continue."}),y&&e("p",{className:H,children:y}),e("button",{type:"button",onClick:()=>f("login"===h?"register":"login"),className:Q,children:"login"===h?"Don't have an account? Create one":"Already have an account? Sign in"})]}),e(_e,{})]})]})})};var ue={container:"passKeyLogin-module__container___IUKxF",card:"passKeyLogin-module__card___2sKM1",title:"passKeyLogin-module__title___HZxvc",button:"passKeyLogin-module__button___FvR4I"};D(".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 ge=()=>{const a=v(),{handlePasskeyLogin:o}=C(),[t,i]=g("");return e("div",{className:ue.container,children:r("div",{className:ue.card,children:[e("h2",{className:ue.title,children:"Login with Passkey"}),e("button",{onClick:async()=>{i("");const{error:e}=await o();e?i("Passkey sign-in could not be completed. Try another sign-in method."):a("/")},className:ue.button,children:"Use Passkey"}),t&&e("p",{className:ue.error,children:t})]})})};var pe="registerPasskey-module__container___jqEh8",he="registerPasskey-module__card___MJAyC",fe="registerPasskey-module__loading___Q4i1x",ve="registerPasskey-module__spinner___V1nhd",ye="registerPasskey-module__supported___ihTih",be="registerPasskey-module__title___vn2Qu",ke="registerPasskey-module__description___AWmO-",xe="registerPasskey-module__button___D3b4B",we="registerPasskey-module__message___RiOw-",Pe="registerPasskey-module__success___HP5aX",Ne="registerPasskey-module__error___hGiqE",Ce="registerPasskey-module__skip___1hDyU",Se="registerPasskey-module__secondary___bnOB4";D(".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 Me={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 Ie=()=>{const{refreshSession:a}=C(),o=M(),{passkeySupported:t,loading:i}=A(),{loginMethods:l,loading:c}=T(),d=v(),[_,m]=g("idle"),[u,p]=g(""),h=O(l),f=async()=>{await a(),d("/")},y=async e=>{const{platform:r,browser:t,deviceInfo:i}=function(){const e=navigator.userAgent.toLowerCase();let r="unknown",a="unknown";return/iphone|ipad|ipod/.test(e)?r="ios":/android/.test(e)?r="android":/mac os/.test(e)?r="mac":/windows/.test(e)?r="windows":/linux/.test(e)&&(r="linux"),navigator.brave?a="brave":/edg\/|edgios/.test(e)?a="edge":/opr\/|opt\/|opera/.test(e)?a="opera":/vivaldi/.test(e)?a="vivaldi":/samsungbrowser/.test(e)?a="samsung":/firefox|fxios/.test(e)?a="firefox":/chrome|crios/.test(e)?a="chrome":/safari/.test(e)&&(a="safari"),{platform:r,browser:a,deviceInfo:`${r} • ${a}`}}(),l={friendlyName:i,platform:r,browser:t,deviceInfo:i};m("loading");try{const{error:r}=await o.registerPasskey({metadata:l,attachment:e});if(r)throw r;await a(),m("success"),p("Passkey registered successfully."),d("/")}catch(e){console.error("Passkey registration failed."),m("error"),p(function(e){const r=n(e);return r?Me[r]:void 0}(e)??(s(e)?"Your session expired before the passkey was saved. Sign in again to add one.":"Error registering passkey."))}};return e("div",{className:pe,children:e("div",{className:he,children:r("div",i||c?{className:fe,children:[e("div",{className:ve}),e("span",{children:"Checking for Passkey Support..."})]}:t?{className:ye,children:[e("h2",{className:be,children:"Secure Your Account with a Passkey"}),e("p",{className:ke,children:"Your device supports passkeys! Register one to skip passwords forever."}),e("button",{onClick:()=>y(),disabled:"loading"===_,className:xe,children:"loading"===_?"Registering...":"Register Passkey"}),e("button",{type:"button",onClick:()=>y("cross-platform"),disabled:"loading"===_,className:Se,children:"Use a security key instead"}),u&&e("p",{className:`${we} ${"success"===_?Pe:Ne}`,children:u}),h&&e("button",{type:"button",onClick:f,disabled:"loading"===_,className:Ce,children:"Skip for now"})]}:{className:ye,children:[e("h2",{className:be,children:"Passkeys are not available here"}),e("p",{className:ke,children:h?"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."}),h&&e("button",{type:"button",onClick:f,className:xe,children:"Continue"})]})})})};var Te="verifyOTP-module__container___wgoDE",Oe="verifyOTP-module__card___3cs2p",Ae="verifyOTP-module__title___fZ-1d",ze="verifyOTP-module__subtitle___dGSu3",Ee="verifyOTP-module__error___GEtlG",Be="verifyOTP-module__success___46TjG",Le="verifyOTP-module__form___jiGG-",qe="verifyOTP-module__label___Zq5Mi",Re="verifyOTP-module__timer___smq-a",Fe="verifyOTP-module__resend___PlmkG",je="verifyOTP-module__button___8pIuO",De="verifyOTP-module__toggle___wbzVa";D(".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 Ve="otpInput-module__otpContainer___MUjby",Ge="otpInput-module__otpInput___3YMVm";D(".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 Ue=({length:r=6,value:a,inputMode:o="numeric",onChange:t,name:i="otp"})=>{const s=p([]),n=a.split("").concat(Array(r).fill("")).slice(0,r),l=(e,r)=>{var a;if(r.length>1)c(e,r);else if(!r||(a=r,"numeric"===o?/^\d$/.test(a):/^[a-zA-Z]$/.test(a))){const a=[...n];a[e]=r;const o=a.join("");t(o),r&&s.current[e+1]&&s.current[e+1]?.focus()}},c=(e,a)=>{let i=a;i="numeric"===o?a.replace(/\D/g,""):a.replace(/[^a-zA-Z]/g,"");const l=i.slice(0,r).split(""),c=[...n];l.forEach((a,o)=>{e+o<r&&(c[e+o]=a)}),t(c.join("")),s.current[Math.min(e+l.length,r-1)]?.focus()};return e("div",{className:Ve,role:"group","aria-label":"One-time password input",onPaste:e=>{e.preventDefault(),c(0,e.clipboardData.getData("text"))},children:n.map((a,c)=>e("input",{ref:e=>s.current[c]=e,type:"text",inputMode:"numeric"===o?"numeric":"text",autoComplete:"one-time-code",name:`${i}-${c}`,maxLength:1,value:a,className:Ge,onChange:e=>l(c,e.target.value),onKeyDown:e=>((e,a)=>{if("Backspace"===a.key){a.preventDefault();const r=[...n];n[e]?(r[e]="",t(r.join(""))):e>0&&(r[e-1]="",t(r.join("")),s.current[e-1]?.focus())}"ArrowLeft"===a.key&&e>0&&s.current[e-1]?.focus(),"ArrowRight"===a.key&&e<r-1&&s.current[e+1]?.focus()})(c,e),"aria-label":`Digit ${c+1}`},c))})},We=()=>{const a=v(),o=y(),{refreshSession:t}=C(),i="login"===o.state?.flow,[s,n]=g(""),[l,c]=g(null),[d,_]=g(""),[m,p]=g(!1),[h,f]=g(""),[b,k]=g(300),x=M(),w=async()=>{if(p(!0),!l){const{error:e}=i?await x.verifyLoginPhoneOtp(s):await x.verifyPhoneOtp(s);if(e)_("Verification failed.");else{if(i)return await t(),void a("/");c(!0)}}p(!1)},P=async()=>{f(""),await(async()=>{_("");const{error:e}=i?await x.requestLoginPhoneOtp():await x.requestPhoneOtp();e?_("Failed to send SMS code. If this persists, refresh the page and try again."):f("Verification SMS has been resent.")})(),f("Verification SMS has been resent.")};return u(()=>{const e=setInterval(()=>{k(e=>e>0?e-1:0)},1e3);return()=>clearInterval(e)},[]),u(()=>{l&&!i&&(async()=>{const{error:e}=await x.requestEmailOtp();e?_("Failed to send Email code. If this persists, refresh the page and try registering again."):a(L)})()},[l,i,a,x]),e("div",{className:Te,children:r("div",{className:Oe,children:[e("h2",{className:Ae,children:"Verify Your Phone Number"}),e("p",{className:ze,children:"Enter the code sent to your phone number."}),d&&e("p",{className:Ee,children:d}),h&&e("p",{className:Be,children:h}),r("form",{onSubmit:async e=>{if(e.preventDefault(),_(""),6===s.length){p(!0);try{await w()}catch{_("Unexpected error occurred.")}finally{p(!1)}}else _("Please enter a valid code.")},className:Le,children:[r("div",{children:[r("label",{htmlFor:"phoneCode",className:qe,children:["Phone Verification Code ",(S=l,!0===S?e("span",{className:"text-green-400 ml-2",children:"✅"}):!1===S?e("span",{className:"text-red-400 ml-2",children:"❌"}):null)," -"," ",r("span",{className:Re,children:["Code expires in: ",(N=b,`${Math.floor(N/60).toString().padStart(2,"0")}:${(N%60).toString().padStart(2,"0")}`)]})]}),e(Ue,{length:6,value:s,onChange:n}),e("button",{type:"button",onClick:()=>P(),className:Fe,children:"Resend code to phone"})]}),e("button",{type:"submit",className:je,children:m?"Verifying...":"Verify & Continue"}),e("button",{type:"button",onClick:()=>a("/login"),className:De,children:"Back to login"})]})]})});var N,S},Ke=()=>{const a=v(),o=y(),{refreshSession:t}=C(),i=M(),{passkeySupported:s}=A(),n="login"===o.state?.flow,[l,c]=g(!1),[d,_]=g(""),[m,p]=g(300),[h,f]=g(""),[b,k]=g("");return u(()=>{const e=setInterval(()=>{p(e=>e>0?e-1:0)},1e3);return()=>clearInterval(e)},[]),e("div",{className:Te,children:r("div",{className:Oe,children:[e("h2",{className:Ae,children:"Verify Your Email"}),e("p",{className:ze,children:"We sent you a verification email. Enter the code below."}),h&&e("p",{className:Ee,children:h}),b&&e("p",{className:Be,children:b}),r("form",{onSubmit:async e=>{if(e.preventDefault(),f(""),6===d.length){c(!0);try{const{error:e}=n?await i.verifyLoginEmailOtp(d):await i.verifyEmailOtp(d);if(e)return void f("Verification failed.");if(n)return await t(),void a("/");s?a(F):(await t(),a("/"))}catch{console.error("Email OTP verification failed."),f("Verification failed.")}finally{c(!1)}}else f("Please enter a valid code.")},className:Le,children:[r("div",{children:[r("label",{htmlFor:"emailCode",className:qe,children:["Email Verification Code",r("span",{className:Re,children:[" ","— Code expires in ",(x=m,`${Math.floor(x/60).toString().padStart(2,"0")}:${(x%60).toString().padStart(2,"0")}`)]})]}),e(Ue,{length:6,value:d,onChange:_,inputMode:"text"}),e("button",{type:"button",onClick:async()=>{f(""),k("");const{error:e}=n?await i.requestLoginEmailOtp():await i.requestEmailOtp();e?f("Failed to send Email code. If this persists, refresh the page and try again."):k("Verification email has been resent.")},className:Fe,children:"Resend code to email"})]}),e("button",{type:"submit",className:je,disabled:l,children:l?"Verifying...":"Verify & Continue"}),e("button",{type:"button",onClick:()=>a("/login"),className:De,children:"Back to login"})]})]})});var x};var Ye={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"};D(".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 $e=()=>{const[a]=b(),o=v(),t=a.get("token"),[i,s]=g(""),[n,l]=g(""),c=M(),{refreshSession:d}=C();return u(()=>{let e=!0,r=null,a=null;return(async()=>{if(!t)return e&&s("Missing token for verification."),void console.error("No magic-link token found.");const{error:i}=await c.verifyMagicLink(t);if(i)return console.error("Failed to verify token"),void(e&&s("Failed to verify token"));await d(),e&&(r=new BroadcastChannel("seamless-auth"),r.postMessage({type:"MAGIC_LINK_AUTH_SUCCESS"}),l("You have been verified on the device and browser that initiated this request"),a=setTimeout(()=>{e&&o("/")},900))})(),()=>{e=!1,r?.close(),a&&clearTimeout(a)}},[t,c,o,d]),e("div",{className:Ye.container,children:r("div",{className:Ye.card,children:[e("h1",{className:Ye.title,children:"Verifying your login"}),r("div",{className:Ye.verificationContent,children:[!n&&!i&&e("div",{className:Ye.spinner}),n&&e("div",{className:Ye.successIcon,children:e("svg",{viewBox:"0 0 24 24",className:Ye.checkIcon,fill:"none",stroke:"currentColor",strokeWidth:"2",children:e("path",{d:"M5 13l4 4L19 7"})})}),!n&&!i&&e("p",{className:Ye.helperText,children:"Please wait while we securely verify your sign-in link."}),n&&e("p",{className:Ye.successText,children:"Login verified. Redirecting…"}),i&&e("p",{className:Ye.error,children:i})]})]})})};const He={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."},Je=()=>{const{finishOAuthLogin:o}=C(),t=v(),[i]=b(),[s,n]=g(""),c=p(!1);return u(()=>{if(c.current)return;c.current=!0;const e=i.get("code"),r=i.get("state"),a=sessionStorage.getItem(de);e&&r&&a?o({providerId:a,code:e,state:r}).then(({data:e,error:r})=>{if(r){const e=l(r);return void n(e?He[e]:"We could not complete sign-in. Please try again.")}sessionStorage.removeItem(de),t(function(e){if(!e)return null;try{const r=new URL(e,window.location.origin);return r.origin!==window.location.origin?null:`${r.pathname}${r.search}${r.hash}`}catch{return null}}(e?.returnTo)??"/")}):n("This sign-in link is missing required information.")},[o,t,i]),r("div",{className:Ye.container,children:[e("h2",{className:Ye.title,children:s?"Sign-in failed":"Completing sign-in..."}),s&&r(a,{children:[e("p",{children:s}),e("button",{type:"button",onClick:()=>t("/login"),children:"Back to login"})]})]})};var Ze="magiclink-module__container___45zcA",Qe="magiclink-module__card___Au9oN",Xe="magiclink-module__iconWrapper___k5ndr",er="magiclink-module__mailIcon___Pwb7q",rr="magiclink-module__iconRing___bdLvf",ar="magiclink-module__heading___b9cre",or="magiclink-module__message___kiVFj",tr="magiclink-module__identifier___JtcFM",ir="magiclink-module__helperText___7Mlfh",sr="magiclink-module__actions___64I-3",nr="magiclink-module__secondaryButton___BntAI",lr="magiclink-module__cooldown___Ib4rm",cr="magiclink-module__linkButton___maJiv";D(".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 dr=()=>{const{refreshSession:a}=C(),o=v(),t=y(),i=M(),s=t.state?.identifier,[n,l]=g(30);u(()=>{const e=setInterval(()=>{l(e=>e>0?e-1:0)},1e3);return()=>clearInterval(e)},[]);const c=h(async()=>{const{data:e,error:r}=await i.checkMagicLink();return!r&&"Success"===e?.message},[i]);return u(()=>{const e=new BroadcastChannel("seamless-auth");return e.onmessage=async e=>{"MAGIC_LINK_AUTH_SUCCESS"===e.data?.type&&await c()&&(await a(),o("/"))},()=>{e.close()}},[c,o,a]),u(()=>{const e=setInterval(async()=>{try{await c()&&(await a(),o("/"))}catch{}},5e3);return()=>clearInterval(e)},[c,a,o]),e("div",{className:Ze,children:r("div",{className:Qe,children:[r("div",{className:Xe,children:[e("div",{className:rr}),r("svg",{className:er,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:ar,children:"Check your email"}),e("p",{className:or,children:"If an account exists for this address, we sent a secure sign-in link."}),s&&e("div",{className:tr,children:s}),e("p",{className:ir,children:"Open the email and click the link to finish signing in."}),e("p",{className:ir,children:"Didn’t receive anything? Check your spam folder or try creating a new account."}),r("div",{className:sr,children:[e("button",{className:nr,disabled:n>0,onClick:async()=>{n>0||(await i.requestMagicLink(),l(30))},children:"Resend link"}),n>0&&r("div",{className:lr,children:["Available in ",n,"s"]}),e("button",{className:cr,onClick:()=>o("/login"),children:"Change email or phone"})]})]})})},_r=()=>r(k,{children:[e(x,{path:z,element:e(me,{})}),e(x,{path:E,element:e(ge,{})}),e(x,{path:B,element:e(We,{})}),e(x,{path:L,element:e(Ke,{})}),e(x,{path:q,element:e($e,{})}),e(x,{path:R,element:e(Je,{})}),e(x,{path:F,element:e(Ie,{})}),e(x,{path:j,element:e(dr,{})}),e(x,{path:"*",element:e(w,{to:z,replace:!0})})]}),mr=()=>C().client.authorizedFetch;export{S as AuthProvider,_r as AuthRoutes,O as hasNonPasskeyLoginMethod,C as useAuth,M as useAuthClient,mr as useAuthorizedFetch,T as useLoginMethods,A as usePasskeySupport};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|