diff --git a/CHANGELOG.md b/CHANGELOG.md
index ccaca8b0..94e94262 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Fixed
+- Fixed issue where new oauth providers weren't being display in the login page
+
## [4.0.1] - 2025-05-28
### Fixed
diff --git a/packages/web/public/keycloak.svg b/packages/web/public/keycloak.svg
new file mode 100644
index 00000000..44798d21
--- /dev/null
+++ b/packages/web/public/keycloak.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/web/public/microsoft_entra.svg b/packages/web/public/microsoft_entra.svg
new file mode 100644
index 00000000..0ed35fb7
--- /dev/null
+++ b/packages/web/public/microsoft_entra.svg
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/packages/web/public/okta.svg b/packages/web/public/okta.svg
new file mode 100644
index 00000000..75b1a850
--- /dev/null
+++ b/packages/web/public/okta.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/packages/web/src/app/login/components/loginForm.tsx b/packages/web/src/app/login/components/loginForm.tsx
index b03a3b3d..132e41ec 100644
--- a/packages/web/src/app/login/components/loginForm.tsx
+++ b/packages/web/src/app/login/components/loginForm.tsx
@@ -1,12 +1,11 @@
'use client';
import { Button } from "@/components/ui/button";
-import googleLogo from "@/public/google.svg";
import Image from "next/image";
import { signIn } from "next-auth/react";
import { Fragment, useCallback, useMemo } from "react";
import { Card } from "@/components/ui/card";
-import { cn, getCodeHostIcon } from "@/lib/utils";
+import { cn, getAuthProviderInfo } from "@/lib/utils";
import { MagicLinkForm } from "./magicLinkForm";
import { CredentialsForm } from "./credentialsForm";
import { SourcebotLogo } from "@/app/components/sourcebotLogo";
@@ -22,15 +21,10 @@ const PRIVACY_POLICY_URL = "https://sourcebot.dev/privacy";
interface LoginFormProps {
callbackUrl?: string;
error?: string;
- enabledMethods: {
- github: boolean;
- google: boolean;
- magicLink: boolean;
- credentials: boolean;
- }
+ providers: Array<{ id: string; name: string }>;
}
-export const LoginForm = ({ callbackUrl, error, enabledMethods }: LoginFormProps) => {
+export const LoginForm = ({ callbackUrl, error, providers }: LoginFormProps) => {
const captureEvent = useCaptureEvent();
const onSignInWithOauth = useCallback((provider: string) => {
signIn(provider, { redirectTo: callbackUrl ?? "/" });
@@ -50,6 +44,33 @@ export const LoginForm = ({ callbackUrl, error, enabledMethods }: LoginFormProps
}
}, [error]);
+ // Separate OAuth providers from special auth methods
+ const oauthProviders = providers.filter(p =>
+ !["credentials", "nodemailer"].includes(p.id)
+ );
+ const hasCredentials = providers.some(p => p.id === "credentials");
+ const hasMagicLink = providers.some(p => p.id === "nodemailer");
+
+ // Helper function to get the correct analytics event name
+ const getLoginEventName = (providerId: string) => {
+ switch (providerId) {
+ case "github":
+ return "wa_login_with_github" as const;
+ case "google":
+ return "wa_login_with_google" as const;
+ case "gitlab":
+ return "wa_login_with_gitlab" as const;
+ case "okta":
+ return "wa_login_with_okta" as const;
+ case "keycloak":
+ return "wa_login_with_keycloak" as const;
+ case "microsoft-entra-id":
+ return "wa_login_with_microsoft_entra_id" as const;
+ default:
+ return "wa_login_with_github" as const; // fallback
+ }
+ };
+
return (
@@ -71,36 +92,28 @@ export const LoginForm = ({ callbackUrl, error, enabledMethods }: LoginFormProps
)}
- {enabledMethods.github && (
- {
- captureEvent("wa_login_with_github", {});
- onSignInWithOauth("github")
- }}
- />
- )}
- {enabledMethods.google && (
- {
- captureEvent("wa_login_with_google", {});
- onSignInWithOauth("google")
- }}
- />
- )}
- >
+ ...(oauthProviders.length > 0 ? [
+
+ {oauthProviders.map((provider) => {
+ const providerInfo = getAuthProviderInfo(provider.id);
+ return (
+
{
+ captureEvent(getLoginEventName(provider.id), {});
+ onSignInWithOauth(provider.id);
+ }}
+ />
+ );
+ })}
+
] : []),
- ...(enabledMethods.magicLink ? [
+ ...(hasMagicLink ? [
] : []),
- ...(enabledMethods.credentials ? [
+ ...(hasCredentials ? [
] : [])
]}
@@ -120,7 +133,7 @@ const ProviderButton = ({
className,
}: {
name: string;
- logo: { src: string, className?: string };
+ logo: { src: string, className?: string } | null;
onClick: () => void;
className?: string;
}) => {
diff --git a/packages/web/src/app/login/page.tsx b/packages/web/src/app/login/page.tsx
index 081c2bbb..1487d182 100644
--- a/packages/web/src/app/login/page.tsx
+++ b/packages/web/src/app/login/page.tsx
@@ -20,11 +20,11 @@ export default async function Login({ searchParams }: LoginProps) {
}
const providers = getProviders();
- const providerMap = providers
+ const providerData = providers
.map((provider) => {
if (typeof provider === "function") {
- const providerData = provider()
- return { id: providerData.id, name: providerData.name }
+ const providerInfo = provider()
+ return { id: providerInfo.id, name: providerInfo.name }
} else {
return { id: provider.id, name: provider.name }
}
@@ -36,12 +36,7 @@ export default async function Login({ searchParams }: LoginProps) {
provider.id === "github"),
- google: providerMap.some(provider => provider.id === "google"),
- magicLink: providerMap.some(provider => provider.id === "nodemailer"),
- credentials: providerMap.some(provider => provider.id === "credentials"),
- }}
+ providers={providerData}
/>
diff --git a/packages/web/src/lib/posthogEvents.ts b/packages/web/src/lib/posthogEvents.ts
index 3f601c0d..f164384a 100644
--- a/packages/web/src/lib/posthogEvents.ts
+++ b/packages/web/src/lib/posthogEvents.ts
@@ -216,6 +216,10 @@ export type PosthogEventMap = {
//////////////////////////////////////////////////////////////////
wa_login_with_github: {},
wa_login_with_google: {},
+ wa_login_with_gitlab: {},
+ wa_login_with_okta: {},
+ wa_login_with_keycloak: {},
+ wa_login_with_microsoft_entra_id: {},
wa_login_with_magic_link: {},
wa_login_with_credentials: {},
//////////////////////////////////////////////////////////////////
diff --git a/packages/web/src/lib/utils.ts b/packages/web/src/lib/utils.ts
index 0d01f9ff..4e416ac7 100644
--- a/packages/web/src/lib/utils.ts
+++ b/packages/web/src/lib/utils.ts
@@ -6,6 +6,10 @@ import giteaLogo from "@/public/gitea.svg";
import gerritLogo from "@/public/gerrit.svg";
import bitbucketLogo from "@/public/bitbucket.svg";
import gitLogo from "@/public/git.svg";
+import googleLogo from "@/public/google.svg";
+import oktaLogo from "@/public/okta.svg";
+import keycloakLogo from "@/public/keycloak.svg";
+import microsoftLogo from "@/public/microsoft_entra.svg";
import { ServiceError } from "./serviceError";
import { StatusCodes } from "http-status-codes";
import { ErrorCode } from "./errorCodes";
@@ -44,6 +48,105 @@ export type CodeHostType =
"bitbucket-server" |
"generic-git-host";
+export type AuthProviderType =
+ "github" |
+ "gitlab" |
+ "google" |
+ "okta" |
+ "keycloak" |
+ "microsoft-entra-id" |
+ "credentials" |
+ "nodemailer";
+
+type AuthProviderInfo = {
+ id: string;
+ name: string;
+ displayName: string;
+ icon: { src: string; className?: string } | null;
+}
+
+export const getAuthProviderInfo = (providerId: string): AuthProviderInfo => {
+ switch (providerId) {
+ case "github":
+ return {
+ id: "github",
+ name: "GitHub",
+ displayName: "GitHub",
+ icon: {
+ src: githubLogo,
+ className: "dark:invert",
+ },
+ };
+ case "gitlab":
+ return {
+ id: "gitlab",
+ name: "GitLab",
+ displayName: "GitLab",
+ icon: {
+ src: gitlabLogo,
+ },
+ };
+ case "google":
+ return {
+ id: "google",
+ name: "Google",
+ displayName: "Google",
+ icon: {
+ src: googleLogo,
+ },
+ };
+ case "okta":
+ return {
+ id: "okta",
+ name: "Okta",
+ displayName: "Okta",
+ icon: {
+ src: oktaLogo,
+ className: "dark:invert",
+ },
+ };
+ case "keycloak":
+ return {
+ id: "keycloak",
+ name: "Keycloak",
+ displayName: "Keycloak",
+ icon: {
+ src: keycloakLogo,
+ },
+ };
+ case "microsoft-entra-id":
+ return {
+ id: "microsoft-entra-id",
+ name: "Microsoft Entra ID",
+ displayName: "Microsoft Entra ID",
+ icon: {
+ src: microsoftLogo,
+ },
+ };
+ case "credentials":
+ return {
+ id: "credentials",
+ name: "Credentials",
+ displayName: "Email & Password",
+ icon: null, // No icon needed for credentials
+ };
+ case "nodemailer":
+ return {
+ id: "nodemailer",
+ name: "Email",
+ displayName: "Email Code",
+ icon: null, // No icon needed for email
+ };
+ default:
+ return {
+ id: providerId,
+ name: providerId,
+ displayName: providerId.charAt(0).toUpperCase() + providerId.slice(1),
+ icon: null,
+ };
+ }
+};
+
type CodeHostInfo = {
type: CodeHostType;
displayName: string;