Review follow-ups on the RemoteOAuthStorage web migration: - useEmaIdpLoginState.logout(): add a .catch to the now-async clearEmaIdpSession call so a storage failure no longer surfaces as an unhandled rejection; leave loginState unchanged on failure (the session was not actually cleared). Add a test asserting no unhandled rejection. - createOAuthStore: attach a no-op catch to the eager persistLoadPromise so a hydration read that fails before any load() awaits it does not become an unhandled rejection; real awaiters still observe the rejection via the shared reference. - storage-remote.test: replace `as unknown as typeof fetch` double-casts with the typed vi.fn<typeof fetch>() pattern the same PR established. - remoteOAuthStorage: correct the doc comment to describe the cache-of-one accurately instead of implying a keyed map. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
57 lines
1.7 KiB
TypeScript
57 lines
1.7 KiB
TypeScript
import { useCallback, useEffect, useState } from "react";
|
|
import type { OAuthStorage } from "../auth/storage.js";
|
|
import {
|
|
clearEmaIdpSession,
|
|
getEmaIdpLoginState,
|
|
normalizeIdpIssuer,
|
|
type EmaIdpLoginState,
|
|
} from "../auth/ema/idpSession.js";
|
|
|
|
export interface UseEmaIdpLoginStateResult {
|
|
loginState: EmaIdpLoginState;
|
|
refresh: () => Promise<void>;
|
|
logout: () => void;
|
|
}
|
|
|
|
/**
|
|
* Read and clear EMA IdP session state for Client Settings UX.
|
|
* Pass `active: true` while the settings surface is open to refresh on open.
|
|
*/
|
|
export function useEmaIdpLoginState(
|
|
storage: OAuthStorage,
|
|
issuer: string | undefined,
|
|
active: boolean,
|
|
): UseEmaIdpLoginStateResult {
|
|
const normalizedIssuer = issuer ? normalizeIdpIssuer(issuer) : "";
|
|
const [loginState, setLoginState] = useState<EmaIdpLoginState>("none");
|
|
|
|
const refresh = useCallback(async () => {
|
|
if (!normalizedIssuer) {
|
|
setLoginState("none");
|
|
return;
|
|
}
|
|
setLoginState(await getEmaIdpLoginState(storage, normalizedIssuer));
|
|
}, [storage, normalizedIssuer]);
|
|
|
|
useEffect(() => {
|
|
if (!active) return;
|
|
void refresh();
|
|
}, [active, refresh]);
|
|
|
|
const logout = useCallback(() => {
|
|
if (!normalizedIssuer) return;
|
|
void clearEmaIdpSession(storage, normalizedIssuer)
|
|
.then(() => {
|
|
setLoginState("none");
|
|
})
|
|
.catch(() => {
|
|
// Clearing the persisted IdP session failed (e.g. the storage backend
|
|
// is unreachable). Swallow the rejection so it does not surface as an
|
|
// unhandled promise, and leave loginState unchanged so the UI keeps
|
|
// reflecting the still-present session rather than falsely showing
|
|
// signed-out.
|
|
});
|
|
}, [storage, normalizedIssuer]);
|
|
|
|
return { loginState, refresh, logout };
|
|
}
|