diff --git a/src/lib/oauth.ts b/src/lib/oauth.ts index 117d863..5976e4b 100644 --- a/src/lib/oauth.ts +++ b/src/lib/oauth.ts @@ -8,6 +8,11 @@ */ export type OAuthFlow = 'login' | 'register' | 'link'; +/** + * Supported OAuth providers. + */ +export type OAuthProvider = 'google' | 'github' | 'microsoft'; + /** * Parameters for storing OAuth state. */ diff --git a/src/lib/webauthn.ts b/src/lib/webauthn.ts index 463bee6..9ab979b 100644 --- a/src/lib/webauthn.ts +++ b/src/lib/webauthn.ts @@ -45,6 +45,19 @@ export async function isPlatformAuthenticatorAvailable(): Promise { } } +// Helper to ensure rp.id is valid for the current origin +function ensureValidRpId(rpId: string): string { + const currentHost = window.location.hostname; + // valid if rpId matches host or is a parent domain (e.g. host ends with .rpId) + const isValid = currentHost === rpId || currentHost.endsWith('.' + rpId); + + if (!isValid) { + console.warn(`[WebAuthn] Invalid rp.id "${rpId}" for current host "${currentHost}". Overriding with "${currentHost}".`); + return currentHost; + } + return rpId; +} + // Types for WebAuthn API responses export interface WebAuthnRegistrationOptions { rp: { @@ -107,6 +120,10 @@ export async function createRegistrationCredential( ): Promise { const publicKeyOptions: PublicKeyCredentialCreationOptions = { ...options, + rp: { + ...options.rp, + id: ensureValidRpId(options.rp.id), + }, challenge: base64ToBuffer(options.challenge), user: { id: base64ToBuffer(options.user.id), @@ -157,7 +174,7 @@ export async function createLoginAssertion( const publicKeyOptions: PublicKeyCredentialRequestOptions = { challenge: base64ToBuffer(options.challenge), timeout: options.timeout, - rpId: options.rpId, + rpId: ensureValidRpId(options.rpId), allowCredentials: options.allowCredentials.map((cred) => ({ ...cred, id: base64ToBuffer(cred.id), diff --git a/src/pages/auth/LoginPage.tsx b/src/pages/auth/LoginPage.tsx index d27709f..84c3a24 100644 --- a/src/pages/auth/LoginPage.tsx +++ b/src/pages/auth/LoginPage.tsx @@ -218,7 +218,7 @@ export default function LoginPage() { try { // Step 1: Get login options from server - const options = await api.webauthn.beginLogin(emailToUse) as WebAuthnLoginOptions; + const options = await api.webauthn.beginLogin(emailToUse) as unknown as WebAuthnLoginOptions; // Step 2: Create assertion using browser WebAuthn API const assertion = await createLoginAssertion(options); @@ -286,7 +286,7 @@ export default function LoginPage() { try { // Step 1: Get login options from server - const options = await api.webauthn.beginLogin(email) as WebAuthnLoginOptions; + const options = await api.webauthn.beginLogin(email) as unknown as WebAuthnLoginOptions; // Step 2: Create assertion using browser WebAuthn API const assertion = await createLoginAssertion(options); diff --git a/tsconfig.app.tsbuildinfo b/tsconfig.app.tsbuildinfo new file mode 100644 index 0000000..1de9171 --- /dev/null +++ b/tsconfig.app.tsbuildinfo @@ -0,0 +1 @@ +{"root":["./src/App.tsx","./src/config.ts","./src/main.tsx","./src/vite-env.d.ts","./src/components/NavLink.tsx","./src/components/auth/BannerAlert.tsx","./src/components/auth/ComplianceBanner.tsx","./src/components/auth/PasswordStrengthMeter.tsx","./src/components/branding/GatehouseLogo.tsx","./src/components/dev/ApiDevTools.tsx","./src/components/layouts/AuthenticatedLayout.tsx","./src/components/layouts/MfaEnforcementLayout.tsx","./src/components/layouts/ProtectedLayout.tsx","./src/components/layouts/PublicLayout.tsx","./src/components/navigation/AppSidebar.tsx","./src/components/navigation/TopBar.tsx","./src/components/security/AddPasskeyWizard.tsx","./src/components/security/TotpEnrollmentWizard.tsx","./src/components/security/TotpRemoveDialog.tsx","./src/components/ui/accordion.tsx","./src/components/ui/alert-dialog.tsx","./src/components/ui/alert.tsx","./src/components/ui/aspect-ratio.tsx","./src/components/ui/avatar.tsx","./src/components/ui/badge.tsx","./src/components/ui/breadcrumb.tsx","./src/components/ui/button.tsx","./src/components/ui/calendar.tsx","./src/components/ui/card.tsx","./src/components/ui/carousel.tsx","./src/components/ui/chart.tsx","./src/components/ui/checkbox.tsx","./src/components/ui/collapsible.tsx","./src/components/ui/command.tsx","./src/components/ui/context-menu.tsx","./src/components/ui/dialog.tsx","./src/components/ui/drawer.tsx","./src/components/ui/dropdown-menu.tsx","./src/components/ui/form.tsx","./src/components/ui/hover-card.tsx","./src/components/ui/input-otp.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/menubar.tsx","./src/components/ui/navigation-menu.tsx","./src/components/ui/pagination.tsx","./src/components/ui/popover.tsx","./src/components/ui/progress.tsx","./src/components/ui/radio-group.tsx","./src/components/ui/resizable.tsx","./src/components/ui/scroll-area.tsx","./src/components/ui/select.tsx","./src/components/ui/separator.tsx","./src/components/ui/sheet.tsx","./src/components/ui/sidebar.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/slider.tsx","./src/components/ui/sonner.tsx","./src/components/ui/switch.tsx","./src/components/ui/table.tsx","./src/components/ui/tabs.tsx","./src/components/ui/textarea.tsx","./src/components/ui/toast.tsx","./src/components/ui/toaster.tsx","./src/components/ui/toggle-group.tsx","./src/components/ui/toggle.tsx","./src/components/ui/tooltip.tsx","./src/components/ui/use-toast.ts","./src/contexts/AuthContext.tsx","./src/hooks/use-mobile.tsx","./src/hooks/use-toast.ts","./src/hooks/useOrganizations.ts","./src/lib/api.ts","./src/lib/encoding.ts","./src/lib/oauth.ts","./src/lib/utils.ts","./src/lib/webauthn.ts","./src/pages/Index.tsx","./src/pages/NotFound.tsx","./src/pages/auth/ForgotPasswordPage.tsx","./src/pages/auth/InviteAcceptPage.tsx","./src/pages/auth/LoginPage.tsx","./src/pages/auth/OAuthCallbackPage.tsx","./src/pages/auth/OIDCConsentPage.tsx","./src/pages/auth/OIDCErrorPage.tsx","./src/pages/auth/RegisterPage.tsx","./src/pages/auth/ResetPasswordPage.tsx","./src/pages/auth/VerifyEmailPage.tsx","./src/pages/org/CompliancePage.tsx","./src/pages/org/MembersPage.tsx","./src/pages/org/OIDCClientsPage.tsx","./src/pages/org/OrgAuditPage.tsx","./src/pages/org/OrgOverviewPage.tsx","./src/pages/org/PoliciesPage.tsx","./src/pages/user/ActivityPage.tsx","./src/pages/user/LinkedAccountsPage.tsx","./src/pages/user/ProfilePage.tsx","./src/pages/user/SecurityPage.tsx"],"errors":true,"version":"5.8.3"} \ No newline at end of file diff --git a/tsconfig.node.tsbuildinfo b/tsconfig.node.tsbuildinfo new file mode 100644 index 0000000..3015526 --- /dev/null +++ b/tsconfig.node.tsbuildinfo @@ -0,0 +1 @@ +{"root":["./vite.config.ts"],"version":"5.8.3"} \ No newline at end of file