@awsmbiz/inbis-auth-ui 0.0.0-stage → 0.1.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/README.md +58 -2
- package/dist/index.d.ts +496 -0
- package/dist/index.js +2374 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -3
- package/styles/styles.css +7 -0
package/README.md
CHANGED
|
@@ -1,3 +1,59 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @awsmbiz/inbis-auth-ui
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React screens for Inbis authentication, built from `@awsmbiz/inbis-ui` (shadcn/ui) and driven by the
|
|
4
|
+
server's public auth config (`GET /api/auth/public-config`): a screen shows only the methods
|
|
5
|
+
the app enables.
|
|
6
|
+
|
|
7
|
+
| Export | Contents |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `SignInScreen` | Email + password sign-in. Props: `client` (`InbisAuthClient`), `onSignedIn()`, optional `callbackURL`, optional `onCreateAccount()`, optional `onForgotPassword()`, optional `onVerifyEmail()`, optional `onPhoneSignIn()` ("Use a phone number instead", shown when the public config has `methods.phoneOtp`). A phone-only app (no email + password) gets the phone form right on this screen, described as signing in "with your phone number". Shows a message when no method is enabled and an alert on failure (wrong credentials, unverified email, network). The "Create an account" link appears only when `onCreateAccount` is given and registration is `open`. "Forgot password?" appears only when `onForgotPassword` is given and the public config offers password reset. After a `403 EMAIL_NOT_VERIFIED`, a "Send a new verification link" button calls `onVerifyEmail`. If the user has 2FA on, the password step is followed by the 2FA challenge, and `onSignedIn` runs after it. |
|
|
10
|
+
| `VerifyEmailScreen` | Where verification links land. Props: `client`, `onSignIn()`, optional `callbackURL` (default `/verify-email?verified=1` on this origin), optional `search` (default `window.location.search`). `?verified=1` shows success. `?error=…` explains that the link is invalid or expired. `?sent=1` says "check your inbox". It always offers a form to request a new link, which gives the same answer for any address. |
|
|
11
|
+
| `ForgotPasswordScreen` | Requests a reset link. Props: `client`, `onSignIn()`, optional `resetPasswordURL` (default `/reset-password` on this origin). It always confirms with the same words, whether or not the account exists. |
|
|
12
|
+
| `ResetPasswordScreen` | Sets a new password from the link's `?token=`. Props: `client`, `onSignIn()`, `onRequestNewLink()`, optional `search`. It checks that the confirmation matches before calling the server. Errors it explains: an expired or used link (`INVALID_TOKEN`, also `?error=`), a breached password (`PASSWORD_COMPROMISED`) and the length bounds. |
|
|
13
|
+
| `SignUpScreen` | Email + password registration. Props: `client`, `onSignedUp()`, `onSignIn()`, optional `callbackURL`. Shows the form only when registration is `open` and email + password is enabled; otherwise says registration is not open. The server enforces the policy regardless. |
|
|
14
|
+
| `AcceptInvitationScreen` | Where invitation links land (`registration.invitations.acceptPath`, default `/accept-invitation`). Props: `client`, `onSignIn()`, optional `search` (default `window.location.search`). Looks the `?token=` up, shows who the invitation is for (and when it makes an administrator), asks for a name and, when email + password is on, a password with confirmation, then says the account is ready and offers sign-in (accepting does not sign in). An invalid, expired, revoked or used link shows a message and no form. Explains `USER_ALREADY_EXISTS`, `REGISTRATION_NOT_ALLOWED`, a breached password and the length bounds. A **phone invitation** (sent by SMS) shows the number and, with the optional `onSignedIn()` prop, signs in with that number right there (send code, enter code; the first sign-in creates the account); without it, it asks the invitee to sign in by phone. |
|
|
15
|
+
| `PhoneSignInScreen` | Phone OTP sign-in. Props: `client`, `onSignedIn()`, optional `onSignIn()` (back to the other methods). Asks for an international number (hint: include the country code), sends a code, then shows `PhoneOtpVerification`. The confirmation never reveals whether the number has an account. Explains an invalid number, a wrong or expired code, too many attempts, `REGISTRATION_NOT_ALLOWED` and a suspended account; offers "Send a new code" and "Use a different number". |
|
|
16
|
+
| `PhoneSignIn` | The same flow as a form inside another screen. Props: `client`, `phoneOtp` (from the public config), `onSignedIn()`, optional `phoneNumber` (fixed number: no number field, e.g. an invited number). |
|
|
17
|
+
| `PhoneOtpVerification` | The code step on its own. Props: `client`, `phoneNumber`, `phoneOtp`, `onSignedIn()`, `onResend()`, optional `onChangeNumber()`. |
|
|
18
|
+
| `TwoFactorChallenge` | The 2FA step of sign-in on its own (`SignInScreen` already includes it): authenticator code, or "Use a backup code" instead. Props: `client`, `onVerified()`. |
|
|
19
|
+
| `TwoFactorSettingsScreen` | Account security for the signed-in user: 2FA status; set up an authenticator app (password → QR code and manual key → verify a code → save the 10 backup codes); regenerate backup codes; turn 2FA off. Every change asks for the current password; regenerating and turning off also need a recent sign-in, and on `SESSION_NOT_FRESH` the screen explains why and offers "Sign in again" when `onReauthenticate()` is given. Props: `client`, optional `onReauthenticate()`. Says 2FA is unavailable when the public config has no `twoFactor`. |
|
|
20
|
+
| `DeleteAccountScreen` | Delete the signed-in user's account: explains what happens (at once, or after the grace period from the public config's `accountDeletion`), asks the user to type `DELETE`, then shows "deleted" or the scheduled date. On `SESSION_NOT_FRESH` it explains and offers "Sign in again" when `onReauthenticate()` is given. Props: `client`, `onDeleted()` (from the confirmation; the session has ended), `onCancel()`, optional `onReauthenticate()`. Says deletion is unavailable when the public config has no `accountDeletion`. |
|
|
21
|
+
| `SessionsScreen` | The signed-in user's sessions: device (user agent), sign-in time and IP for each, "This device" on the current one, "Sign out" for each other session and "Sign out all other sessions" (then "Signed out N other sessions."). Signing out needs a recent sign-in; on `SESSION_NOT_FRESH` it explains why and offers "Sign in again" when `onReauthenticate()` is given. Props: `client`, optional `onReauthenticate()`. |
|
|
22
|
+
| `StepUpScreen` | Shown **instead of** a protected page whose route answered `403 TWO_FACTOR_REQUIRED` or `403 STEP_UP_REQUIRED` (role policy, ADR-3), so the page's content never renders. Props: `client`, `requirement` (from `accessRequirementOf(response)` in `@awsmbiz/inbis-auth/client`), `onSatisfied()` (retry the page), `onSignInAgain()` (sign out, open sign-in). 2FA required and none set up: "Set up two-factor authentication" (password → QR code → code → backup codes; enrolment verifies the session), then `onSatisfied`. 2FA required and already set up: "Verify it's you" with "Sign in again". Step-up: "Sign in again" naming the allowed methods (e.g. "your email and password"). |
|
|
23
|
+
| `useTurnstile(captcha, action)` | The Turnstile check for a custom form: `{ widget, ready, token, reset }`. `captcha` is the public config's `captcha`, `action` `"signIn"`, `"signUp"`, `"passwordReset"` or `"phoneOtp"`; a no-op when the action needs no check. Render `widget`, submit only when `ready`, send `token` as `captchaToken`, call `reset()` after every submit (tokens are single use). |
|
|
24
|
+
| `usePublicAuthConfig(client)` | `{ status: "loading" } \| { status: "error" } \| { status: "ready", config }` |
|
|
25
|
+
| `messages`, `t(key, values?)`, `MessageKey` | Every user-facing string by message key (English only for now). |
|
|
26
|
+
| `@awsmbiz/inbis-auth-ui/styles.css` | `@source` for this package's `dist`, so Tailwind generates its classes |
|
|
27
|
+
|
|
28
|
+
**Turnstile.** When the public config has `captcha`, `SignInScreen` (email + password),
|
|
29
|
+
`SignUpScreen`, `ForgotPasswordScreen` and the phone screens (`PhoneSignInScreen`, `PhoneSignIn`, the phone path of `AcceptInvitationScreen`)
|
|
30
|
+
render Cloudflare's Turnstile widget and send its token in the `x-captcha-response` header; the
|
|
31
|
+
submit button waits for the check. The widget script is loaded once, on demand, from
|
|
32
|
+
`https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit` (explicit rendering, as
|
|
33
|
+
Cloudflare documents it), so a Content-Security-Policy must allow
|
|
34
|
+
`https://challenges.cloudflare.com` in `script-src` and `frame-src`. "Send a new code" goes back
|
|
35
|
+
to the number step for a fresh check. The screens also explain the abuse-control refusals
|
|
36
|
+
(`PHONE_COUNTRY_NOT_ALLOWED`, `TOO_MANY_OTP_REQUESTS`, `SMS_DAILY_CAP_REACHED`, `RATE_LIMITED`,
|
|
37
|
+
a failed check).
|
|
38
|
+
|
|
39
|
+
Peer dependencies: `react`, `react-dom` ^19.3.1. Depends on `@awsmbiz/inbis-auth` (for
|
|
40
|
+
`@awsmbiz/inbis-auth/client` only), `@awsmbiz/inbis-ui` and `uqr` (QR code as SVG, no dependencies; the QR
|
|
41
|
+
code is rendered locally, the TOTP secret never leaves the page).
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import { createInbisAuthClient } from "@awsmbiz/inbis-auth/client";
|
|
45
|
+
import { SignInScreen } from "@awsmbiz/inbis-auth-ui";
|
|
46
|
+
|
|
47
|
+
const client = createInbisAuthClient(); // same origin as the API
|
|
48
|
+
<SignInScreen client={client} onSignedIn={() => navigate("/")} />;
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```css
|
|
52
|
+
@import "@awsmbiz/inbis-ui/globals.css";
|
|
53
|
+
@import "@awsmbiz/inbis-auth-ui/styles.css";
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Tested in a real browser by the playground's Playwright suite
|
|
57
|
+
(`pnpm --filter identity-playground test:e2e`): each config preset (`e2e/presets.spec.ts`) and
|
|
58
|
+
axe-core WCAG 2.x A/AA checks of every screen in the states users meet them
|
|
59
|
+
(`e2e/accessibility.spec.ts`).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { AccessRequirement, CaptchaAction, InbisAuthClient, PublicAuthConfig } from "@awsmbiz/inbis-auth/client";
|
|
3
|
+
interface AcceptInvitationScreenProps {
|
|
4
|
+
client: InbisAuthClient;
|
|
5
|
+
/** Called to go to the sign-in screen (after accepting, or to give up). */
|
|
6
|
+
onSignIn: () => void;
|
|
7
|
+
/** The page's query string, which carries `token` from the emailed link. Defaults to `window.location.search`. */
|
|
8
|
+
search?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Called once an invited phone number has signed in. Phone invitations (sent
|
|
11
|
+
* by SMS) are accepted by signing in with the number right on this screen;
|
|
12
|
+
* without this callback the screen only points the invitee to phone sign-in.
|
|
13
|
+
*/
|
|
14
|
+
onSignedIn?: () => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Accepts an invitation from its emailed link (`?token=…`): shows who it is
|
|
18
|
+
* for, asks for a name and (when the password method is on) a password, and
|
|
19
|
+
* creates the account. Accepting does not sign in; it offers sign-in after.
|
|
20
|
+
* An invalid, expired, revoked or used link says so and creates nothing.
|
|
21
|
+
*/
|
|
22
|
+
export declare function AcceptInvitationScreen(props: AcceptInvitationScreenProps): import("react").JSX.Element;
|
|
23
|
+
interface ForgotPasswordScreenProps {
|
|
24
|
+
client: InbisAuthClient;
|
|
25
|
+
/** Called to go back to the sign-in screen. */
|
|
26
|
+
onSignIn: () => void;
|
|
27
|
+
/**
|
|
28
|
+
* Where the emailed link sends the user (the `ResetPasswordScreen`), as a
|
|
29
|
+
* relative path or a trusted-origin URL. Default `/reset-password` on this origin.
|
|
30
|
+
*/
|
|
31
|
+
resetPasswordURL?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Asks for an email address and sends a password-reset link. Always confirms
|
|
35
|
+
* in the same words, whether or not the address has an account. Renders the
|
|
36
|
+
* Turnstile widget when the server's public config asks for it.
|
|
37
|
+
*/
|
|
38
|
+
export declare function ForgotPasswordScreen({ client, onSignIn, resetPasswordURL }: ForgotPasswordScreenProps): import("react").JSX.Element;
|
|
39
|
+
/**
|
|
40
|
+
* Every user-facing string of `@awsmbiz/inbis-auth-ui`, by message key. English only
|
|
41
|
+
* in the baseline; keys keep a later translation a data change.
|
|
42
|
+
*/
|
|
43
|
+
export declare const messages: {
|
|
44
|
+
readonly "signIn.title": "Sign in";
|
|
45
|
+
readonly "signIn.description": "Sign in to {appName} with your email and password.";
|
|
46
|
+
readonly "signIn.descriptionPhone": "Sign in to {appName} with your phone number.";
|
|
47
|
+
readonly "signIn.email": "Email";
|
|
48
|
+
readonly "signIn.password": "Password";
|
|
49
|
+
readonly "signIn.submit": "Sign in";
|
|
50
|
+
readonly "signIn.submitting": "Signing in…";
|
|
51
|
+
readonly "signIn.failed": "Could not sign in";
|
|
52
|
+
readonly "signIn.invalidCredentials": "The email or password is incorrect.";
|
|
53
|
+
readonly "signIn.noMethods": "No sign-in method is enabled for this app.";
|
|
54
|
+
readonly "twoFactor.title": "Two-factor authentication";
|
|
55
|
+
readonly "twoFactor.challenge.description": "Enter the 6-digit code from your authenticator app.";
|
|
56
|
+
readonly "twoFactor.challenge.backupDescription": "Enter one of your backup codes. Each code works once.";
|
|
57
|
+
readonly "twoFactor.code": "6-digit code";
|
|
58
|
+
readonly "twoFactor.backupCode": "Backup code";
|
|
59
|
+
readonly "twoFactor.verify": "Verify";
|
|
60
|
+
readonly "twoFactor.verifying": "Verifying…";
|
|
61
|
+
readonly "twoFactor.useBackupCode": "Use a backup code";
|
|
62
|
+
readonly "twoFactor.useAuthenticator": "Use your authenticator app";
|
|
63
|
+
readonly "twoFactor.failed": "Could not verify";
|
|
64
|
+
readonly "twoFactor.invalidCode": "That code is not valid. Check your authenticator app and try again.";
|
|
65
|
+
readonly "twoFactor.invalidBackupCode": "That backup code is not valid or has already been used.";
|
|
66
|
+
readonly "twoFactor.challengeExpired": "This sign-in has expired. Sign in again.";
|
|
67
|
+
readonly "twoFactor.tooManyAttempts": "Too many attempts. Sign in again to get a new chance.";
|
|
68
|
+
readonly "twoFactor.locked": "Too many failed attempts. Try again later.";
|
|
69
|
+
readonly "twoFactor.settings.loading": "Loading…";
|
|
70
|
+
readonly "twoFactor.settings.unavailable": "Two-factor authentication is not available for this app.";
|
|
71
|
+
readonly "twoFactor.settings.on": "Two-factor authentication is on.";
|
|
72
|
+
readonly "twoFactor.settings.off": "Two-factor authentication is off.";
|
|
73
|
+
readonly "twoFactor.settings.explainer": "Protect your account with a code from an authenticator app each time you sign in.";
|
|
74
|
+
readonly "twoFactor.settings.setUp": "Set up authenticator app";
|
|
75
|
+
readonly "twoFactor.settings.regenerate": "Regenerate backup codes";
|
|
76
|
+
readonly "twoFactor.settings.disable": "Turn off two-factor authentication";
|
|
77
|
+
readonly "twoFactor.settings.failed": "Could not change two-factor authentication";
|
|
78
|
+
readonly "twoFactor.password": "Current password";
|
|
79
|
+
readonly "twoFactor.password.setUp": "Confirm your password to set up an authenticator app.";
|
|
80
|
+
readonly "twoFactor.password.regenerate": "Confirm your password to replace your backup codes. The old codes stop working.";
|
|
81
|
+
readonly "twoFactor.password.disable": "Confirm your password to turn off two-factor authentication.";
|
|
82
|
+
readonly "twoFactor.continue": "Continue";
|
|
83
|
+
readonly "twoFactor.regenerateSubmit": "Regenerate";
|
|
84
|
+
readonly "twoFactor.disableSubmit": "Turn off";
|
|
85
|
+
readonly "twoFactor.cancel": "Cancel";
|
|
86
|
+
readonly "twoFactor.working": "Working…";
|
|
87
|
+
readonly "twoFactor.invalidPassword": "The password is incorrect.";
|
|
88
|
+
readonly "twoFactor.notFresh": "For your security, sign in again before changing two-factor authentication.";
|
|
89
|
+
readonly "twoFactor.signInAgain": "Sign in again";
|
|
90
|
+
readonly "twoFactor.enrol.scan": "Scan this QR code with your authenticator app, then enter the 6-digit code it shows.";
|
|
91
|
+
readonly "twoFactor.enrol.qrAlt": "QR code for your authenticator app";
|
|
92
|
+
readonly "twoFactor.enrol.manual": "Can't scan it? Enter this key instead:";
|
|
93
|
+
readonly "twoFactor.enrol.submit": "Verify and turn on";
|
|
94
|
+
readonly "twoFactor.backupCodes.title": "Save your backup codes";
|
|
95
|
+
readonly "twoFactor.backupCodes.description": "Each code signs you in once if you lose your authenticator. Store them somewhere safe; they are not shown again.";
|
|
96
|
+
readonly "twoFactor.backupCodes.list": "Backup codes";
|
|
97
|
+
readonly "twoFactor.backupCodes.done": "I have saved my backup codes";
|
|
98
|
+
readonly "signIn.noAccount": "No account yet?";
|
|
99
|
+
readonly "signIn.createAccount": "Create an account";
|
|
100
|
+
readonly "signUp.title": "Create an account";
|
|
101
|
+
readonly "signUp.description": "Create your {appName} account.";
|
|
102
|
+
readonly "signUp.name": "Name";
|
|
103
|
+
readonly "signUp.email": "Email";
|
|
104
|
+
readonly "signUp.password": "Password";
|
|
105
|
+
readonly "signUp.passwordHint": "{minLength} to {maxLength} characters.";
|
|
106
|
+
readonly "signUp.submit": "Create account";
|
|
107
|
+
readonly "signUp.submitting": "Creating account…";
|
|
108
|
+
readonly "signUp.failed": "Could not create the account";
|
|
109
|
+
readonly "signUp.closedTitle": "Registration is not open";
|
|
110
|
+
readonly "signUp.closed": "{appName} does not accept new accounts here. Ask an administrator for an invitation.";
|
|
111
|
+
readonly "signUp.backToSignIn": "Back to sign in";
|
|
112
|
+
readonly "deleteAccount.title": "Delete account";
|
|
113
|
+
readonly "deleteAccount.unavailable": "Accounts cannot be deleted here. Contact {appName} to close your account.";
|
|
114
|
+
readonly "deleteAccount.warning": "This permanently deletes your {appName} account and signs you out everywhere. It cannot be undone.";
|
|
115
|
+
readonly "deleteAccount.warningGrace": "Your {appName} account will be permanently deleted after {graceDays} days, and you will be signed out everywhere. Signing in again before then cancels the deletion.";
|
|
116
|
+
readonly "deleteAccount.confirmLabel": "Type DELETE to confirm";
|
|
117
|
+
readonly "deleteAccount.confirmWord": "DELETE";
|
|
118
|
+
readonly "deleteAccount.submit": "Delete my account";
|
|
119
|
+
readonly "deleteAccount.submitting": "Deleting…";
|
|
120
|
+
readonly "deleteAccount.cancel": "Cancel";
|
|
121
|
+
readonly "deleteAccount.failed": "Could not delete the account";
|
|
122
|
+
readonly "deleteAccount.notFresh": "For your security, sign in again before deleting your account.";
|
|
123
|
+
readonly "deleteAccount.signInAgain": "Sign in again";
|
|
124
|
+
readonly "deleteAccount.deleted": "Your account has been deleted.";
|
|
125
|
+
readonly "deleteAccount.scheduled": "Your account will be deleted on {date}. Sign in before then to keep it.";
|
|
126
|
+
readonly "deleteAccount.done": "Done";
|
|
127
|
+
readonly "signIn.forgotPassword": "Forgot password?";
|
|
128
|
+
readonly "signIn.emailNotVerified": "Verify your email address before signing in. Check your inbox for the link.";
|
|
129
|
+
readonly "signIn.resendVerification": "Send a new verification link";
|
|
130
|
+
readonly "verifyEmail.title": "Verify your email";
|
|
131
|
+
readonly "verifyEmail.description": "Enter your email address and we will send you a new verification link.";
|
|
132
|
+
readonly "verifyEmail.email": "Email";
|
|
133
|
+
readonly "verifyEmail.submit": "Send verification link";
|
|
134
|
+
readonly "verifyEmail.submitting": "Sending…";
|
|
135
|
+
readonly "verifyEmail.sent": "If that email belongs to an account that still needs verifying, a new link is on its way. It works for a limited time.";
|
|
136
|
+
readonly "verifyEmail.checkInbox": "Check your inbox: we sent you a link to verify your email address.";
|
|
137
|
+
readonly "verifyEmail.verifiedTitle": "Email verified";
|
|
138
|
+
readonly "verifyEmail.verified": "Your email address is verified.";
|
|
139
|
+
readonly "verifyEmail.continue": "Continue to sign in";
|
|
140
|
+
readonly "verifyEmail.invalid": "This verification link is invalid or has expired. Request a new one below.";
|
|
141
|
+
readonly "verifyEmail.failed": "Could not send the link";
|
|
142
|
+
readonly "forgotPassword.title": "Reset your password";
|
|
143
|
+
readonly "forgotPassword.description": "Enter your email address and we will send you a link to choose a new password.";
|
|
144
|
+
readonly "forgotPassword.email": "Email";
|
|
145
|
+
readonly "forgotPassword.submit": "Send reset link";
|
|
146
|
+
readonly "forgotPassword.submitting": "Sending…";
|
|
147
|
+
readonly "forgotPassword.sent": "If an account exists for that email, we have sent a link to reset the password. It works for a limited time.";
|
|
148
|
+
readonly "forgotPassword.failed": "Could not send the link";
|
|
149
|
+
readonly "resetPassword.title": "Choose a new password";
|
|
150
|
+
readonly "resetPassword.newPassword": "New password";
|
|
151
|
+
readonly "resetPassword.confirmPassword": "Confirm new password";
|
|
152
|
+
readonly "resetPassword.submit": "Set new password";
|
|
153
|
+
readonly "resetPassword.submitting": "Saving…";
|
|
154
|
+
readonly "resetPassword.failed": "Could not change the password";
|
|
155
|
+
readonly "resetPassword.mismatch": "The passwords do not match.";
|
|
156
|
+
readonly "resetPassword.invalidLink": "This reset link has expired or was already used.";
|
|
157
|
+
readonly "resetPassword.compromised": "This password has appeared in a data breach, so it is not safe to use. Choose a different one.";
|
|
158
|
+
readonly "resetPassword.tooShort": "The password must be at least {minLength} characters.";
|
|
159
|
+
readonly "resetPassword.tooLong": "The password must be at most {maxLength} characters.";
|
|
160
|
+
readonly "resetPassword.done": "Your password has been changed. Sign in with your new password.";
|
|
161
|
+
readonly "resetPassword.requestNew": "Request a new link";
|
|
162
|
+
readonly "invitation.title": "Accept your invitation";
|
|
163
|
+
readonly "invitation.description": "Create your {appName} account for {email}.";
|
|
164
|
+
readonly "invitation.descriptionAdmin": "Create your {appName} administrator account for {email}.";
|
|
165
|
+
readonly "invitation.loading": "Checking your invitation…";
|
|
166
|
+
readonly "invitation.invalid": "This invitation link is invalid, has expired or was already used. Ask an administrator for a new invitation.";
|
|
167
|
+
readonly "invitation.phone": "This invitation is for a phone number. Sign in with your phone number to accept it.";
|
|
168
|
+
readonly "invitation.name": "Name";
|
|
169
|
+
readonly "invitation.password": "Password";
|
|
170
|
+
readonly "invitation.confirmPassword": "Confirm password";
|
|
171
|
+
readonly "invitation.submit": "Create account";
|
|
172
|
+
readonly "invitation.submitting": "Creating account…";
|
|
173
|
+
readonly "invitation.failed": "Could not create the account";
|
|
174
|
+
readonly "invitation.nameRequired": "Enter your name.";
|
|
175
|
+
readonly "invitation.exists": "An account with this email already exists. Sign in instead.";
|
|
176
|
+
readonly "invitation.notAllowed": "This app is not accepting new accounts right now. Ask an administrator.";
|
|
177
|
+
readonly "invitation.done": "Your account is ready. Sign in to continue.";
|
|
178
|
+
readonly "sessions.title": "Your sessions";
|
|
179
|
+
readonly "sessions.description": "Devices and browsers signed in to your account.";
|
|
180
|
+
readonly "sessions.loading": "Loading…";
|
|
181
|
+
readonly "sessions.loadFailed": "Could not load your sessions. Reload the page to try again.";
|
|
182
|
+
readonly "sessions.unknownDevice": "Unknown device";
|
|
183
|
+
readonly "sessions.signedInAt": "Signed in {date}";
|
|
184
|
+
readonly "sessions.thisDevice": "This device";
|
|
185
|
+
readonly "sessions.revoke": "Sign out";
|
|
186
|
+
readonly "sessions.revokeOthers": "Sign out all other sessions";
|
|
187
|
+
readonly "sessions.othersRevoked": "Signed out {count} other sessions.";
|
|
188
|
+
readonly "sessions.failed": "Could not sign out the session";
|
|
189
|
+
readonly "sessions.notFresh": "For your security, sign in again before signing out other sessions.";
|
|
190
|
+
readonly "sessions.notFound": "That session has already ended.";
|
|
191
|
+
readonly "sessions.signInAgain": "Sign in again";
|
|
192
|
+
readonly "signIn.withPhone": "Use a phone number instead";
|
|
193
|
+
readonly "phone.title": "Sign in with your phone";
|
|
194
|
+
readonly "phone.description": "We will text a sign-in code to your phone.";
|
|
195
|
+
readonly "phone.number": "Phone number";
|
|
196
|
+
readonly "phone.numberHint": "Include the country code, for example +44 7700 900123.";
|
|
197
|
+
readonly "phone.send": "Send code";
|
|
198
|
+
readonly "phone.sending": "Sending…";
|
|
199
|
+
readonly "phone.codeSent": "If {phoneNumber} can sign in here, a {codeLength}-digit code is on its way. It works for {minutes} minutes.";
|
|
200
|
+
readonly "phone.code": "6-digit code";
|
|
201
|
+
readonly "phone.verify": "Verify";
|
|
202
|
+
readonly "phone.verifying": "Verifying…";
|
|
203
|
+
readonly "phone.resend": "Send a new code";
|
|
204
|
+
readonly "phone.changeNumber": "Use a different number";
|
|
205
|
+
readonly "phone.failed": "Could not sign in";
|
|
206
|
+
readonly "phone.invalidNumber": "Enter the number in international format, starting with + and the country code.";
|
|
207
|
+
readonly "phone.invalidCode": "That code is not right. Check the SMS and try again.";
|
|
208
|
+
readonly "phone.expired": "That code has expired or was already used. Send a new code.";
|
|
209
|
+
readonly "phone.tooManyAttempts": "Too many wrong codes. Send a new code.";
|
|
210
|
+
readonly "phone.notAllowed": "This number cannot sign in here. Ask an administrator for an invitation.";
|
|
211
|
+
readonly "phone.banned": "This account is suspended. Contact an administrator.";
|
|
212
|
+
readonly "invitation.descriptionPhone": "Join {appName} with {phoneNumber}. We will text you a sign-in code.";
|
|
213
|
+
readonly "invitation.descriptionPhoneAdmin": "Join {appName} as an administrator with {phoneNumber}. We will text you a sign-in code.";
|
|
214
|
+
readonly "stepUp.setUp.title": "Set up two-factor authentication";
|
|
215
|
+
readonly "stepUp.setUp.description": "This area needs two-factor authentication. Set up an authenticator app to continue.";
|
|
216
|
+
readonly "stepUp.verify.title": "Verify it's you";
|
|
217
|
+
readonly "stepUp.verify.description": "This area needs a sign-in completed with your authenticator app. Sign in again with your password and code.";
|
|
218
|
+
readonly "stepUp.method.title": "Sign in again";
|
|
219
|
+
readonly "stepUp.method.description": "This area needs a sign-in with {methods}. Sign in again to continue.";
|
|
220
|
+
readonly "stepUp.method.password": "your email and password";
|
|
221
|
+
readonly "stepUp.method.phoneOtp": "a code sent to your phone";
|
|
222
|
+
readonly "stepUp.method.other": "another sign-in method";
|
|
223
|
+
readonly "stepUp.signInAgain": "Sign in again";
|
|
224
|
+
readonly "auth.backToSignIn": "Back to sign in";
|
|
225
|
+
readonly "auth.signIn": "Sign in";
|
|
226
|
+
readonly "config.loading": "Loading…";
|
|
227
|
+
readonly "config.failed": "Could not load the sign-in options. Reload the page to try again.";
|
|
228
|
+
readonly "error.network": "Could not reach the server. Check your connection and try again.";
|
|
229
|
+
readonly "error.generic": "Something went wrong. Try again.";
|
|
230
|
+
readonly "error.rateLimited": "Too many attempts. Wait a minute and try again.";
|
|
231
|
+
readonly "captcha.failed": "The security check could not run. Reload the page to try again.";
|
|
232
|
+
readonly "captcha.rejected": "The security check did not pass. Complete it again and retry.";
|
|
233
|
+
readonly "phone.countryNotAllowed": "Phone numbers from this country cannot be used here.";
|
|
234
|
+
readonly "phone.tooManyRequests": "Too many codes were requested. Wait a while before asking for another.";
|
|
235
|
+
readonly "phone.smsPaused": "Text messages are paused for today. Try again later or use another way to sign in.";
|
|
236
|
+
};
|
|
237
|
+
type MessageKey = keyof typeof messages;
|
|
238
|
+
/** Looks up a message and fills `{name}` placeholders. */
|
|
239
|
+
export declare function t(key: MessageKey, values?: Record<string, string>): string;
|
|
240
|
+
interface ResetPasswordScreenProps {
|
|
241
|
+
client: InbisAuthClient;
|
|
242
|
+
/** Called to go to the sign-in screen (after the change, or to give up). */
|
|
243
|
+
onSignIn: () => void;
|
|
244
|
+
/** Called to request a new link, e.g. to navigate to the `ForgotPasswordScreen`. */
|
|
245
|
+
onRequestNewLink: () => void;
|
|
246
|
+
/**
|
|
247
|
+
* The page's query string, which carries `token` (or `error`) from the
|
|
248
|
+
* emailed link. Defaults to `window.location.search`.
|
|
249
|
+
*/
|
|
250
|
+
search?: string;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Sets a new password with the token from an emailed reset link
|
|
254
|
+
* (`?token=…`). An expired or used link (`?error=INVALID_TOKEN`, or the
|
|
255
|
+
* server rejecting the token) offers a new link instead.
|
|
256
|
+
*/
|
|
257
|
+
export declare function ResetPasswordScreen(props: ResetPasswordScreenProps): import("react").JSX.Element;
|
|
258
|
+
interface SignInScreenProps {
|
|
259
|
+
client: InbisAuthClient;
|
|
260
|
+
/** Called after a successful sign-in, e.g. to navigate to the protected area. */
|
|
261
|
+
onSignedIn: () => void;
|
|
262
|
+
/** Where the server may redirect after sign-in; must be relative or a trusted origin. */
|
|
263
|
+
callbackURL?: string;
|
|
264
|
+
/**
|
|
265
|
+
* Called when the user chooses to create an account, e.g. to navigate to the
|
|
266
|
+
* `SignUpScreen`. The link is shown only when registration is `open`.
|
|
267
|
+
*/
|
|
268
|
+
onCreateAccount?: () => void;
|
|
269
|
+
/**
|
|
270
|
+
* Called when the user forgot their password, e.g. to navigate to the
|
|
271
|
+
* `ForgotPasswordScreen`. Shown only when the server offers password reset.
|
|
272
|
+
*/
|
|
273
|
+
onForgotPassword?: () => void;
|
|
274
|
+
/**
|
|
275
|
+
* Called when the user wants a new verification link after signing in with
|
|
276
|
+
* an unverified address, e.g. to navigate to the `VerifyEmailScreen`.
|
|
277
|
+
*/
|
|
278
|
+
onVerifyEmail?: () => void;
|
|
279
|
+
/**
|
|
280
|
+
* Called when the user chooses to sign in with a phone number, e.g. to
|
|
281
|
+
* navigate to the `PhoneSignInScreen`. Shown only when the server offers phone OTP.
|
|
282
|
+
*/
|
|
283
|
+
onPhoneSignIn?: () => void;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* The sign-in screen. Renders only the methods the server's public config
|
|
287
|
+
* enables (email + password, and phone OTP). For a user with 2FA on, the password is
|
|
288
|
+
* followed by the 2FA challenge (authenticator code or backup code).
|
|
289
|
+
*/
|
|
290
|
+
export declare function SignInScreen(props: SignInScreenProps): import("react").JSX.Element;
|
|
291
|
+
interface SignUpScreenProps {
|
|
292
|
+
client: InbisAuthClient;
|
|
293
|
+
/** Called after the account was created (Better Auth signs the new user in). */
|
|
294
|
+
onSignedUp: () => void;
|
|
295
|
+
/** Called to go back to the sign-in screen. */
|
|
296
|
+
onSignIn: () => void;
|
|
297
|
+
/** Where the server may redirect after sign-up; must be relative or a trusted origin. */
|
|
298
|
+
callbackURL?: string;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* The register screen. Shows the form only when the server's public config says
|
|
302
|
+
* registration is `open` and email + password is enabled; otherwise it explains
|
|
303
|
+
* that registration is not open. The server enforces the policy either way.
|
|
304
|
+
*/
|
|
305
|
+
export declare function SignUpScreen(props: SignUpScreenProps): import("react").JSX.Element;
|
|
306
|
+
type PublicAuthConfigState = {
|
|
307
|
+
status: "loading";
|
|
308
|
+
} | {
|
|
309
|
+
status: "error";
|
|
310
|
+
} | {
|
|
311
|
+
status: "ready";
|
|
312
|
+
config: PublicAuthConfig;
|
|
313
|
+
};
|
|
314
|
+
/** Loads `GET {basePath}/public-config` once per client. */
|
|
315
|
+
export declare function usePublicAuthConfig(client: InbisAuthClient): PublicAuthConfigState;
|
|
316
|
+
interface VerifyEmailScreenProps {
|
|
317
|
+
client: InbisAuthClient;
|
|
318
|
+
/** Called to go to the sign-in screen. */
|
|
319
|
+
onSignIn: () => void;
|
|
320
|
+
/**
|
|
321
|
+
* Where verification links return to: this screen, with `verified=1`.
|
|
322
|
+
* Relative path or trusted-origin URL. Default `/verify-email?verified=1` on this origin.
|
|
323
|
+
* A failed link comes back with `&error=TOKEN_EXPIRED|INVALID_TOKEN`.
|
|
324
|
+
*/
|
|
325
|
+
callbackURL?: string;
|
|
326
|
+
/** The page's query string. Defaults to `window.location.search`. */
|
|
327
|
+
search?: string;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Where verification links land, and where to send users after sign-up
|
|
331
|
+
* (`?sent=1`: "check your inbox"). Shows success (`?verified=1`), explains an
|
|
332
|
+
* expired or invalid link (`?error=…`), and otherwise lets the user ask for a
|
|
333
|
+
* new link. Requesting a link answers the same whether or not the address has
|
|
334
|
+
* an account.
|
|
335
|
+
*/
|
|
336
|
+
export declare function VerifyEmailScreen(props: VerifyEmailScreenProps): import("react").JSX.Element;
|
|
337
|
+
interface TwoFactorChallengeProps {
|
|
338
|
+
client: InbisAuthClient;
|
|
339
|
+
/** Called once the second factor is accepted and the session exists. */
|
|
340
|
+
onVerified: () => void;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* The second step of a password sign-in for a user with 2FA on: a code from
|
|
344
|
+
* the authenticator app, or a single-use backup code instead. Rendered by
|
|
345
|
+
* `SignInScreen` after the password is accepted; usable on its own too.
|
|
346
|
+
*/
|
|
347
|
+
export declare function TwoFactorChallenge({ client, onVerified }: TwoFactorChallengeProps): import("react").JSX.Element;
|
|
348
|
+
interface TwoFactorSettingsScreenProps {
|
|
349
|
+
client: InbisAuthClient;
|
|
350
|
+
/**
|
|
351
|
+
* Called when a change needs a recent sign-in (`SESSION_NOT_FRESH`) and the
|
|
352
|
+
* user chooses to sign in again, e.g. sign out and navigate to sign-in.
|
|
353
|
+
* Without it, the screen only explains why the change was refused.
|
|
354
|
+
*/
|
|
355
|
+
onReauthenticate?: () => void;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Account security: shows whether 2FA is on and lets the signed-in user set up
|
|
359
|
+
* an authenticator app (QR code, verify, backup codes), regenerate the backup
|
|
360
|
+
* codes, or turn 2FA off. Every change asks for the current password, and
|
|
361
|
+
* regenerating or turning off also needs a recent sign-in.
|
|
362
|
+
*/
|
|
363
|
+
export declare function TwoFactorSettingsScreen(props: TwoFactorSettingsScreenProps): import("react").JSX.Element;
|
|
364
|
+
interface DeleteAccountScreenProps {
|
|
365
|
+
client: InbisAuthClient;
|
|
366
|
+
/** Called from the confirmation shown after the deletion (the session has ended). */
|
|
367
|
+
onDeleted: () => void;
|
|
368
|
+
/** Called when the user backs out. */
|
|
369
|
+
onCancel: () => void;
|
|
370
|
+
/**
|
|
371
|
+
* Called when deletion needs a recent sign-in (`SESSION_NOT_FRESH`) and the
|
|
372
|
+
* user chooses to sign in again, e.g. sign out and navigate to sign-in.
|
|
373
|
+
*/
|
|
374
|
+
onReauthenticate?: () => void;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Lets the signed-in user delete their account, when the server's public
|
|
378
|
+
* config allows it. The user confirms by typing a word; the server also
|
|
379
|
+
* requires a recent sign-in and offers "sign in again" when it is not.
|
|
380
|
+
*/
|
|
381
|
+
export declare function DeleteAccountScreen(props: DeleteAccountScreenProps): import("react").JSX.Element;
|
|
382
|
+
interface SessionsScreenProps {
|
|
383
|
+
client: InbisAuthClient;
|
|
384
|
+
/**
|
|
385
|
+
* Called when signing a session out needs a recent sign-in
|
|
386
|
+
* (`SESSION_NOT_FRESH`) and the user chooses to sign in again, e.g. sign out
|
|
387
|
+
* and navigate to sign-in. Without it, the screen only explains why.
|
|
388
|
+
*/
|
|
389
|
+
onReauthenticate?: () => void;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* The signed-in user's sessions: where and when each one signed in, which one
|
|
393
|
+
* is this device, "Sign out" for each other session and "Sign out all other
|
|
394
|
+
* sessions". Signing out other sessions needs a recent sign-in; on
|
|
395
|
+
* `SESSION_NOT_FRESH` the screen explains why and offers "Sign in again".
|
|
396
|
+
*/
|
|
397
|
+
export declare function SessionsScreen(props: SessionsScreenProps): import("react").JSX.Element;
|
|
398
|
+
interface StepUpScreenProps {
|
|
399
|
+
client: InbisAuthClient;
|
|
400
|
+
/** What the protected route answered, from `accessRequirementOf(response)`. */
|
|
401
|
+
requirement: AccessRequirement;
|
|
402
|
+
/**
|
|
403
|
+
* Called once the current session satisfies the requirement (after setting
|
|
404
|
+
* up 2FA, which verifies the session): retry the protected page.
|
|
405
|
+
*/
|
|
406
|
+
onSatisfied: () => void;
|
|
407
|
+
/** Sign out and go to sign-in, where the next sign-in meets the policy. */
|
|
408
|
+
onSignInAgain: () => void;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Shown instead of a protected page that answered `403 TWO_FACTOR_REQUIRED`
|
|
412
|
+
* or `403 STEP_UP_REQUIRED` (ADR-3), so the page's content never renders:
|
|
413
|
+
* - 2FA required, none set up: sets up an authenticator app here (enrolment
|
|
414
|
+
* verifies the session), then `onSatisfied()`.
|
|
415
|
+
* - 2FA required, already set up: explains, and offers "Sign in again"
|
|
416
|
+
* (password, then the authenticator code).
|
|
417
|
+
* - Step-up: names the allowed methods and offers "Sign in again".
|
|
418
|
+
*/
|
|
419
|
+
export declare function StepUpScreen({ client, requirement, onSatisfied, onSignInAgain }: StepUpScreenProps): import("react").JSX.Element;
|
|
420
|
+
type PhoneOtp = NonNullable<PublicAuthConfig["methods"]["phoneOtp"]>;
|
|
421
|
+
interface PhoneOtpVerificationProps {
|
|
422
|
+
client: InbisAuthClient;
|
|
423
|
+
/** The number the code was sent to (as entered; the server normalises it). */
|
|
424
|
+
phoneNumber: string;
|
|
425
|
+
/** Code length and lifetime, from the public config's `methods.phoneOtp`. */
|
|
426
|
+
phoneOtp: PhoneOtp;
|
|
427
|
+
/** Called once the code is accepted and the session exists. */
|
|
428
|
+
onSignedIn: () => void;
|
|
429
|
+
/** Asks for another code. */
|
|
430
|
+
onResend: () => void;
|
|
431
|
+
/** Goes back to entering a number; hidden when absent (e.g. a fixed, invited number). */
|
|
432
|
+
onChangeNumber?: () => void;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* The second step of phone sign-in: the code from the SMS. Better Auth allows
|
|
436
|
+
* a few attempts per code; after that, or once it expires, the user sends a new one.
|
|
437
|
+
*/
|
|
438
|
+
export declare function PhoneOtpVerification({ client, phoneNumber, phoneOtp, onSignedIn, onResend, onChangeNumber }: PhoneOtpVerificationProps): import("react").JSX.Element;
|
|
439
|
+
interface PhoneSignInProps {
|
|
440
|
+
client: InbisAuthClient;
|
|
441
|
+
phoneOtp: PhoneOtp;
|
|
442
|
+
onSignedIn: () => void;
|
|
443
|
+
/** A number fixed by the caller (an invited number): no number field, sent as is. */
|
|
444
|
+
phoneNumber?: string;
|
|
445
|
+
/** The public config's `captcha`: when it covers `phoneOtp`, each code request needs a Turnstile token. */
|
|
446
|
+
captcha?: PublicAuthConfig["captcha"];
|
|
447
|
+
}
|
|
448
|
+
/** Phone sign-in as a form: the number (unless fixed), then the code. */
|
|
449
|
+
export declare function PhoneSignIn({ client, phoneOtp, onSignedIn, phoneNumber: fixed, captcha }: PhoneSignInProps): import("react").JSX.Element;
|
|
450
|
+
interface PhoneSignInScreenProps {
|
|
451
|
+
client: InbisAuthClient;
|
|
452
|
+
/** Called after a successful sign-in, e.g. to navigate to the protected area. */
|
|
453
|
+
onSignedIn: () => void;
|
|
454
|
+
/** Back to the other sign-in methods (e.g. the `SignInScreen`); hidden when absent. */
|
|
455
|
+
onSignIn?: () => void;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Phone sign-in screen (when the public config has `methods.phoneOtp`): enter
|
|
459
|
+
* an international number, receive a code by SMS, enter it. A number's first
|
|
460
|
+
* sign-in creates the account when registration is open or the number was
|
|
461
|
+
* invited; the screen never reveals whether a number has an account.
|
|
462
|
+
*/
|
|
463
|
+
export declare function PhoneSignInScreen({ client, onSignedIn, onSignIn }: PhoneSignInScreenProps): import("react").JSX.Element;
|
|
464
|
+
interface TurnstileApi {
|
|
465
|
+
render(container: HTMLElement, options: {
|
|
466
|
+
sitekey: string;
|
|
467
|
+
action?: string;
|
|
468
|
+
callback: (token: string) => void;
|
|
469
|
+
"expired-callback": () => void;
|
|
470
|
+
"error-callback": () => void;
|
|
471
|
+
}): string;
|
|
472
|
+
reset(widgetId: string): void;
|
|
473
|
+
remove(widgetId: string): void;
|
|
474
|
+
}
|
|
475
|
+
declare global {
|
|
476
|
+
interface Window {
|
|
477
|
+
turnstile?: TurnstileApi;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
interface TurnstileState {
|
|
481
|
+
/** The widget to render inside the form; `null` when no check is needed. */
|
|
482
|
+
widget: ReactNode;
|
|
483
|
+
/** Whether the form may submit: no check is needed, or a token is ready. */
|
|
484
|
+
ready: boolean;
|
|
485
|
+
/** The token to send (`undefined` when no check is needed). */
|
|
486
|
+
token: string | undefined;
|
|
487
|
+
/** Call after every submit: a token works once. */
|
|
488
|
+
reset: () => void;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* The Turnstile check for one form, when the public config's `captcha` covers
|
|
492
|
+
* `action`; otherwise a no-op state.
|
|
493
|
+
*/
|
|
494
|
+
export declare function useTurnstile(captcha: PublicAuthConfig["captcha"], action: CaptchaAction): TurnstileState;
|
|
495
|
+
export type { AcceptInvitationScreenProps, DeleteAccountScreenProps, ForgotPasswordScreenProps, MessageKey, PhoneOtpVerificationProps, PhoneSignInProps, PhoneSignInScreenProps, PublicAuthConfigState, ResetPasswordScreenProps, SessionsScreenProps, SignInScreenProps, SignUpScreenProps, StepUpScreenProps, TurnstileState, TwoFactorChallengeProps, TwoFactorSettingsScreenProps, VerifyEmailScreenProps };
|
|
496
|
+
//# sourceMappingURL=index.d.ts.map
|