@dloizides/auth-web 1.9.1 → 1.10.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 +53 -0
- package/dist/index.d.mts +92 -1
- package/dist/index.d.ts +92 -1
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +38 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,58 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.10.0 (2026-07-17)
|
|
4
|
+
|
|
5
|
+
Additive release adding the shared **logout sequencer**. Purely additive — every
|
|
6
|
+
1.9.x export is unchanged, no component behaviour changes, and apps that do not
|
|
7
|
+
import `performBffLogout` are byte-identical.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`performBffLogout({ client, onClearSession?, onRedirect?, onError?, navigateToIdp? })`**
|
|
12
|
+
— the shared logout sequence, extracted because all six consuming apps
|
|
13
|
+
hand-rolled it in their own `AuthProvider` and the orderings disagreed. Runs:
|
|
14
|
+
1. `onClearSession()` — local state first, so the UI reflects the sign-out
|
|
15
|
+
immediately.
|
|
16
|
+
2. `await client.logout({ redirect: false })` — lets the `POST /bff/logout`
|
|
17
|
+
SETTLE, and takes the IdP logout URL rather than letting the client navigate
|
|
18
|
+
mid-sequence.
|
|
19
|
+
3. Navigates exactly once, last: to the IdP logout URL when the BFF returned
|
|
20
|
+
one (ending the IdP browser session), otherwise `onRedirect()`. When it
|
|
21
|
+
navigates to the IdP it deliberately does NOT also call `onRedirect` — two
|
|
22
|
+
competing navigations would race, and the IdP round-trip already returns the
|
|
23
|
+
user to the app.
|
|
24
|
+
|
|
25
|
+
Never rejects; always navigates, even when the BFF logout fails (a down BFF
|
|
26
|
+
must not trap the user on a page they asked to leave).
|
|
27
|
+
- **`BffLogoutClient`** / **`PerformBffLogoutOptions`** types.
|
|
28
|
+
|
|
29
|
+
### Why
|
|
30
|
+
|
|
31
|
+
`zygos-web` and `agora-web` navigated FIRST and called the BFF second:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
redirectTo('/(auth)/login'); // → window.location.replace() → document unload
|
|
35
|
+
await bffAuthClient.logout(); // races the unload — may never be sent
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
That is wrong twice over:
|
|
39
|
+
|
|
40
|
+
1. **The BFF call can be cancelled.** A document navigation kills in-flight
|
|
41
|
+
fetches, so `POST /bff/logout` may never reach the server: the SPA looks
|
|
42
|
+
logged out while the httpOnly session cookie stays alive. The `catch` logged
|
|
43
|
+
it "non-fatal", so the defect was silent.
|
|
44
|
+
2. **The IdP session survives.** `BffAuthClient.logout()` ends the identity
|
|
45
|
+
provider's browser session with a top-level navigation to its RP-initiated
|
|
46
|
+
logout URL (only a real navigation carries the IdP's own cookie — a
|
|
47
|
+
back-channel POST cannot clear it). An app that navigates to its own login
|
|
48
|
+
route first pre-empts that navigation, so the IdP still considers the user
|
|
49
|
+
signed in and the next authorize round-trip silently re-authenticates them —
|
|
50
|
+
the "sign-out that signs you back in" bug the client's own docs warn about.
|
|
51
|
+
|
|
52
|
+
Synthetic tests that stub the client never reproduce either failure (there is no
|
|
53
|
+
real navigation to cancel the request), which is why they survived. The helper
|
|
54
|
+
owns the sequence so neither can be reintroduced per-app.
|
|
55
|
+
|
|
3
56
|
## 1.8.0 (2026-07-09)
|
|
4
57
|
|
|
5
58
|
Additive release for the Move 2b passkey in-app rebuild — the passkey ceremony
|
package/dist/index.d.mts
CHANGED
|
@@ -1925,6 +1925,97 @@ declare function resolvePostLoginRoute(user: BffUser, table: RoleRouteTable): st
|
|
|
1925
1925
|
*/
|
|
1926
1926
|
declare function resolveAccessibleRoutes(user: BffUser, table: RoleRouteTable): RoleRoute[];
|
|
1927
1927
|
|
|
1928
|
+
/**
|
|
1929
|
+
* `performBffLogout` — the shared logout sequencer.
|
|
1930
|
+
*
|
|
1931
|
+
* Every app hand-rolled its own logout ordering inside its `AuthProvider`, and
|
|
1932
|
+
* the orderings disagreed. Two of them (zygos-web, agora-web) navigated FIRST
|
|
1933
|
+
* and called the BFF second:
|
|
1934
|
+
*
|
|
1935
|
+
* ```ts
|
|
1936
|
+
* redirectTo('/(auth)/login'); // window.location.replace() → document unload
|
|
1937
|
+
* await bffAuthClient.logout(); // races the unload — may never be sent
|
|
1938
|
+
* ```
|
|
1939
|
+
*
|
|
1940
|
+
* That is wrong twice over:
|
|
1941
|
+
*
|
|
1942
|
+
* 1. **The BFF call can be cancelled.** A document navigation kills in-flight
|
|
1943
|
+
* fetches, so `POST /bff/logout` may never reach the server: the SPA looks
|
|
1944
|
+
* logged out while the httpOnly session cookie stays ALIVE. The `catch`
|
|
1945
|
+
* logged it "non-fatal", so the defect was silent.
|
|
1946
|
+
* 2. **The IdP session survives.** `BffAuthClient.logout()` ends the *identity
|
|
1947
|
+
* provider's* browser session by doing a top-level navigation to the IdP's
|
|
1948
|
+
* RP-initiated logout URL (only a real navigation carries the IdP's own
|
|
1949
|
+
* cookie — a back-channel POST cannot clear it). An app that navigates to
|
|
1950
|
+
* its own login route first pre-empts that navigation, so the IdP still
|
|
1951
|
+
* considers the user signed in and the next authorize round-trip silently
|
|
1952
|
+
* re-authenticates them. That is the "sign-out that signs you back in" bug
|
|
1953
|
+
* the client's own docs warn about.
|
|
1954
|
+
*
|
|
1955
|
+
* This helper owns the whole sequence so neither can be reintroduced per-app:
|
|
1956
|
+
*
|
|
1957
|
+
* 1. `onClearSession()` — drop local state immediately so the UI reflects the
|
|
1958
|
+
* sign-out right away (perceived performance; no spinner needed).
|
|
1959
|
+
* 2. `await client.logout({ redirect: false })` — let the network call SETTLE,
|
|
1960
|
+
* and take the IdP logout URL rather than letting the client navigate mid-
|
|
1961
|
+
* sequence. Errors are captured, never rethrown.
|
|
1962
|
+
* 3. Navigate LAST, exactly once, when nothing is in flight to cancel:
|
|
1963
|
+
* - an IdP logout URL → a top-level navigation to the IdP, which ends that
|
|
1964
|
+
* session and bounces the user back. `onRedirect` is deliberately NOT
|
|
1965
|
+
* called: two competing navigations would race, and the IdP round-trip
|
|
1966
|
+
* already returns the user to the app.
|
|
1967
|
+
* - otherwise (ROPC / OTP / device-PIN logins never had an IdP cookie, so
|
|
1968
|
+
* the URL is `null`) → `onRedirect()`, the app's own login route.
|
|
1969
|
+
*
|
|
1970
|
+
* Step 3 runs even when step 2 rejects: a BFF that is down must not trap the
|
|
1971
|
+
* user on a page they asked to leave. The local session is already gone, so the
|
|
1972
|
+
* client-side sign-out holds regardless.
|
|
1973
|
+
*
|
|
1974
|
+
* Headless and framework-free: it takes callbacks, so an app can pair it with
|
|
1975
|
+
* Redux, a context, expo-router, or `window.location` without this package
|
|
1976
|
+
* knowing about any of them.
|
|
1977
|
+
*/
|
|
1978
|
+
/**
|
|
1979
|
+
* The only part of a `BffAuthClient` this helper needs. Structural, so it is
|
|
1980
|
+
* trivially fakeable in tests and does not couple to the full client surface.
|
|
1981
|
+
*/
|
|
1982
|
+
interface BffLogoutClient {
|
|
1983
|
+
/**
|
|
1984
|
+
* `POST /bff/logout` — ends the server-side session. With `redirect: false`
|
|
1985
|
+
* it returns the IdP's RP-initiated logout URL (or `null` when the session
|
|
1986
|
+
* never involved an IdP browser cookie) instead of navigating there itself.
|
|
1987
|
+
*/
|
|
1988
|
+
logout: (options?: {
|
|
1989
|
+
redirect?: boolean;
|
|
1990
|
+
}) => Promise<string | null>;
|
|
1991
|
+
}
|
|
1992
|
+
/** Callbacks + client for {@link performBffLogout}. */
|
|
1993
|
+
interface PerformBffLogoutOptions {
|
|
1994
|
+
/** The BFF auth client whose `logout()` ends the server-side session. */
|
|
1995
|
+
client: BffLogoutClient;
|
|
1996
|
+
/** Drop local session state. Runs BEFORE the network call. Optional. */
|
|
1997
|
+
onClearSession?: () => void;
|
|
1998
|
+
/**
|
|
1999
|
+
* Navigate to the app's logged-out route. Runs only AFTER the network call
|
|
2000
|
+
* settles, and only when there is no IdP logout URL to honour. Optional.
|
|
2001
|
+
*/
|
|
2002
|
+
onRedirect?: () => void;
|
|
2003
|
+
/** Observe a failed logout step (e.g. to log it). Never rethrown. Optional. */
|
|
2004
|
+
onError?: (error: Error) => void;
|
|
2005
|
+
/**
|
|
2006
|
+
* Test seam: perform the top-level navigation to the IdP logout URL. Defaults
|
|
2007
|
+
* to `window.location.assign`. Must be a real navigation — a `fetch` here
|
|
2008
|
+
* would not carry the IdP cookie and would silently fail to end the session.
|
|
2009
|
+
*/
|
|
2010
|
+
navigateToIdp?: (url: string) => void;
|
|
2011
|
+
}
|
|
2012
|
+
/**
|
|
2013
|
+
* End the session: clear locally, await the BFF, then navigate exactly once.
|
|
2014
|
+
*
|
|
2015
|
+
* Never rejects — callers may `void` it safely.
|
|
2016
|
+
*/
|
|
2017
|
+
declare function performBffLogout(options: Readonly<PerformBffLogoutOptions>): Promise<void>;
|
|
2018
|
+
|
|
1928
2019
|
/**
|
|
1929
2020
|
* The discrete ways a password can fail the shared client-side policy.
|
|
1930
2021
|
*
|
|
@@ -1970,4 +2061,4 @@ declare function validatePasswordPolicy(password: string): PasswordPolicyError[]
|
|
|
1970
2061
|
/** `true` when the password satisfies every policy rule. */
|
|
1971
2062
|
declare function isPasswordValid(password: string): boolean;
|
|
1972
2063
|
|
|
1973
|
-
export { AuthScreen, type AuthScreenProps, AuthTestIds, type AuthTheme, type AuthThemeColors, AuthThemeProvider, type AuthThemeProviderProps, type AuthThemeRadii, type AuthThemeSpacing, type AuthThemeTypography, BffAuthStatus, type BffDeviceState, type BffLoginConfig, type CreateBffAuthClientOptions, DEFAULT_AUTH_CARD_MAX_WIDTH, DEFAULT_DEVICE_PIN_ENROLL_LABELS, DEFAULT_DEVICE_PIN_SETTINGS_LABELS, DEFAULT_DEVICE_PIN_UNLOCK_LABELS, DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS, DEFAULT_FORGOT_PASSWORD_LABELS, DEFAULT_LOGIN_LABELS, DEFAULT_OTP_LABELS, DEFAULT_PASSKEY_LOGIN_LABELS, DEFAULT_PASSKEY_SETTINGS_LABELS, DEFAULT_PIN_LABELS, DEFAULT_PREFERRED_METHOD_HINT_LABELS, DEFAULT_PREFERRED_METHOD_SETTINGS_LABELS, DEFAULT_RESET_PASSWORD_LABELS, DEVICE_PIN_ALLOWED_DIGITS, DEVICE_PIN_DEFAULT_DIGITS, type DevicePinCapableClient, type DevicePinDisableCapableClient, type DevicePinEnrollCapableClient, DevicePinEnrollErrorKey, DevicePinEnrollForm, type DevicePinEnrollFormProps, type DevicePinEnrollLabels, type DevicePinEnrollResult, DevicePinErrorKey, DevicePinInput, type DevicePinInputProps, DevicePinLengthPicker, type DevicePinLengthPickerProps, DevicePinOffer, type DevicePinOfferProps, DevicePinSettingsCard, type DevicePinSettingsCardProps, type DevicePinSettingsLabels, type DevicePinUnlockCapableClient, type DevicePinUnlockLabels, type DevicePinUnlockResult, DevicePinUnlockScreen, type DevicePinUnlockScreenProps, type DevicePinUnlockedUser, ForgotPasswordFields, type ForgotPasswordFieldsLabels, type ForgotPasswordFieldsProps, ForgotPasswordForm, type ForgotPasswordFormLabels, type ForgotPasswordFormProps, type LoginConfigCapableClient, LoginForm, type LoginFormLabels, type LoginFormProps, OtpForm, type OtpFormLabels, type OtpFormProps, OtpLoginStep, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, PasskeyCeremonyMode, PasskeyCeremonyStatus, type PasskeyErrorCode, PasskeyLoginButton, type PasskeyLoginButtonProps, type PasskeyLoginLabels, PasskeySettingsCard, type PasskeySettingsCardProps, type PasskeySettingsLabels, PasswordPolicyError, PinForm, type PinFormLabels, type PinFormProps, type PreferencesClientOptions, type PreferredMethodClient, PreferredMethodHint, type PreferredMethodHintLabels, type PreferredMethodHintProps, PreferredMethodSettingsCard, type PreferredMethodSettingsCardProps, type PreferredMethodSettingsLabels, ResetPasswordError, ResetPasswordForm, type ResetPasswordFormLabels, type ResetPasswordFormProps, type RoleRoute, type RoleRouteTable, type UseBffAuthOptions, type UseBffAuthResult, type UseBffForgotPasswordOptions, type UseBffLoginConfigResult, type UseBffResetPasswordOptions, type UseDevicePinDisableArgs, type UseDevicePinDisableResult, type UseDevicePinEnrollArgs, type UseDevicePinEnrollResult, type UseDevicePinUnlockArgs, type UseDevicePinUnlockResult, type UseForgotPasswordSubmitArgs, type UseForgotPasswordSubmitResult, type UseOtpLoginOptions, type UseOtpLoginResult, type UsePasskeyCeremonyOptions, type UsePasskeyCeremonyResult, type UsePinLoginOptions, type UsePinLoginResult, type UsePreferredMethodSettingArgs, type UsePreferredMethodSettingResult, type UseResetPasswordFormArgs, type UseResetPasswordFormResult, cachePreferredMethod, clearCachedPreferredMethod, collectUserRoles, createBffAuthClient, createPreferencesClient, defaultAuthTheme, isAllowedPinDigits, isPasswordValid, isValidForgotPasswordEmail, readCachedPreferredMethod, readPasskeyError, readPasskeyRegistered, resolveAccessibleRoutes, resolvePostLoginRoute, startPasskeyLogin, startPasskeyRegistration, syncPreferredMethodFromServer, useAuthTheme, useBffAuth, useBffForgotPassword, useBffLoginConfig, useBffResetPassword, useDevicePinDisable, useDevicePinEnroll, useDevicePinUnlock, useForgotPasswordSubmit, useOtpLogin, usePasskeyCeremony, usePinLogin, usePreferredMethodSetting, useResetPasswordForm, validatePasswordPolicy, withTestIdPrefix };
|
|
2064
|
+
export { AuthScreen, type AuthScreenProps, AuthTestIds, type AuthTheme, type AuthThemeColors, AuthThemeProvider, type AuthThemeProviderProps, type AuthThemeRadii, type AuthThemeSpacing, type AuthThemeTypography, BffAuthStatus, type BffDeviceState, type BffLoginConfig, type BffLogoutClient, type CreateBffAuthClientOptions, DEFAULT_AUTH_CARD_MAX_WIDTH, DEFAULT_DEVICE_PIN_ENROLL_LABELS, DEFAULT_DEVICE_PIN_SETTINGS_LABELS, DEFAULT_DEVICE_PIN_UNLOCK_LABELS, DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS, DEFAULT_FORGOT_PASSWORD_LABELS, DEFAULT_LOGIN_LABELS, DEFAULT_OTP_LABELS, DEFAULT_PASSKEY_LOGIN_LABELS, DEFAULT_PASSKEY_SETTINGS_LABELS, DEFAULT_PIN_LABELS, DEFAULT_PREFERRED_METHOD_HINT_LABELS, DEFAULT_PREFERRED_METHOD_SETTINGS_LABELS, DEFAULT_RESET_PASSWORD_LABELS, DEVICE_PIN_ALLOWED_DIGITS, DEVICE_PIN_DEFAULT_DIGITS, type DevicePinCapableClient, type DevicePinDisableCapableClient, type DevicePinEnrollCapableClient, DevicePinEnrollErrorKey, DevicePinEnrollForm, type DevicePinEnrollFormProps, type DevicePinEnrollLabels, type DevicePinEnrollResult, DevicePinErrorKey, DevicePinInput, type DevicePinInputProps, DevicePinLengthPicker, type DevicePinLengthPickerProps, DevicePinOffer, type DevicePinOfferProps, DevicePinSettingsCard, type DevicePinSettingsCardProps, type DevicePinSettingsLabels, type DevicePinUnlockCapableClient, type DevicePinUnlockLabels, type DevicePinUnlockResult, DevicePinUnlockScreen, type DevicePinUnlockScreenProps, type DevicePinUnlockedUser, ForgotPasswordFields, type ForgotPasswordFieldsLabels, type ForgotPasswordFieldsProps, ForgotPasswordForm, type ForgotPasswordFormLabels, type ForgotPasswordFormProps, type LoginConfigCapableClient, LoginForm, type LoginFormLabels, type LoginFormProps, OtpForm, type OtpFormLabels, type OtpFormProps, OtpLoginStep, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, PasskeyCeremonyMode, PasskeyCeremonyStatus, type PasskeyErrorCode, PasskeyLoginButton, type PasskeyLoginButtonProps, type PasskeyLoginLabels, PasskeySettingsCard, type PasskeySettingsCardProps, type PasskeySettingsLabels, PasswordPolicyError, type PerformBffLogoutOptions, PinForm, type PinFormLabels, type PinFormProps, type PreferencesClientOptions, type PreferredMethodClient, PreferredMethodHint, type PreferredMethodHintLabels, type PreferredMethodHintProps, PreferredMethodSettingsCard, type PreferredMethodSettingsCardProps, type PreferredMethodSettingsLabels, ResetPasswordError, ResetPasswordForm, type ResetPasswordFormLabels, type ResetPasswordFormProps, type RoleRoute, type RoleRouteTable, type UseBffAuthOptions, type UseBffAuthResult, type UseBffForgotPasswordOptions, type UseBffLoginConfigResult, type UseBffResetPasswordOptions, type UseDevicePinDisableArgs, type UseDevicePinDisableResult, type UseDevicePinEnrollArgs, type UseDevicePinEnrollResult, type UseDevicePinUnlockArgs, type UseDevicePinUnlockResult, type UseForgotPasswordSubmitArgs, type UseForgotPasswordSubmitResult, type UseOtpLoginOptions, type UseOtpLoginResult, type UsePasskeyCeremonyOptions, type UsePasskeyCeremonyResult, type UsePinLoginOptions, type UsePinLoginResult, type UsePreferredMethodSettingArgs, type UsePreferredMethodSettingResult, type UseResetPasswordFormArgs, type UseResetPasswordFormResult, cachePreferredMethod, clearCachedPreferredMethod, collectUserRoles, createBffAuthClient, createPreferencesClient, defaultAuthTheme, isAllowedPinDigits, isPasswordValid, isValidForgotPasswordEmail, performBffLogout, readCachedPreferredMethod, readPasskeyError, readPasskeyRegistered, resolveAccessibleRoutes, resolvePostLoginRoute, startPasskeyLogin, startPasskeyRegistration, syncPreferredMethodFromServer, useAuthTheme, useBffAuth, useBffForgotPassword, useBffLoginConfig, useBffResetPassword, useDevicePinDisable, useDevicePinEnroll, useDevicePinUnlock, useForgotPasswordSubmit, useOtpLogin, usePasskeyCeremony, usePinLogin, usePreferredMethodSetting, useResetPasswordForm, validatePasswordPolicy, withTestIdPrefix };
|
package/dist/index.d.ts
CHANGED
|
@@ -1925,6 +1925,97 @@ declare function resolvePostLoginRoute(user: BffUser, table: RoleRouteTable): st
|
|
|
1925
1925
|
*/
|
|
1926
1926
|
declare function resolveAccessibleRoutes(user: BffUser, table: RoleRouteTable): RoleRoute[];
|
|
1927
1927
|
|
|
1928
|
+
/**
|
|
1929
|
+
* `performBffLogout` — the shared logout sequencer.
|
|
1930
|
+
*
|
|
1931
|
+
* Every app hand-rolled its own logout ordering inside its `AuthProvider`, and
|
|
1932
|
+
* the orderings disagreed. Two of them (zygos-web, agora-web) navigated FIRST
|
|
1933
|
+
* and called the BFF second:
|
|
1934
|
+
*
|
|
1935
|
+
* ```ts
|
|
1936
|
+
* redirectTo('/(auth)/login'); // window.location.replace() → document unload
|
|
1937
|
+
* await bffAuthClient.logout(); // races the unload — may never be sent
|
|
1938
|
+
* ```
|
|
1939
|
+
*
|
|
1940
|
+
* That is wrong twice over:
|
|
1941
|
+
*
|
|
1942
|
+
* 1. **The BFF call can be cancelled.** A document navigation kills in-flight
|
|
1943
|
+
* fetches, so `POST /bff/logout` may never reach the server: the SPA looks
|
|
1944
|
+
* logged out while the httpOnly session cookie stays ALIVE. The `catch`
|
|
1945
|
+
* logged it "non-fatal", so the defect was silent.
|
|
1946
|
+
* 2. **The IdP session survives.** `BffAuthClient.logout()` ends the *identity
|
|
1947
|
+
* provider's* browser session by doing a top-level navigation to the IdP's
|
|
1948
|
+
* RP-initiated logout URL (only a real navigation carries the IdP's own
|
|
1949
|
+
* cookie — a back-channel POST cannot clear it). An app that navigates to
|
|
1950
|
+
* its own login route first pre-empts that navigation, so the IdP still
|
|
1951
|
+
* considers the user signed in and the next authorize round-trip silently
|
|
1952
|
+
* re-authenticates them. That is the "sign-out that signs you back in" bug
|
|
1953
|
+
* the client's own docs warn about.
|
|
1954
|
+
*
|
|
1955
|
+
* This helper owns the whole sequence so neither can be reintroduced per-app:
|
|
1956
|
+
*
|
|
1957
|
+
* 1. `onClearSession()` — drop local state immediately so the UI reflects the
|
|
1958
|
+
* sign-out right away (perceived performance; no spinner needed).
|
|
1959
|
+
* 2. `await client.logout({ redirect: false })` — let the network call SETTLE,
|
|
1960
|
+
* and take the IdP logout URL rather than letting the client navigate mid-
|
|
1961
|
+
* sequence. Errors are captured, never rethrown.
|
|
1962
|
+
* 3. Navigate LAST, exactly once, when nothing is in flight to cancel:
|
|
1963
|
+
* - an IdP logout URL → a top-level navigation to the IdP, which ends that
|
|
1964
|
+
* session and bounces the user back. `onRedirect` is deliberately NOT
|
|
1965
|
+
* called: two competing navigations would race, and the IdP round-trip
|
|
1966
|
+
* already returns the user to the app.
|
|
1967
|
+
* - otherwise (ROPC / OTP / device-PIN logins never had an IdP cookie, so
|
|
1968
|
+
* the URL is `null`) → `onRedirect()`, the app's own login route.
|
|
1969
|
+
*
|
|
1970
|
+
* Step 3 runs even when step 2 rejects: a BFF that is down must not trap the
|
|
1971
|
+
* user on a page they asked to leave. The local session is already gone, so the
|
|
1972
|
+
* client-side sign-out holds regardless.
|
|
1973
|
+
*
|
|
1974
|
+
* Headless and framework-free: it takes callbacks, so an app can pair it with
|
|
1975
|
+
* Redux, a context, expo-router, or `window.location` without this package
|
|
1976
|
+
* knowing about any of them.
|
|
1977
|
+
*/
|
|
1978
|
+
/**
|
|
1979
|
+
* The only part of a `BffAuthClient` this helper needs. Structural, so it is
|
|
1980
|
+
* trivially fakeable in tests and does not couple to the full client surface.
|
|
1981
|
+
*/
|
|
1982
|
+
interface BffLogoutClient {
|
|
1983
|
+
/**
|
|
1984
|
+
* `POST /bff/logout` — ends the server-side session. With `redirect: false`
|
|
1985
|
+
* it returns the IdP's RP-initiated logout URL (or `null` when the session
|
|
1986
|
+
* never involved an IdP browser cookie) instead of navigating there itself.
|
|
1987
|
+
*/
|
|
1988
|
+
logout: (options?: {
|
|
1989
|
+
redirect?: boolean;
|
|
1990
|
+
}) => Promise<string | null>;
|
|
1991
|
+
}
|
|
1992
|
+
/** Callbacks + client for {@link performBffLogout}. */
|
|
1993
|
+
interface PerformBffLogoutOptions {
|
|
1994
|
+
/** The BFF auth client whose `logout()` ends the server-side session. */
|
|
1995
|
+
client: BffLogoutClient;
|
|
1996
|
+
/** Drop local session state. Runs BEFORE the network call. Optional. */
|
|
1997
|
+
onClearSession?: () => void;
|
|
1998
|
+
/**
|
|
1999
|
+
* Navigate to the app's logged-out route. Runs only AFTER the network call
|
|
2000
|
+
* settles, and only when there is no IdP logout URL to honour. Optional.
|
|
2001
|
+
*/
|
|
2002
|
+
onRedirect?: () => void;
|
|
2003
|
+
/** Observe a failed logout step (e.g. to log it). Never rethrown. Optional. */
|
|
2004
|
+
onError?: (error: Error) => void;
|
|
2005
|
+
/**
|
|
2006
|
+
* Test seam: perform the top-level navigation to the IdP logout URL. Defaults
|
|
2007
|
+
* to `window.location.assign`. Must be a real navigation — a `fetch` here
|
|
2008
|
+
* would not carry the IdP cookie and would silently fail to end the session.
|
|
2009
|
+
*/
|
|
2010
|
+
navigateToIdp?: (url: string) => void;
|
|
2011
|
+
}
|
|
2012
|
+
/**
|
|
2013
|
+
* End the session: clear locally, await the BFF, then navigate exactly once.
|
|
2014
|
+
*
|
|
2015
|
+
* Never rejects — callers may `void` it safely.
|
|
2016
|
+
*/
|
|
2017
|
+
declare function performBffLogout(options: Readonly<PerformBffLogoutOptions>): Promise<void>;
|
|
2018
|
+
|
|
1928
2019
|
/**
|
|
1929
2020
|
* The discrete ways a password can fail the shared client-side policy.
|
|
1930
2021
|
*
|
|
@@ -1970,4 +2061,4 @@ declare function validatePasswordPolicy(password: string): PasswordPolicyError[]
|
|
|
1970
2061
|
/** `true` when the password satisfies every policy rule. */
|
|
1971
2062
|
declare function isPasswordValid(password: string): boolean;
|
|
1972
2063
|
|
|
1973
|
-
export { AuthScreen, type AuthScreenProps, AuthTestIds, type AuthTheme, type AuthThemeColors, AuthThemeProvider, type AuthThemeProviderProps, type AuthThemeRadii, type AuthThemeSpacing, type AuthThemeTypography, BffAuthStatus, type BffDeviceState, type BffLoginConfig, type CreateBffAuthClientOptions, DEFAULT_AUTH_CARD_MAX_WIDTH, DEFAULT_DEVICE_PIN_ENROLL_LABELS, DEFAULT_DEVICE_PIN_SETTINGS_LABELS, DEFAULT_DEVICE_PIN_UNLOCK_LABELS, DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS, DEFAULT_FORGOT_PASSWORD_LABELS, DEFAULT_LOGIN_LABELS, DEFAULT_OTP_LABELS, DEFAULT_PASSKEY_LOGIN_LABELS, DEFAULT_PASSKEY_SETTINGS_LABELS, DEFAULT_PIN_LABELS, DEFAULT_PREFERRED_METHOD_HINT_LABELS, DEFAULT_PREFERRED_METHOD_SETTINGS_LABELS, DEFAULT_RESET_PASSWORD_LABELS, DEVICE_PIN_ALLOWED_DIGITS, DEVICE_PIN_DEFAULT_DIGITS, type DevicePinCapableClient, type DevicePinDisableCapableClient, type DevicePinEnrollCapableClient, DevicePinEnrollErrorKey, DevicePinEnrollForm, type DevicePinEnrollFormProps, type DevicePinEnrollLabels, type DevicePinEnrollResult, DevicePinErrorKey, DevicePinInput, type DevicePinInputProps, DevicePinLengthPicker, type DevicePinLengthPickerProps, DevicePinOffer, type DevicePinOfferProps, DevicePinSettingsCard, type DevicePinSettingsCardProps, type DevicePinSettingsLabels, type DevicePinUnlockCapableClient, type DevicePinUnlockLabels, type DevicePinUnlockResult, DevicePinUnlockScreen, type DevicePinUnlockScreenProps, type DevicePinUnlockedUser, ForgotPasswordFields, type ForgotPasswordFieldsLabels, type ForgotPasswordFieldsProps, ForgotPasswordForm, type ForgotPasswordFormLabels, type ForgotPasswordFormProps, type LoginConfigCapableClient, LoginForm, type LoginFormLabels, type LoginFormProps, OtpForm, type OtpFormLabels, type OtpFormProps, OtpLoginStep, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, PasskeyCeremonyMode, PasskeyCeremonyStatus, type PasskeyErrorCode, PasskeyLoginButton, type PasskeyLoginButtonProps, type PasskeyLoginLabels, PasskeySettingsCard, type PasskeySettingsCardProps, type PasskeySettingsLabels, PasswordPolicyError, PinForm, type PinFormLabels, type PinFormProps, type PreferencesClientOptions, type PreferredMethodClient, PreferredMethodHint, type PreferredMethodHintLabels, type PreferredMethodHintProps, PreferredMethodSettingsCard, type PreferredMethodSettingsCardProps, type PreferredMethodSettingsLabels, ResetPasswordError, ResetPasswordForm, type ResetPasswordFormLabels, type ResetPasswordFormProps, type RoleRoute, type RoleRouteTable, type UseBffAuthOptions, type UseBffAuthResult, type UseBffForgotPasswordOptions, type UseBffLoginConfigResult, type UseBffResetPasswordOptions, type UseDevicePinDisableArgs, type UseDevicePinDisableResult, type UseDevicePinEnrollArgs, type UseDevicePinEnrollResult, type UseDevicePinUnlockArgs, type UseDevicePinUnlockResult, type UseForgotPasswordSubmitArgs, type UseForgotPasswordSubmitResult, type UseOtpLoginOptions, type UseOtpLoginResult, type UsePasskeyCeremonyOptions, type UsePasskeyCeremonyResult, type UsePinLoginOptions, type UsePinLoginResult, type UsePreferredMethodSettingArgs, type UsePreferredMethodSettingResult, type UseResetPasswordFormArgs, type UseResetPasswordFormResult, cachePreferredMethod, clearCachedPreferredMethod, collectUserRoles, createBffAuthClient, createPreferencesClient, defaultAuthTheme, isAllowedPinDigits, isPasswordValid, isValidForgotPasswordEmail, readCachedPreferredMethod, readPasskeyError, readPasskeyRegistered, resolveAccessibleRoutes, resolvePostLoginRoute, startPasskeyLogin, startPasskeyRegistration, syncPreferredMethodFromServer, useAuthTheme, useBffAuth, useBffForgotPassword, useBffLoginConfig, useBffResetPassword, useDevicePinDisable, useDevicePinEnroll, useDevicePinUnlock, useForgotPasswordSubmit, useOtpLogin, usePasskeyCeremony, usePinLogin, usePreferredMethodSetting, useResetPasswordForm, validatePasswordPolicy, withTestIdPrefix };
|
|
2064
|
+
export { AuthScreen, type AuthScreenProps, AuthTestIds, type AuthTheme, type AuthThemeColors, AuthThemeProvider, type AuthThemeProviderProps, type AuthThemeRadii, type AuthThemeSpacing, type AuthThemeTypography, BffAuthStatus, type BffDeviceState, type BffLoginConfig, type BffLogoutClient, type CreateBffAuthClientOptions, DEFAULT_AUTH_CARD_MAX_WIDTH, DEFAULT_DEVICE_PIN_ENROLL_LABELS, DEFAULT_DEVICE_PIN_SETTINGS_LABELS, DEFAULT_DEVICE_PIN_UNLOCK_LABELS, DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS, DEFAULT_FORGOT_PASSWORD_LABELS, DEFAULT_LOGIN_LABELS, DEFAULT_OTP_LABELS, DEFAULT_PASSKEY_LOGIN_LABELS, DEFAULT_PASSKEY_SETTINGS_LABELS, DEFAULT_PIN_LABELS, DEFAULT_PREFERRED_METHOD_HINT_LABELS, DEFAULT_PREFERRED_METHOD_SETTINGS_LABELS, DEFAULT_RESET_PASSWORD_LABELS, DEVICE_PIN_ALLOWED_DIGITS, DEVICE_PIN_DEFAULT_DIGITS, type DevicePinCapableClient, type DevicePinDisableCapableClient, type DevicePinEnrollCapableClient, DevicePinEnrollErrorKey, DevicePinEnrollForm, type DevicePinEnrollFormProps, type DevicePinEnrollLabels, type DevicePinEnrollResult, DevicePinErrorKey, DevicePinInput, type DevicePinInputProps, DevicePinLengthPicker, type DevicePinLengthPickerProps, DevicePinOffer, type DevicePinOfferProps, DevicePinSettingsCard, type DevicePinSettingsCardProps, type DevicePinSettingsLabels, type DevicePinUnlockCapableClient, type DevicePinUnlockLabels, type DevicePinUnlockResult, DevicePinUnlockScreen, type DevicePinUnlockScreenProps, type DevicePinUnlockedUser, ForgotPasswordFields, type ForgotPasswordFieldsLabels, type ForgotPasswordFieldsProps, ForgotPasswordForm, type ForgotPasswordFormLabels, type ForgotPasswordFormProps, type LoginConfigCapableClient, LoginForm, type LoginFormLabels, type LoginFormProps, OtpForm, type OtpFormLabels, type OtpFormProps, OtpLoginStep, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, PasskeyCeremonyMode, PasskeyCeremonyStatus, type PasskeyErrorCode, PasskeyLoginButton, type PasskeyLoginButtonProps, type PasskeyLoginLabels, PasskeySettingsCard, type PasskeySettingsCardProps, type PasskeySettingsLabels, PasswordPolicyError, type PerformBffLogoutOptions, PinForm, type PinFormLabels, type PinFormProps, type PreferencesClientOptions, type PreferredMethodClient, PreferredMethodHint, type PreferredMethodHintLabels, type PreferredMethodHintProps, PreferredMethodSettingsCard, type PreferredMethodSettingsCardProps, type PreferredMethodSettingsLabels, ResetPasswordError, ResetPasswordForm, type ResetPasswordFormLabels, type ResetPasswordFormProps, type RoleRoute, type RoleRouteTable, type UseBffAuthOptions, type UseBffAuthResult, type UseBffForgotPasswordOptions, type UseBffLoginConfigResult, type UseBffResetPasswordOptions, type UseDevicePinDisableArgs, type UseDevicePinDisableResult, type UseDevicePinEnrollArgs, type UseDevicePinEnrollResult, type UseDevicePinUnlockArgs, type UseDevicePinUnlockResult, type UseForgotPasswordSubmitArgs, type UseForgotPasswordSubmitResult, type UseOtpLoginOptions, type UseOtpLoginResult, type UsePasskeyCeremonyOptions, type UsePasskeyCeremonyResult, type UsePinLoginOptions, type UsePinLoginResult, type UsePreferredMethodSettingArgs, type UsePreferredMethodSettingResult, type UseResetPasswordFormArgs, type UseResetPasswordFormResult, cachePreferredMethod, clearCachedPreferredMethod, collectUserRoles, createBffAuthClient, createPreferencesClient, defaultAuthTheme, isAllowedPinDigits, isPasswordValid, isValidForgotPasswordEmail, performBffLogout, readCachedPreferredMethod, readPasskeyError, readPasskeyRegistered, resolveAccessibleRoutes, resolvePostLoginRoute, startPasskeyLogin, startPasskeyRegistration, syncPreferredMethodFromServer, useAuthTheme, useBffAuth, useBffForgotPassword, useBffLoginConfig, useBffResetPassword, useDevicePinDisable, useDevicePinEnroll, useDevicePinUnlock, useForgotPasswordSubmit, useOtpLogin, usePasskeyCeremony, usePinLogin, usePreferredMethodSetting, useResetPasswordForm, validatePasswordPolicy, withTestIdPrefix };
|
package/dist/index.js
CHANGED
|
@@ -3286,6 +3286,43 @@ function resolveAccessibleRoutes(user, table) {
|
|
|
3286
3286
|
return table.routes.filter((entry) => userRoles.has(entry.role));
|
|
3287
3287
|
}
|
|
3288
3288
|
|
|
3289
|
+
// src/session/performBffLogout.ts
|
|
3290
|
+
function toError4(caught) {
|
|
3291
|
+
return caught instanceof Error ? caught : new Error(String(caught));
|
|
3292
|
+
}
|
|
3293
|
+
function runGuarded(callback, onError) {
|
|
3294
|
+
if (callback === void 0) {
|
|
3295
|
+
return;
|
|
3296
|
+
}
|
|
3297
|
+
try {
|
|
3298
|
+
callback();
|
|
3299
|
+
} catch (caught) {
|
|
3300
|
+
onError?.(toError4(caught));
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
function assignLocation(url) {
|
|
3304
|
+
if (typeof window === "undefined") {
|
|
3305
|
+
return;
|
|
3306
|
+
}
|
|
3307
|
+
window.location.assign(url);
|
|
3308
|
+
}
|
|
3309
|
+
async function performBffLogout(options) {
|
|
3310
|
+
const { client, onClearSession, onRedirect, onError, navigateToIdp = assignLocation } = options;
|
|
3311
|
+
runGuarded(onClearSession, onError);
|
|
3312
|
+
let idpLogoutUrl = null;
|
|
3313
|
+
try {
|
|
3314
|
+
idpLogoutUrl = await client.logout({ redirect: false });
|
|
3315
|
+
} catch (caught) {
|
|
3316
|
+
onError?.(toError4(caught));
|
|
3317
|
+
}
|
|
3318
|
+
if (idpLogoutUrl !== null) {
|
|
3319
|
+
const url = idpLogoutUrl;
|
|
3320
|
+
runGuarded(() => navigateToIdp(url), onError);
|
|
3321
|
+
return;
|
|
3322
|
+
}
|
|
3323
|
+
runGuarded(onRedirect, onError);
|
|
3324
|
+
}
|
|
3325
|
+
|
|
3289
3326
|
Object.defineProperty(exports, "BffAuthClient", {
|
|
3290
3327
|
enumerable: true,
|
|
3291
3328
|
get: function () { return authClient.BffAuthClient; }
|
|
@@ -3348,6 +3385,7 @@ exports.defaultAuthTheme = defaultAuthTheme;
|
|
|
3348
3385
|
exports.isAllowedPinDigits = isAllowedPinDigits;
|
|
3349
3386
|
exports.isPasswordValid = isPasswordValid;
|
|
3350
3387
|
exports.isValidForgotPasswordEmail = isValidForgotPasswordEmail;
|
|
3388
|
+
exports.performBffLogout = performBffLogout;
|
|
3351
3389
|
exports.readCachedPreferredMethod = readCachedPreferredMethod;
|
|
3352
3390
|
exports.readPasskeyError = readPasskeyError;
|
|
3353
3391
|
exports.readPasskeyRegistered = readPasskeyRegistered;
|