diff --git a/README.md b/README.md index f3074d4..83fd83c 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ This app is intended to run alongside the Seamless Auth API as part of a self-ho - filter and investigate authentication events - review suspicious activity and anomaly signals - edit system configuration +- configure allowed login methods and OAuth providers - operate with runtime config injection in containerized environments ## Tech Stack @@ -68,6 +69,55 @@ That runtime-config flow is intentional. The dashboard is designed to be reconfi ### System Configuration - manage available roles and auth settings +- enable or disable login methods such as passkeys, magic links, OTP, and OAuth +- configure OAuth providers without entering provider client secrets in the browser + +#### OAuth Provider Configuration + +The dashboard edits the Seamless Auth API `oauth_providers` system config. OAuth is enabled by +turning on the `OAuth` login method and adding one or more provider records. + +Each provider record includes: + +- provider id, such as `google` or `github` +- display name +- client id +- `clientSecretEnv`, the name of the server environment variable holding the client secret +- authorization URL +- token URL +- userinfo URL +- requested scopes +- JSON paths used to read provider subject, email, and name from the userinfo response +- optional redirect URI +- signup policy + +The dashboard intentionally does **not** collect provider client-secret values. Store those secrets +on the Seamless Auth API host and reference them by environment variable name, for example +`GOOGLE_CLIENT_SECRET`. + +Example provider configuration: + +```json +{ + "id": "google", + "name": "Google", + "enabled": true, + "clientId": "google-oauth-client-id", + "clientSecretEnv": "GOOGLE_CLIENT_SECRET", + "authorizationUrl": "https://accounts.google.com/o/oauth2/v2/auth", + "tokenUrl": "https://oauth2.googleapis.com/token", + "userInfoUrl": "https://openidconnect.googleapis.com/v1/userinfo", + "scopes": ["openid", "email", "profile"], + "redirectUri": "https://app.example.com/oauth/callback", + "subjectJsonPath": "sub", + "emailJsonPath": "email", + "nameJsonPath": "name", + "allowSignup": true +} +``` + +After saving config, clients can discover enabled providers with `GET /oauth/providers` and start +login with `POST /oauth/:providerId/start`. ### Appearance diff --git a/src/components/PieChart.tsx b/src/components/PieChart.tsx index d17975a..47f1f5b 100644 --- a/src/components/PieChart.tsx +++ b/src/components/PieChart.tsx @@ -19,6 +19,7 @@ import { buildEventQuery } from "../lib/eventNavigation"; type PieChartDatum = { type: string; + label?: string; count: number; }; @@ -29,11 +30,15 @@ function generateColor(index: number) { "var(--primary)", "var(--accent)", "var(--highlight)", - "#8C6A5D", - "#5A7D7C", - "#D4A373", - "#7F5539", - "#6B9080", + "color-mix(in srgb, var(--primary) 70%, var(--accent))", + "color-mix(in srgb, var(--accent) 70%, var(--highlight))", + "color-mix(in srgb, var(--highlight) 70%, var(--primary))", + "color-mix(in srgb, var(--primary) 55%, var(--surface-alt))", + "color-mix(in srgb, var(--accent) 55%, var(--surface-alt))", + "color-mix(in srgb, var(--highlight) 55%, var(--surface-alt))", + "color-mix(in srgb, var(--primary) 65%, var(--text-muted))", + "color-mix(in srgb, var(--accent) 65%, var(--text-muted))", + "color-mix(in srgb, var(--highlight) 65%, var(--text-muted))", ]; return palette[index % palette.length]; @@ -43,15 +48,29 @@ function generateColor(index: number) { export default function PieChart({ data }: { data: PieChartDatum[] }) { const navigate = useNavigate(); + const chartData = data + .filter((item) => item.count > 0) + .map((item) => ({ + ...item, + label: item.label ?? item.type, + })); + + if (chartData.length === 0) { + return ( +
+ No event data yet +
+ ); + } return ( -
+
- {data.map((_, i) => ( + {chartData.map((_, i) => ( diff --git a/src/hooks/useGroupedEvents.ts b/src/hooks/useGroupedEvents.ts index bd062cf..f7e2ffa 100644 --- a/src/hooks/useGroupedEvents.ts +++ b/src/hooks/useGroupedEvents.ts @@ -6,16 +6,34 @@ import { useQuery } from "@tanstack/react-query"; import { apiFetch } from "../lib/api"; +import { categorizeEventSummary } from "../lib/eventCategories"; -export interface groupedEvents { +type EventSummaryResponse = { summary: { type: string; count: number; }[]; +}; + +export interface GroupedEvents { + summary: { + type: string; + label: string; + count: number; + }[]; } + export function useGroupedEvents() { return useQuery({ queryKey: ["grouped-events"], - queryFn: () => apiFetch("/internal/auth-events/grouped"), + queryFn: async (): Promise => { + const data = await apiFetch( + "/internal/auth-events/summary", + ); + + return { + summary: categorizeEventSummary(data.summary), + }; + }, }); } diff --git a/src/hooks/useSystemConfig.ts b/src/hooks/useSystemConfig.ts index f51766f..a005de6 100644 --- a/src/hooks/useSystemConfig.ts +++ b/src/hooks/useSystemConfig.ts @@ -8,7 +8,29 @@ import { useQuery } from "@tanstack/react-query"; import { apiFetch } from "../lib/api"; -export type LoginMethod = "passkey" | "magic_link" | "email_otp" | "phone_otp"; +export type LoginMethod = + | "passkey" + | "magic_link" + | "email_otp" + | "phone_otp" + | "oauth"; + +export type OAuthProviderConfig = { + id: string; + name: string; + enabled: boolean; + clientId: string; + clientSecretEnv: string; + authorizationUrl: string; + tokenUrl: string; + userInfoUrl: string; + scopes: string[]; + redirectUri?: string; + subjectJsonPath: string; + emailJsonPath: string; + nameJsonPath?: string; + allowSignup: boolean; +}; export type SystemConfig = { app_name: string; @@ -20,6 +42,7 @@ export type SystemConfig = { delay_after: number; login_methods: LoginMethod[]; passkey_login_fallback_enabled: boolean; + oauth_providers: OAuthProviderConfig[]; rpid: string; origins: string[]; }; diff --git a/src/hooks/useUpdateSystemConfig.ts b/src/hooks/useUpdateSystemConfig.ts index 255babc..32000d8 100644 --- a/src/hooks/useUpdateSystemConfig.ts +++ b/src/hooks/useUpdateSystemConfig.ts @@ -6,7 +6,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; import { apiFetch } from "../lib/api"; -import type { LoginMethod } from "./useSystemConfig"; +import type { LoginMethod, OAuthProviderConfig } from "./useSystemConfig"; export type SystemConfig = { app_name: string; @@ -18,6 +18,7 @@ export type SystemConfig = { delay_after: number; login_methods: LoginMethod[]; passkey_login_fallback_enabled: boolean; + oauth_providers: OAuthProviderConfig[]; rpid: string; origins: string[]; }; diff --git a/src/lib/eventCategories.test.ts b/src/lib/eventCategories.test.ts new file mode 100644 index 0000000..e0fb38b --- /dev/null +++ b/src/lib/eventCategories.test.ts @@ -0,0 +1,39 @@ +/* + * Copyright © 2026 Fells Code, LLC + * Licensed under the GNU Affero General Public License v3.0 + * See LICENSE file in the project root for full license information + */ + +import { describe, expect, it } from "vitest"; +import { categorizeEventSummary, getEventCategory } from "./eventCategories"; + +describe("eventCategories", () => { + it("classifies concrete API event types into operator categories", () => { + expect(getEventCategory("oauth_login_success").value).toBe("oauth"); + expect(getEventCategory("webauthn_login_success").value).toBe("webauthn"); + expect(getEventCategory("refresh_token_failed").value).toBe("token"); + expect(getEventCategory("service_token_success").value).toBe( + "serviceToken", + ); + expect(getEventCategory("step_up_challenge").value).toBe("stepUp"); + expect(getEventCategory("login_suspicious").value).toBe("security"); + }); + + it("groups raw event summaries and only keeps populated categories", () => { + expect( + categorizeEventSummary([ + { type: "login_success", count: 8 }, + { type: "refresh_token_failed", count: 5 }, + { type: "service_token_success", count: 3 }, + { type: "oauth_login_failed", count: 4 }, + { type: "unknown_backend_event", count: 2 }, + ]), + ).toEqual([ + { type: "login", label: "Login", count: 8 }, + { type: "token", label: "Session Tokens", count: 5 }, + { type: "oauth", label: "OAuth", count: 4 }, + { type: "serviceToken", label: "Service Tokens", count: 3 }, + { type: "other", label: "Other", count: 2 }, + ]); + }); +}); diff --git a/src/lib/eventCategories.ts b/src/lib/eventCategories.ts new file mode 100644 index 0000000..81656c5 --- /dev/null +++ b/src/lib/eventCategories.ts @@ -0,0 +1,170 @@ +/* + * Copyright © 2026 Fells Code, LLC + * Licensed under the GNU Affero General Public License v3.0 + * See LICENSE file in the project root for full license information + */ + +export type EventCategory = { + label: string; + value: string; + match: (type: string) => boolean; +}; + +export type EventCount = { + type: string; + count: number; +}; + +export type EventCategoryCount = { + type: string; + label: string; + count: number; +}; + +const exact = (types: string[]) => { + const values = new Set(types); + + return (type: string) => values.has(type); +}; + +const startsWithAny = (prefixes: string[]) => (type: string) => + prefixes.some((prefix) => type.startsWith(prefix)); + +const isSuspicious = (type: string) => type.includes("suspicious"); + +export const eventCategories: EventCategory[] = [ + { + label: "Security", + value: "security", + match: (type) => isSuspicious(type) || type === "request_suspicious", + }, + { + label: "Login", + value: "login", + match: exact(["login_challenge", "login_failed", "login_success"]), + }, + { + label: "OAuth", + value: "oauth", + match: startsWithAny(["oauth_"]), + }, + { + label: "Passkeys", + value: "webauthn", + match: startsWithAny(["webauthn_"]), + }, + { + label: "Magic Links", + value: "magicLink", + match: startsWithAny(["magic_link_"]), + }, + { + label: "OTP", + value: "otp", + match: startsWithAny(["otp_", "recovery_otp_", "verify_otp_"]), + }, + { + label: "TOTP / MFA", + value: "totp", + match: startsWithAny(["mfa_otp_", "totp_"]), + }, + { + label: "Step-Up", + value: "stepUp", + match: startsWithAny(["step_up_"]), + }, + { + label: "Registration", + value: "registration", + match: startsWithAny(["registration_"]), + }, + { + label: "Session Tokens", + value: "token", + match: startsWithAny(["bearer_token_", "cookie_token_", "refresh_token_"]), + }, + { + label: "Service Tokens", + value: "serviceToken", + match: startsWithAny(["service_token_"]), + }, + { + label: "Logout", + value: "logout", + match: startsWithAny(["logout_"]), + }, + { + label: "User Admin", + value: "user", + match: exact([ + "credentials_deleted", + "internal_user_updated_by_owner", + "user_created", + "user_data_failed", + "user_data_success", + "user_deleted", + ]), + }, + { + label: "System Config", + value: "system", + match: startsWithAny(["system_config_"]), + }, + { + label: "Bootstrap", + value: "bootstrap", + match: startsWithAny(["bootstrap_admin_"]), + }, + { + label: "JWKS", + value: "jwks", + match: startsWithAny(["jwks_"]), + }, + { + label: "Notifications", + value: "notification", + match: exact(["notification_sent", "notication_sent"]), + }, + { + label: "Operations", + value: "operation", + match: exact(["auth_action_incremented", "informational"]), + }, +]; + +export const otherEventCategory: EventCategory = { + label: "Other", + value: "other", + match: () => false, +}; + +export function getEventCategory(type: string) { + return ( + eventCategories.find((category) => category.match(type)) ?? + otherEventCategory + ); +} + +export function categorizeEventSummary( + summary: EventCount[], +): EventCategoryCount[] { + const counts = new Map(); + + for (const category of [...eventCategories, otherEventCategory]) { + counts.set(category.value, 0); + } + + for (const item of summary) { + const category = getEventCategory(item.type); + counts.set(category.value, (counts.get(category.value) ?? 0) + item.count); + } + + return [...eventCategories, otherEventCategory] + .map((category) => ({ + type: category.value, + label: category.label, + count: counts.get(category.value) ?? 0, + })) + .filter((item) => item.count > 0) + .sort((a, b) => b.count - a.count); +} diff --git a/src/lib/eventGroups.test.ts b/src/lib/eventGroups.test.ts index 6c1027f..e3609e6 100644 --- a/src/lib/eventGroups.test.ts +++ b/src/lib/eventGroups.test.ts @@ -11,11 +11,24 @@ describe("eventGroups", () => { it("includes the expected top-level quick filters", () => { expect(eventGroups.map((group) => group.value)).toEqual([ "", + "security", "login", + "oauth", "webauthn", + "magicLink", "otp", + "totp", + "stepUp", + "registration", "token", - "security", + "serviceToken", + "logout", + "user", + "system", + "bootstrap", + "jwks", + "notification", + "operation", ]); }); @@ -24,6 +37,7 @@ describe("eventGroups", () => { expect(loginGroup?.match("login_success")).toBe(true); expect(loginGroup?.match("login_suspicious")).toBe(false); + expect(loginGroup?.match("oauth_login_success")).toBe(false); }); it("matches security-only suspicious activity", () => { @@ -34,4 +48,16 @@ describe("eventGroups", () => { expect(securityGroup?.match("request_suspicious")).toBe(true); expect(securityGroup?.match("login_success")).toBe(false); }); + + it("matches infrastructure-specific event families", () => { + const tokenGroup = eventGroups.find((group) => group.value === "token"); + const serviceTokenGroup = eventGroups.find( + (group) => group.value === "serviceToken", + ); + const stepUpGroup = eventGroups.find((group) => group.value === "stepUp"); + + expect(tokenGroup?.match("refresh_token_failed")).toBe(true); + expect(serviceTokenGroup?.match("service_token_success")).toBe(true); + expect(stepUpGroup?.match("step_up_challenge")).toBe(true); + }); }); diff --git a/src/lib/eventGroups.ts b/src/lib/eventGroups.ts index bbec8dd..b5aeed5 100644 --- a/src/lib/eventGroups.ts +++ b/src/lib/eventGroups.ts @@ -5,8 +5,7 @@ */ // src/lib/eventGroups.ts - -const isSuspicious = (t: string) => t.includes("suspicious"); +import { eventCategories } from "./eventCategories"; export const eventGroups = [ { @@ -14,34 +13,5 @@ export const eventGroups = [ value: "", match: () => true, }, - - { - label: "Login", - value: "login", - match: (t: string) => t.startsWith("login") && !isSuspicious(t), - }, - - { - label: "WebAuthn", - value: "webauthn", - match: (t: string) => t.startsWith("webauthn") && !isSuspicious(t), - }, - - { - label: "OTP", - value: "otp", - match: (t: string) => t.includes("otp") && !isSuspicious(t), - }, - - { - label: "Tokens", - value: "token", - match: (t: string) => t.includes("token") && !isSuspicious(t), - }, - - { - label: "Security", - value: "security", - match: (t: string) => isSuspicious(t) || t === "request_suspicious", - }, + ...eventCategories, ]; diff --git a/src/lib/eventMapping.test.ts b/src/lib/eventMapping.test.ts index e3485ea..1a7c0d9 100644 --- a/src/lib/eventMapping.test.ts +++ b/src/lib/eventMapping.test.ts @@ -19,12 +19,19 @@ describe("collapseTypes", () => { it("collapses magic link and suspicious groups", () => { expect(collapseTypes(["magic_link_requested"])).toBe("magicLink"); - expect(collapseTypes(["request_suspicious"])).toBe("suspicious"); + expect(collapseTypes(["request_suspicious"])).toBe("security"); + }); + + it("collapses infrastructure event families", () => { + expect(collapseTypes(["refresh_token_success"])).toBe("token"); + expect(collapseTypes(["service_token_success"])).toBe("serviceToken"); + expect(collapseTypes(["oauth_login_success"])).toBe("oauth"); + expect(collapseTypes(["step_up_success"])).toBe("stepUp"); }); it("falls back to the first concrete type", () => { - expect(collapseTypes(["token_refreshed", "token_issued"])).toBe( - "token_refreshed", + expect(collapseTypes(["unknown_event", "unknown_other"])).toBe( + "unknown_event", ); }); diff --git a/src/lib/eventMapping.ts b/src/lib/eventMapping.ts index 9910785..0172598 100644 --- a/src/lib/eventMapping.ts +++ b/src/lib/eventMapping.ts @@ -5,35 +5,14 @@ */ // src/lib/eventTypeMapping.ts +import { getEventCategory } from "./eventCategories"; export function collapseTypes(types: string[]): string { if (!types || types.length === 0) return ""; - if (types.includes("login_success") || types.includes("login_failed")) { - return "login"; - } + const matchedCategory = types + .map((type) => getEventCategory(type)) + .find((category) => category.value !== "other"); - if (types.includes("otp_success") || types.includes("otp_failed")) { - return "otp"; - } - - if ( - types.includes("webauthn_login_success") || - types.includes("webauthn_login_failed") - ) { - return "webauthn"; - } - - if ( - types.includes("magic_link_success") || - types.includes("magic_link_requested") - ) { - return "magicLink"; - } - - if (types.some((t) => t.includes("suspicious"))) { - return "suspicious"; - } - - return types[0]; // fallback + return matchedCategory?.value ?? types[0]; } diff --git a/src/pages/Overview.tsx b/src/pages/Overview.tsx index d9157ec..9be6be9 100644 --- a/src/pages/Overview.tsx +++ b/src/pages/Overview.tsx @@ -47,11 +47,8 @@ export default function Overview() { }, grouped.summary[0]) : undefined; - const securitySignalCount = grouped?.summary.length - ? grouped.summary - .filter((item) => item.type.includes("suspicious")) - .reduce((sum, item) => sum + item.count, 0) - : 0; + const securitySignalCount = + grouped?.summary.find((item) => item.type === "security")?.count ?? 0; return (
@@ -90,7 +87,7 @@ export default function Overview() { />
diff --git a/src/pages/SystemConfig.test.tsx b/src/pages/SystemConfig.test.tsx index 277a87f..81e6748 100644 --- a/src/pages/SystemConfig.test.tsx +++ b/src/pages/SystemConfig.test.tsx @@ -32,6 +32,7 @@ const baseConfig = { delay_after: 10, login_methods: ["passkey", "magic_link"], passkey_login_fallback_enabled: true, + oauth_providers: [], rpid: "example.com", origins: ["https://example.com"], }; @@ -65,4 +66,53 @@ describe("SystemConfigPage", () => { }), ); }); + + it("adds OAuth provider configuration without a secret value", () => { + render(); + + fireEvent.change(screen.getByLabelText(/provider id/i), { + target: { value: "google" }, + }); + fireEvent.change(screen.getByLabelText(/display name/i), { + target: { value: "Google" }, + }); + fireEvent.change(screen.getByLabelText(/client id/i), { + target: { value: "client-id" }, + }); + fireEvent.change(screen.getByLabelText(/client secret env/i), { + target: { value: "GOOGLE_CLIENT_SECRET" }, + }); + fireEvent.change(screen.getByLabelText(/authorization url/i), { + target: { value: "https://accounts.google.com/o/oauth2/v2/auth" }, + }); + fireEvent.change(screen.getByLabelText(/token url/i), { + target: { value: "https://oauth2.googleapis.com/token" }, + }); + fireEvent.change(screen.getByLabelText(/user info url/i), { + target: { value: "https://openidconnect.googleapis.com/v1/userinfo" }, + }); + fireEvent.change(screen.getByLabelText(/redirect uri/i), { + target: { value: "https://example.com/oauth/callback" }, + }); + fireEvent.change(screen.getByLabelText(/scopes/i), { + target: { value: "openid, email, profile" }, + }); + + fireEvent.click(screen.getByRole("button", { name: /add provider/i })); + fireEvent.click(screen.getByRole("button", { name: /save changes/i })); + + expect(mocks.mutate).toHaveBeenCalledWith( + expect.objectContaining({ + oauth_providers: [ + expect.objectContaining({ + id: "google", + name: "Google", + clientId: "client-id", + clientSecretEnv: "GOOGLE_CLIENT_SECRET", + scopes: ["openid", "email", "profile"], + }), + ], + }), + ); + }); }); diff --git a/src/pages/SystemConfig.tsx b/src/pages/SystemConfig.tsx index 47e91bb..b4dd717 100644 --- a/src/pages/SystemConfig.tsx +++ b/src/pages/SystemConfig.tsx @@ -9,6 +9,7 @@ import { KeyRound, ShieldCheck, TimerReset, Waypoints } from "lucide-react"; import { useSystemConfig, type LoginMethod, + type OAuthProviderConfig, type SystemConfig, } from "../hooks/useSystemConfig"; import { useUpdateSystemConfig } from "../hooks/useUpdateSystemConfig"; @@ -43,6 +44,11 @@ const LOGIN_METHOD_OPTIONS: { label: "SMS OTP", description: "One-time SMS codes after login initiation.", }, + { + value: "oauth", + label: "OAuth", + description: "External identity providers such as Google or GitHub.", + }, ]; export default function SystemConfigPage() { @@ -139,6 +145,10 @@ export default function SystemConfigPage() { label="Login methods" value={`${form.login_methods.length}`} /> +
@@ -278,6 +288,16 @@ export default function SystemConfigPage() { +
+ updateField("oauth_providers", value)} + /> +
+
onChange(e.target.value)} className="w-full rounded-md border border-subtle bg-surface-alt px-3 py-2 text-sm outline-none transition focus:border-[var(--primary)] focus:ring-1 focus:ring-[var(--primary)]" @@ -569,6 +590,203 @@ function CheckboxField({ ); } +const emptyOAuthProvider: OAuthProviderConfig = { + id: "", + name: "", + enabled: true, + clientId: "", + clientSecretEnv: "", + authorizationUrl: "", + tokenUrl: "", + userInfoUrl: "", + scopes: [], + redirectUri: "", + subjectJsonPath: "sub", + emailJsonPath: "email", + nameJsonPath: "name", + allowSignup: true, +}; + +function OAuthProvidersEditor({ + providers, + setProviders, +}: { + providers: OAuthProviderConfig[]; + setProviders: (providers: OAuthProviderConfig[]) => void; +}) { + const [draft, setDraft] = useState(emptyOAuthProvider); + + const addProvider = () => { + if (!draft.id || !draft.name || !draft.clientId || !draft.clientSecretEnv) { + return; + } + + setProviders([ + ...providers.filter((provider) => provider.id !== draft.id), + { + ...draft, + scopes: draft.scopes.filter(Boolean), + redirectUri: draft.redirectUri || undefined, + nameJsonPath: draft.nameJsonPath || undefined, + }, + ]); + setDraft(emptyOAuthProvider); + }; + + const updateProvider = ( + index: number, + updates: Partial, + ) => { + setProviders( + providers.map((provider, currentIndex) => + currentIndex === index ? { ...provider, ...updates } : provider, + ), + ); + }; + + const removeProvider = (index: number) => { + setProviders(providers.filter((_, currentIndex) => currentIndex !== index)); + }; + + return ( +
+
+ setDraft({ ...draft, id: value })} + /> + setDraft({ ...draft, name: value })} + /> + setDraft({ ...draft, clientId: value })} + /> + setDraft({ ...draft, clientSecretEnv: value })} + /> + setDraft({ ...draft, authorizationUrl: value })} + /> + setDraft({ ...draft, tokenUrl: value })} + /> + setDraft({ ...draft, userInfoUrl: value })} + /> + setDraft({ ...draft, redirectUri: value })} + /> + + setDraft({ + ...draft, + scopes: value + .split(",") + .map((scope) => scope.trim()) + .filter(Boolean), + }) + } + /> + setDraft({ ...draft, subjectJsonPath: value })} + /> + setDraft({ ...draft, emailJsonPath: value })} + /> + setDraft({ ...draft, nameJsonPath: value })} + /> +
+ +
+
+ +
+ {providers.map((provider, index) => ( +
+
+
+
{provider.name}
+
{provider.id}
+
+ Secret env: {provider.clientSecretEnv} +
+
+ +
+ + +
+
+ +
+ Client ID: {provider.clientId} + + Scopes: {provider.scopes.join(", ") || "None"} + + + Authorization: {provider.authorizationUrl} + + + User info: {provider.userInfoUrl} + +
+
+ ))} + + {providers.length === 0 && ( +
+ No OAuth providers configured. +
+ )} +
+
+ ); +} + function OriginsEditor({ origins, setOrigins, diff --git a/src/types/authEventTypes.ts b/src/types/authEventTypes.ts index 305a09a..e9e26c2 100644 --- a/src/types/authEventTypes.ts +++ b/src/types/authEventTypes.ts @@ -8,23 +8,22 @@ import { z } from "zod"; export const AuthEventTypeEnum = z.enum([ - "login", - "magicLink", - "suspicious", - "otp", - "webauthn", "auth_action_incremented", "bearer_token_failed", "bearer_token_success", "bearer_token_suspicious", + "bootstrap_admin_check_skipped", + "bootstrap_admin_granted", "cookie_token_failed", "cookie_token_success", "cookie_token_suspicious", + "credentials_deleted", "informational", "internal_user_updated_by_owner", "jwks_failed", "jwks_success", "jwks_suspicious", + "login_challenge", "login_failed", "login_success", "login_suspicious", @@ -37,7 +36,10 @@ export const AuthEventTypeEnum = z.enum([ "mfa_otp_failed", "mfa_otp_success", "mfa_otp_suspicious", - "notication_sent", + "notification_sent", + "oauth_login_failed", + "oauth_login_started", + "oauth_login_success", "otp_failed", "otp_success", "otp_suspicious", @@ -50,17 +52,29 @@ export const AuthEventTypeEnum = z.enum([ "registration_failed", "registration_success", "registration_suspicious", + "request_suspicious", "service_token_failed", "service_token_rotated", "service_token_success", "service_token_suspicious", + "step_up_challenge", + "step_up_failed", + "step_up_success", + "step_up_suspicious", "system_config_error", "system_config_read", "system_config_updated", + "totp_disabled", + "totp_enrollment_started", + "totp_enrollment_success", + "totp_failed", + "totp_success", + "totp_suspicious", "user_created", "user_data_failed", "user_data_success", "user_data_suspicious", + "user_deleted", "verify_otp_failed", "verify_otp_success", "verify_otp_suspicious",