diff --git a/.changeset/canonical-auth-route-names.md b/.changeset/canonical-auth-route-names.md deleted file mode 100644 index 2d1b463..0000000 --- a/.changeset/canonical-auth-route-names.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -'@seamless-auth/react': minor ---- - -Rename the bundled `AuthRoutes` paths to a consistent kebab-case set. The previous mixed-case paths are no longer served. - -| Old path | New path | -| ------------------ | ------------------- | -| `/passKeyLogin` | `/passkey-login` | -| `/verifyPhoneOTP` | `/verify-phone-otp` | -| `/verifyEmailOTP` | `/verify-email-otp` | -| `/registerPasskey` | `/register-passkey` | -| `/magiclinks-sent` | `/magic-link-sent` | - -BREAKING: anything linking directly to an old path now falls through to `/login`. Apps that only render `AuthRoutes` and rely on its internal navigation need no change. - -`/login`, `/verify-magiclink`, and `/oauth/callback` are unchanged. The latter two are fixed by contracts outside this package: the auth API builds the magic-link URL when it sends the email, and the OAuth callback is registered with providers as an allowed redirect URI. diff --git a/.changeset/client-result-objects.md b/.changeset/client-result-objects.md deleted file mode 100644 index 4fad87e..0000000 --- a/.changeset/client-result-objects.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -'@seamless-auth/react': minor ---- - -Standardize the headless client on a single result convention. Every request method now resolves to a `SeamlessAuthResult`: - -```ts -const { data, error } = await authClient.getCurrentUser(); -if (error) { - setMessage(error.message); - return; -} -setUser(data.user); -``` - -BREAKING: the client previously mixed three conventions. Most methods returned a raw `Response`, a few returned a result object with a `success` flag, and `listOAuthProviders` and `startOAuthLogin` threw a generic `Error` that discarded the server's detail. All of them now return `{ data, error }`. - -What this changes for callers: - -- Methods that returned `Response`: replace `response.ok` and `await response.json()` with `error` and `data`. The response body is now parsed and typed, so `CurrentUserResult`, `LoginStartResult`, `OrganizationsResult`, `TotpStatus`, and friends are real return types instead of types you cast to by hand. -- `loginWithPasskey`, `registerPasskey`, and the step-up verifiers: replace `result.success` with `!result.error`. Their payloads moved to `data`, so `result.prf` becomes `result.data.prf`, and `result.credentialId` becomes `result.data.credentialId`. -- `listOAuthProviders` and `startOAuthLogin`: these no longer throw. Check `error` instead of wrapping the call in `try`/`catch`. -- Removed types: `PasskeyLoginResult`, `PasskeyLoginWithPrfResult`, `PasskeyRegistrationResult`, `StepUpVerificationResult`, and `StepUpWithPasskeyPrfResult`. Replaced by `PasskeyLoginData`, `PasskeyRegistrationData`, `StepUpPrfData`, and the shared `SeamlessAuthResult`. - -Failures no longer throw for HTTP or transport errors, so an expected auth outcome such as a wrong code or an expired link is a value rather than an exception. `SeamlessAuthError` carries the server `message`, HTTP `status`, and parsed `body`, and a transport failure is reported with `status` `0`. diff --git a/.changeset/credential-response-types.md b/.changeset/credential-response-types.md deleted file mode 100644 index 54dc9d8..0000000 --- a/.changeset/credential-response-types.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'@seamless-auth/react': patch ---- - -Correct the response types for credential and session endpoints, checked against the API's response schemas. - -`updateCredential()` on `useAuth()` now returns the credential itself. It previously returned the `{ message, credential }` wrapper while declaring `Promise`, so callers reading a field such as `friendlyName` got `undefined`, and the cast hid the mismatch from TypeScript. - -Client return types corrected to match the server schemas: - -- `updateCredential` returns `CredentialUpdateResult` (`{ message, credential }`), replacing `CredentialMutationResult`, which modelled `credential` as optional when the API always sends it -- `deleteCredential` returns `MessageResult` -- `logout`, `logoutAllSessions`, and `deleteUser` return `MessageResult` rather than being typed as returning no body - -`CredentialMutationResult` is removed and replaced by `CredentialUpdateResult`. diff --git a/.changeset/custom-ui-docs.md b/.changeset/custom-ui-docs.md deleted file mode 100644 index 34fa865..0000000 --- a/.changeset/custom-ui-docs.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@seamless-auth/react': patch ---- - -Expand the custom UI documentation with worked examples for registration, OTP and magic-link continuation, and credential management. The OTP and magic-link section documents that `requestMagicLink()`, `requestLoginEmailOtp()`, and `requestLoginPhoneOtp()` take no identifier and depend on server-side state from a preceding `login()` call, which is not evident from their signatures. - -Also corrects README references left stale by the result-object change: the `useAuth()` signature block, the step-up examples that used `result.success`, and the PRF example that read `result.prf`. diff --git a/.changeset/dts-relative-paths.md b/.changeset/dts-relative-paths.md deleted file mode 100644 index 1900fd7..0000000 --- a/.changeset/dts-relative-paths.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@seamless-auth/react': patch ---- - -Emit relative import paths in the published type declarations. The build kept the -`@/*` tsconfig path aliases in the generated `.d.ts` files, which no consumer can -resolve, so every downstream project silently saw the SDK's public surface as -`any` (masked by `skipLibCheck`). A `tsc-alias` post-build step now rewrites the -aliases to relative paths, restoring real types for consumers. diff --git a/.changeset/email-only-registration.md b/.changeset/email-only-registration.md deleted file mode 100644 index ed8b06d..0000000 --- a/.changeset/email-only-registration.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@seamless-auth/react': patch ---- - -The bundled registration screen now asks for an email only. It previously required a phone number as well, but registration only needs an email (the API treats phone as optional and it can be added and verified later), so the field was a mismatch that blocked email-only sign-up. - -`RegisterInput.phone` is now optional (`phone?: string | null`) and is only sent when a caller provides one, so headless consumers can still submit a phone at registration if they want to. diff --git a/.changeset/encode-magic-link-token.md b/.changeset/encode-magic-link-token.md deleted file mode 100644 index 9596001..0000000 --- a/.changeset/encode-magic-link-token.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@seamless-auth/react': patch ---- - -Encode the magic-link token before placing it in the request path. The token is read from a link's query string, so it is untrusted input, and leaving it unencoded let path segments inside it redirect the request to a different endpoint under `/auth`. Because requests are sent with credentials, a crafted link could cause a signed-in user's browser to call unintended endpoints, including ones that send an SMS or email. Affects 0.4.0 and earlier. diff --git a/.changeset/fetchwithauth-request-hygiene.md b/.changeset/fetchwithauth-request-hygiene.md deleted file mode 100644 index 5111b06..0000000 --- a/.changeset/fetchwithauth-request-hygiene.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@seamless-auth/react': patch ---- - -Improve request hygiene in the shared auth fetch helper. It no longer sends a JSON `Content-Type` on bodyless requests (some proxies reject a GET that advertises a request content type), and it no longer logs a `console.warn` on every non-ok response. Callers already inspect `response.ok`, and caller-provided headers still take precedence. diff --git a/.changeset/magic-link-poll-204.md b/.changeset/magic-link-poll-204.md deleted file mode 100644 index c4a8fd0..0000000 --- a/.changeset/magic-link-poll-204.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@seamless-auth/react': patch ---- - -Fix the magic-link waiting screen treating an unused link as completed. The poll endpoint answers `204` with no body until the emailed link is consumed, so checking only for a non-error response redirected on the first poll, before the user clicked the link. It now waits for the endpoint to report success. diff --git a/.changeset/magic-link-post.md b/.changeset/magic-link-post.md deleted file mode 100644 index f6c5c94..0000000 --- a/.changeset/magic-link-post.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@seamless-auth/react': minor ---- - -Request magic links over `POST /auth/magic-link` instead of `GET`. - -`GET /auth/magic-link` was a state-changing route reachable as a simple cross-site request, so an `` on any page could trigger unbounded magic-link emails to a signed-in user. The Express adapter removed the GET route and replaced it with POST. - -The request now carries an empty JSON body. The adapter ignores it, but the resulting JSON content type forces a CORS preflight, which is what actually keeps the route from being reachable cross-site. A bodyless POST would still be a simple request. - -BREAKING: this release requires an adapter with the POST route (`@seamless-auth/express` 0.9.0 or later). Against an older adapter, `requestMagicLink` gets a 404. Callers of `requestMagicLink()` need no code change. diff --git a/.changeset/magic-link-refresh-session.md b/.changeset/magic-link-refresh-session.md deleted file mode 100644 index 707d090..0000000 --- a/.changeset/magic-link-refresh-session.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@seamless-auth/react': patch ---- - -Fix magic-link verification so the tab that completes the link refreshes provider state and lands authenticated, instead of relying on another tab or a manual reload. diff --git a/.changeset/oauth-error-detail.md b/.changeset/oauth-error-detail.md deleted file mode 100644 index fd2d2ef..0000000 --- a/.changeset/oauth-error-detail.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@seamless-auth/react': minor ---- - -Surface the auth server's error detail when an OAuth callback fails. `finishOAuthLogin()` previously discarded the response body and threw a generic `Error('Failed to finish OAuth login')`, so consuming apps could not tell users what went wrong. - -It now throws a `SeamlessAuthError` carrying the server message, the HTTP `status`, and the parsed `body`. `SeamlessAuthError` is exported so callers can narrow with `instanceof` and map known failures to their own messaging. A body that is empty or not JSON is handled gracefully and falls back to the previous generic message. - -Callers matching on the exact string `'Failed to finish OAuth login'` should switch to inspecting `status` or `body`. diff --git a/.changeset/oauth-redirect-basename.md b/.changeset/oauth-redirect-basename.md deleted file mode 100644 index ff847cd..0000000 --- a/.changeset/oauth-redirect-basename.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@seamless-auth/react': patch ---- - -Fix the bundled OAuth provider buttons so the callback redirect URI respects the router basename. Apps mounted under a non-root basename (for example `/app`) now send `/app/oauth/callback` instead of `/oauth/callback`, which previously failed redirect URI allowlisting or landed on a route that did not exist. Apps mounted at the root are unaffected. diff --git a/.changeset/organization-response-types.md b/.changeset/organization-response-types.md deleted file mode 100644 index 80902cd..0000000 --- a/.changeset/organization-response-types.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@seamless-auth/react': patch ---- - -Correct the organization response types, checked against the API's declared response schemas. - -- `addOrganizationMember` and `updateOrganizationMember` return `OrganizationMembershipResult` (`{ membership }`). They were typed as returning a members list, so reading `.members` gave `undefined`. -- `removeOrganizationMember` returns `MessageResult`, not a members list. -- `switchOrganization` returns `OrganizationSwitchResult` (`{ message, organizationId, organization }`), which describes what the endpoint actually sends. - -`OrganizationMembershipResult` and `OrganizationSwitchResult` are new exports. diff --git a/.changeset/otp-generate-post.md b/.changeset/otp-generate-post.md deleted file mode 100644 index 0b2b961..0000000 --- a/.changeset/otp-generate-post.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@seamless-auth/react': minor ---- - -Request OTP generation over `POST` instead of `GET`. - -`requestPhoneOtp`, `requestEmailOtp`, `requestLoginPhoneOtp`, and `requestLoginEmailOtp` each caused an SMS or email to be sent, so they were state changing. Sent as a `GET`, they were simple cross-site requests, so an `` on any page could trigger unbounded OTP messages to a signed-in user. They now POST an empty JSON body, which forces a CORS preflight and closes the vector. This mirrors the `requestMagicLink` change. - -BREAKING: this requires an adapter serving these routes over POST (`@seamless-auth/express` with the OTP POST change, released alongside this). Against an older adapter the four request methods get a 404. Callers need no code change. diff --git a/.changeset/parse-user-agent-browsers.md b/.changeset/parse-user-agent-browsers.md deleted file mode 100644 index 2878774..0000000 --- a/.changeset/parse-user-agent-browsers.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@seamless-auth/react': patch ---- - -Improve browser detection used for passkey device labels. Opera, Vivaldi, Samsung Internet, and Brave are now identified instead of being reported as Chrome, and Chrome and Firefox on iOS are recognized through their `CriOS` and `FxiOS` tokens. Chromium derivatives are now matched before the generic chrome token, so the result no longer depends on check ordering. - -Brave is detected through `navigator.brave` because it ships a user agent identical to Chrome's. Arc exposes no distinguishing marker and is still reported as chrome. diff --git a/.changeset/provider-result-convention.md b/.changeset/provider-result-convention.md deleted file mode 100644 index 706b488..0000000 --- a/.changeset/provider-result-convention.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -'@seamless-auth/react': minor ---- - -Unify `useAuth()` on the same result convention as the headless client. Every provider helper now returns `SeamlessAuthResult` and none of them throw, so the whole SDK reports failure one way. - -```tsx -const { error } = await updateCredential({ ...credential, friendlyName: 'Work laptop' }); -if (error) { - setMessage(error.message); -} -``` - -BREAKING: `useAuth()` previously mixed four styles. Seven helpers threw, four returned a result, `handlePasskeyLogin` returned a boolean, and `refreshStepUpStatus` returned `StepUpStatus | null`. - -Migration: - -- `deleteUser`, `updateCredential`, `deleteCredential`, `switchOrganization`, `listOAuthProviders`, `startOAuthLogin`, and `finishOAuthLogin` no longer throw. Replace `try`/`catch` with an `error` check. -- `handlePasskeyLogin` returns a result instead of a boolean. Replace `if (await handlePasskeyLogin())` with `if (!(await handlePasskeyLogin()).error)`. -- `refreshStepUpStatus` returns a result instead of `StepUpStatus | null`. Read `data` instead of the return value directly. -- `updateCredential` returns the credential under `data`. -- `logout`, `logoutAllSessions`, and `refreshSession` now return a result. Existing callers that ignore the return value keep working. - -Helpers that mutate provider state still do so only when the call succeeds. diff --git a/.changeset/react-router-security-floor.md b/.changeset/react-router-security-floor.md deleted file mode 100644 index 1da06ab..0000000 --- a/.changeset/react-router-security-floor.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@seamless-auth/react': patch ---- - -Raise the `react-router-dom` v7 peer floor to `^7.15.1` to steer adopters off versions affected by a high-severity advisory (GHSA in react-router's vendored turbo-stream, affecting react-router 7.0.0 through 7.15.0). React Router 6 is unaffected, so the `^6.4.0` range is unchanged. - -This is a peer range change, so adopters on a vulnerable v7 (7.0.0 to 7.15.0) will see an npm warning until they upgrade to 7.15.1 or later. No SDK code change and no runtime behavior change. diff --git a/.changeset/remove-passkey-mfa-required.md b/.changeset/remove-passkey-mfa-required.md deleted file mode 100644 index d45bc79..0000000 --- a/.changeset/remove-passkey-mfa-required.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@seamless-auth/react': minor ---- - -Remove the unused `mfaRequired` field from `PasskeyLoginResult` (and the inherited `PasskeyLoginWithPrfResult`). The backend never gated passkey login on a second factor, so the field was always `false` and the related fail-closed path in `handlePasskeyLogin` was dead code. - -BREAKING: consumers reading `result.mfaRequired` from `loginWithPasskey()` should remove that check. A successful passkey login is now indicated solely by `result.success`. diff --git a/.changeset/ssr-safe-platform-authenticator.md b/.changeset/ssr-safe-platform-authenticator.md deleted file mode 100644 index f3a9d91..0000000 --- a/.changeset/ssr-safe-platform-authenticator.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@seamless-auth/react': patch ---- - -Make the platform authenticator capability check safe to evaluate in a server environment. `isPlatformAuthenticatorAvailable` previously read `window` without a guard and threw a `ReferenceError` during server-side rendering. It now returns `false` when there is no `window`, matching the guard already used by the WebAuthn availability check. diff --git a/CHANGELOG.md b/CHANGELOG.md index 36bdf4f..9695016 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,139 @@ # @seamless-auth/react +## 0.5.0 + +### Minor Changes + +- e591d09: Rename the bundled `AuthRoutes` paths to a consistent kebab-case set. The previous mixed-case paths are no longer served. + + | Old path | New path | + | ------------------ | ------------------- | + | `/passKeyLogin` | `/passkey-login` | + | `/verifyPhoneOTP` | `/verify-phone-otp` | + | `/verifyEmailOTP` | `/verify-email-otp` | + | `/registerPasskey` | `/register-passkey` | + | `/magiclinks-sent` | `/magic-link-sent` | + + BREAKING: anything linking directly to an old path now falls through to `/login`. Apps that only render `AuthRoutes` and rely on its internal navigation need no change. + + `/login`, `/verify-magiclink`, and `/oauth/callback` are unchanged. The latter two are fixed by contracts outside this package: the auth API builds the magic-link URL when it sends the email, and the OAuth callback is registered with providers as an allowed redirect URI. + +- 7931828: Standardize the headless client on a single result convention. Every request method now resolves to a `SeamlessAuthResult`: + + ```ts + const { data, error } = await authClient.getCurrentUser(); + if (error) { + setMessage(error.message); + return; + } + setUser(data.user); + ``` + + BREAKING: the client previously mixed three conventions. Most methods returned a raw `Response`, a few returned a result object with a `success` flag, and `listOAuthProviders` and `startOAuthLogin` threw a generic `Error` that discarded the server's detail. All of them now return `{ data, error }`. + + What this changes for callers: + - Methods that returned `Response`: replace `response.ok` and `await response.json()` with `error` and `data`. The response body is now parsed and typed, so `CurrentUserResult`, `LoginStartResult`, `OrganizationsResult`, `TotpStatus`, and friends are real return types instead of types you cast to by hand. + - `loginWithPasskey`, `registerPasskey`, and the step-up verifiers: replace `result.success` with `!result.error`. Their payloads moved to `data`, so `result.prf` becomes `result.data.prf`, and `result.credentialId` becomes `result.data.credentialId`. + - `listOAuthProviders` and `startOAuthLogin`: these no longer throw. Check `error` instead of wrapping the call in `try`/`catch`. + - Removed types: `PasskeyLoginResult`, `PasskeyLoginWithPrfResult`, `PasskeyRegistrationResult`, `StepUpVerificationResult`, and `StepUpWithPasskeyPrfResult`. Replaced by `PasskeyLoginData`, `PasskeyRegistrationData`, `StepUpPrfData`, and the shared `SeamlessAuthResult`. + + Failures no longer throw for HTTP or transport errors, so an expected auth outcome such as a wrong code or an expired link is a value rather than an exception. `SeamlessAuthError` carries the server `message`, HTTP `status`, and parsed `body`, and a transport failure is reported with `status` `0`. + +- 02c99f3: Request magic links over `POST /auth/magic-link` instead of `GET`. + + `GET /auth/magic-link` was a state-changing route reachable as a simple cross-site request, so an `` on any page could trigger unbounded magic-link emails to a signed-in user. The Express adapter removed the GET route and replaced it with POST. + + The request now carries an empty JSON body. The adapter ignores it, but the resulting JSON content type forces a CORS preflight, which is what actually keeps the route from being reachable cross-site. A bodyless POST would still be a simple request. + + BREAKING: this release requires an adapter with the POST route (`@seamless-auth/express` 0.9.0 or later). Against an older adapter, `requestMagicLink` gets a 404. Callers of `requestMagicLink()` need no code change. + +- 0a37241: Surface the auth server's error detail when an OAuth callback fails. `finishOAuthLogin()` previously discarded the response body and threw a generic `Error('Failed to finish OAuth login')`, so consuming apps could not tell users what went wrong. + + It now throws a `SeamlessAuthError` carrying the server message, the HTTP `status`, and the parsed `body`. `SeamlessAuthError` is exported so callers can narrow with `instanceof` and map known failures to their own messaging. A body that is empty or not JSON is handled gracefully and falls back to the previous generic message. + + Callers matching on the exact string `'Failed to finish OAuth login'` should switch to inspecting `status` or `body`. + +- 2dccbed: Request OTP generation over `POST` instead of `GET`. + + `requestPhoneOtp`, `requestEmailOtp`, `requestLoginPhoneOtp`, and `requestLoginEmailOtp` each caused an SMS or email to be sent, so they were state changing. Sent as a `GET`, they were simple cross-site requests, so an `` on any page could trigger unbounded OTP messages to a signed-in user. They now POST an empty JSON body, which forces a CORS preflight and closes the vector. This mirrors the `requestMagicLink` change. + + BREAKING: this requires an adapter serving these routes over POST (`@seamless-auth/express` with the OTP POST change, released alongside this). Against an older adapter the four request methods get a 404. Callers need no code change. + +- f0c4bd9: Unify `useAuth()` on the same result convention as the headless client. Every provider helper now returns `SeamlessAuthResult` and none of them throw, so the whole SDK reports failure one way. + + ```tsx + const { error } = await updateCredential({ + ...credential, + friendlyName: 'Work laptop', + }); + if (error) { + setMessage(error.message); + } + ``` + + BREAKING: `useAuth()` previously mixed four styles. Seven helpers threw, four returned a result, `handlePasskeyLogin` returned a boolean, and `refreshStepUpStatus` returned `StepUpStatus | null`. + + Migration: + - `deleteUser`, `updateCredential`, `deleteCredential`, `switchOrganization`, `listOAuthProviders`, `startOAuthLogin`, and `finishOAuthLogin` no longer throw. Replace `try`/`catch` with an `error` check. + - `handlePasskeyLogin` returns a result instead of a boolean. Replace `if (await handlePasskeyLogin())` with `if (!(await handlePasskeyLogin()).error)`. + - `refreshStepUpStatus` returns a result instead of `StepUpStatus | null`. Read `data` instead of the return value directly. + - `updateCredential` returns the credential under `data`. + - `logout`, `logoutAllSessions`, and `refreshSession` now return a result. Existing callers that ignore the return value keep working. + + Helpers that mutate provider state still do so only when the call succeeds. + +- dda670b: Remove the unused `mfaRequired` field from `PasskeyLoginResult` (and the inherited `PasskeyLoginWithPrfResult`). The backend never gated passkey login on a second factor, so the field was always `false` and the related fail-closed path in `handlePasskeyLogin` was dead code. + + BREAKING: consumers reading `result.mfaRequired` from `loginWithPasskey()` should remove that check. A successful passkey login is now indicated solely by `result.success`. + +### Patch Changes + +- d4e455d: Correct the response types for credential and session endpoints, checked against the API's response schemas. + + `updateCredential()` on `useAuth()` now returns the credential itself. It previously returned the `{ message, credential }` wrapper while declaring `Promise`, so callers reading a field such as `friendlyName` got `undefined`, and the cast hid the mismatch from TypeScript. + + Client return types corrected to match the server schemas: + - `updateCredential` returns `CredentialUpdateResult` (`{ message, credential }`), replacing `CredentialMutationResult`, which modelled `credential` as optional when the API always sends it + - `deleteCredential` returns `MessageResult` + - `logout`, `logoutAllSessions`, and `deleteUser` return `MessageResult` rather than being typed as returning no body + + `CredentialMutationResult` is removed and replaced by `CredentialUpdateResult`. + +- c17cf9c: Expand the custom UI documentation with worked examples for registration, OTP and magic-link continuation, and credential management. The OTP and magic-link section documents that `requestMagicLink()`, `requestLoginEmailOtp()`, and `requestLoginPhoneOtp()` take no identifier and depend on server-side state from a preceding `login()` call, which is not evident from their signatures. + + Also corrects README references left stale by the result-object change: the `useAuth()` signature block, the step-up examples that used `result.success`, and the PRF example that read `result.prf`. + +- 41ebb86: Emit relative import paths in the published type declarations. The build kept the + `@/*` tsconfig path aliases in the generated `.d.ts` files, which no consumer can + resolve, so every downstream project silently saw the SDK's public surface as + `any` (masked by `skipLibCheck`). A `tsc-alias` post-build step now rewrites the + aliases to relative paths, restoring real types for consumers. +- 597f6d2: The bundled registration screen now asks for an email only. It previously required a phone number as well, but registration only needs an email (the API treats phone as optional and it can be added and verified later), so the field was a mismatch that blocked email-only sign-up. + + `RegisterInput.phone` is now optional (`phone?: string | null`) and is only sent when a caller provides one, so headless consumers can still submit a phone at registration if they want to. + +- ede35a8: Encode the magic-link token before placing it in the request path. The token is read from a link's query string, so it is untrusted input, and leaving it unencoded let path segments inside it redirect the request to a different endpoint under `/auth`. Because requests are sent with credentials, a crafted link could cause a signed-in user's browser to call unintended endpoints, including ones that send an SMS or email. Affects 0.4.0 and earlier. +- 0f2d686: Improve request hygiene in the shared auth fetch helper. It no longer sends a JSON `Content-Type` on bodyless requests (some proxies reject a GET that advertises a request content type), and it no longer logs a `console.warn` on every non-ok response. Callers already inspect `response.ok`, and caller-provided headers still take precedence. +- 46f2a7c: Fix the magic-link waiting screen treating an unused link as completed. The poll endpoint answers `204` with no body until the emailed link is consumed, so checking only for a non-error response redirected on the first poll, before the user clicked the link. It now waits for the endpoint to report success. +- 0e208b4: Fix magic-link verification so the tab that completes the link refreshes provider state and lands authenticated, instead of relying on another tab or a manual reload. +- 90b7c14: Fix the bundled OAuth provider buttons so the callback redirect URI respects the router basename. Apps mounted under a non-root basename (for example `/app`) now send `/app/oauth/callback` instead of `/oauth/callback`, which previously failed redirect URI allowlisting or landed on a route that did not exist. Apps mounted at the root are unaffected. +- 1c4f4d2: Correct the organization response types, checked against the API's declared response schemas. + - `addOrganizationMember` and `updateOrganizationMember` return `OrganizationMembershipResult` (`{ membership }`). They were typed as returning a members list, so reading `.members` gave `undefined`. + - `removeOrganizationMember` returns `MessageResult`, not a members list. + - `switchOrganization` returns `OrganizationSwitchResult` (`{ message, organizationId, organization }`), which describes what the endpoint actually sends. + + `OrganizationMembershipResult` and `OrganizationSwitchResult` are new exports. + +- aa2fda8: Improve browser detection used for passkey device labels. Opera, Vivaldi, Samsung Internet, and Brave are now identified instead of being reported as Chrome, and Chrome and Firefox on iOS are recognized through their `CriOS` and `FxiOS` tokens. Chromium derivatives are now matched before the generic chrome token, so the result no longer depends on check ordering. + + Brave is detected through `navigator.brave` because it ships a user agent identical to Chrome's. Arc exposes no distinguishing marker and is still reported as chrome. + +- a9580e8: Raise the `react-router-dom` v7 peer floor to `^7.15.1` to steer adopters off versions affected by a high-severity advisory (GHSA in react-router's vendored turbo-stream, affecting react-router 7.0.0 through 7.15.0). React Router 6 is unaffected, so the `^6.4.0` range is unchanged. + + This is a peer range change, so adopters on a vulnerable v7 (7.0.0 to 7.15.0) will see an npm warning until they upgrade to 7.15.1 or later. No SDK code change and no runtime behavior change. + +- 5d865ab: Make the platform authenticator capability check safe to evaluate in a server environment. `isPlatformAuthenticatorAvailable` previously read `window` without a guard and threw a `ReferenceError` during server-side rendering. It now returns `false` when there is no `window`, matching the guard already used by the WebAuthn availability check. + ## 0.4.0 ### Minor Changes diff --git a/package.json b/package.json index 62199a6..0b80ed2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamless-auth/react", - "version": "0.4.0", + "version": "0.5.0", "description": "A drop-in authentication solution for modern React applications.", "type": "module", "exports": {