mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 04:15:30 +00:00
fix issue with new auth providers not being shown in login page
This commit is contained in:
parent
97a2a3efac
commit
a2e06266db
8 changed files with 177 additions and 46 deletions
|
|
@ -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
|
||||
|
|
|
|||
1
packages/web/public/keycloak.svg
Normal file
1
packages/web/public/keycloak.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="167.117" height="150.658" viewBox="0 0 44.216 39.861"><path d="m88.61 138.456 5.716-9.865 23.018-.004 5.686 9.965.007 19.932-5.691 9.957-23.012.008-5.782-9.965z" style="display:inline;fill:#4d4d4d;fill-opacity:1;stroke-width:.264583" transform="translate(-82.815 -128.588)"/><path d="M88.552 158.481h10.375l-5.699-10.041 4.634-9.982-9.252-.002-5.795 10.065" style="fill:#ededed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:.330729" transform="translate(-82.815 -128.588)"/><path d="M102.073 158.481h7.582l6.706-9.773-6.589-10.156h-8.921l-5.373 9.814z" style="fill:#e0e0e0;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:.330729" transform="translate(-82.815 -128.588)"/><path d="m82.815 148.52 5.738 9.964h10.374l-5.636-9.93z" style="fill:#acacac;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:.330729" transform="translate(-82.815 -128.588)"/><path d="m95.589 148.522 6.484 9.963h7.582l6.601-9.959z" style="fill:#9e9e9e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:.330729" transform="translate(-82.815 -128.588)"/><path d="m98.157 148.529-1.958.569-1.877-.572 7.667-13.288 1.918 3.316" style="fill:#00b8e3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:.330729" transform="translate(-82.815 -128.588)"/><path d="m103.9 158.482-1.909 3.332-5.093-5.487-2.58-7.797v-.004h3.838" style="fill:#33c6e9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:.330729" transform="translate(-82.815 -128.588)"/><path d="M94.322 148.526h-.003v.003l-1.918 3.322-1.925-3.307 1.952-3.386 5.728-9.92h3.834" style="fill:#008aaa;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:.330729" transform="translate(-82.815 -128.588)"/><path d="M115.42 158.481h11.611l-.007-19.93h-11.605z" style="fill:#d4d4d4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:.330729" transform="translate(-82.815 -128.588)"/><path d="M115.42 148.554v9.93h11.59v-9.93z" style="fill:#919191;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:.330729" transform="translate(-82.815 -128.588)"/><path d="M101.992 161.817h-3.836l-5.755-9.966 1.918-3.321z" style="fill:#00b8e3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:.330729" transform="translate(-82.815 -128.588)"/><path d="m117.333 148.526-7.669 13.289c-.705-1.036-1.913-3.331-1.913-3.331l5.753-9.959z" style="fill:#008aaa;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:.330729" transform="translate(-82.815 -128.588)"/><path d="m113.495 161.815-3.831-.001 7.67-13.288 1.917-3.317 1.921 3.34m-3.839-.023h-3.828l-5.755-9.973 1.905-3.314 4.658 5.922z" style="fill:#00b8e3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:.330729" transform="translate(-82.815 -128.588)"/><path d="M119.25 145.205v.003l-1.917 3.318-7.677-13.286 3.841.002z" style="fill:#33c6e9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:.330729" transform="translate(-82.815 -128.588)"/></svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
9
packages/web/public/microsoft_entra.svg
Normal file
9
packages/web/public/microsoft_entra.svg
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="uuid-f8d4d392-7c12-4bd9-baff-66fbf7814b91" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||
<path d="m3.802,14.032c.388.242,1.033.511,1.715.511.621,0,1.198-.18,1.676-.487,0,0,.001,0,.002-.001l1.805-1.128v4.073c-.286,0-.574-.078-.824-.234l-4.374-2.734Z" fill="#225086"/>
|
||||
<path d="m7.853,1.507L.353,9.967c-.579.654-.428,1.642.323,2.111,0,0,2.776,1.735,3.126,1.954.388.242,1.033.511,1.715.511.621,0,1.198-.18,1.676-.487,0,0,.001,0,.002-.001l1.805-1.128-4.364-2.728,4.365-4.924V1s0,0,0,0c-.424,0-.847.169-1.147.507Z" fill="#6df"/>
|
||||
<polygon points="4.636 10.199 4.688 10.231 9 12.927 9.001 12.927 9.001 12.927 9.001 5.276 9 5.275 4.636 10.199" fill="#cbf8ff"/>
|
||||
<path d="m17.324,12.078c.751-.469.902-1.457.323-2.111l-4.921-5.551c-.397-.185-.842-.291-1.313-.291-.925,0-1.752.399-2.302,1.026l-.109.123h0s4.364,4.924,4.364,4.924h0s0,0,0,0l-4.365,2.728v4.073c.287,0,.573-.078.823-.234l7.5-4.688Z" fill="#074793"/>
|
||||
<path d="m9.001,1v4.275s.109-.123.109-.123c.55-.627,1.377-1.026,2.302-1.026.472,0,.916.107,1.313.291l-2.579-2.909c-.299-.338-.723-.507-1.146-.507Z" fill="#0294e4"/>
|
||||
<polygon points="13.365 10.199 13.365 10.199 13.365 10.199 9.001 5.276 9.001 12.926 13.365 10.199" fill="#96bcc2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
3
packages/web/public/okta.svg
Normal file
3
packages/web/public/okta.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36">
|
||||
<path d="M19.8,.26l-.74,9.12c-.35-.04-.7-.06-1.06-.06-.45,0-.89,.03-1.32,.1l-.42-4.42c-.01-.14,.1-.26,.24-.26h.75l-.36-4.47c-.01-.14,.1-.26,.23-.26h2.45c.14,0,.25,.12,.23,.26h0Zm-6.18,.45c-.04-.13-.18-.21-.31-.16l-2.3,.84c-.13,.05-.19,.2-.13,.32l1.87,4.08-.71,.26c-.13,.05-.19,.2-.13,.32l1.91,4.01c.69-.38,1.44-.67,2.23-.85L13.63,.71ZM7.98,3.25l5.29,7.46c-.67,.44-1.28,.96-1.8,1.56l-3.17-3.12c-.1-.1-.09-.26,.01-.35l.58-.48-3.15-3.19c-.1-.1-.09-.26,.02-.35l1.87-1.57c.11-.09,.26-.07,.34,.04ZM3.54,7.57c-.11-.08-.27-.04-.34,.08l-1.22,2.12c-.07,.12-.02,.27,.1,.33l4.06,1.92-.38,.65c-.07,.12-.02,.28,.11,.33l4.04,1.85c.29-.75,.68-1.45,1.16-2.08L3.54,7.57ZM.55,13.33c.02-.14,.16-.22,.29-.19l8.85,2.31c-.23,.75-.36,1.54-.38,2.36l-4.43-.36c-.14-.01-.24-.14-.21-.28l.13-.74-4.47-.42c-.14-.01-.23-.14-.21-.28l.42-2.41h0Zm-.33,5.98c-.14,.01-.23,.14-.21,.28l.43,2.41c.02,.14,.16,.22,.29,.19l4.34-1.13,.13,.74c.02,.14,.16,.22,.29,.19l4.28-1.18c-.25-.74-.41-1.53-.45-2.34L.21,19.31Zm1.42,6.34c-.07-.12-.02-.27,.1-.33l8.26-3.92c.31,.74,.73,1.43,1.23,2.05l-3.62,2.58c-.11,.08-.27,.05-.34-.07l-.38-.66-3.69,2.55c-.11,.08-.27,.04-.34-.08l-1.23-2.12Zm10.01-1.72l-6.43,6.51c-.1,.1-.09,.26,.02,.35l1.88,1.57c.11,.09,.26,.07,.34-.04l2.6-3.66,.58,.49c.11,.09,.27,.07,.35-.05l2.52-3.66c-.68-.42-1.31-.93-1.85-1.51Zm-1.27,10.45c-.13-.05-.19-.2-.13-.32l3.81-8.32c.7,.36,1.46,.63,2.25,.78l-1.12,4.3c-.03,.13-.18,.21-.31,.16l-.71-.26-1.19,4.33c-.04,.13-.18,.21-.31,.16l-2.3-.84h0Zm6.56-7.75l-.74,9.12c-.01,.14,.1,.26,.23,.26h2.45c.14,0,.25-.12,.23-.26l-.36-4.47h.75c.14,0,.25-.12,.24-.26l-.42-4.42c-.43,.07-.87,.1-1.32,.1-.36,0-.71-.02-1.06-.07h0ZM25.76,1.94c.06-.13,0-.27-.13-.32l-2.3-.84c-.13-.05-.27,.03-.31,.16l-1.19,4.33-.71-.26c-.13-.05-.27,.03-.31,.16l-1.12,4.3c.8,.16,1.55,.43,2.25,.78L25.76,1.94h0Zm5.02,3.63l-6.43,6.51c-.54-.58-1.16-1.09-1.85-1.51l2.52-3.66c.08-.11,.24-.14,.35-.05l.58,.49,2.6-3.66c.08-.11,.24-.13,.34-.04l1.88,1.57c.11,.09,.11,.25,.02,.35Zm3.48,5.12c.13-.06,.17-.21,.1-.33l-1.23-2.12c-.07-.12-.23-.15-.34-.08l-3.69,2.55-.38-.65c-.07-.12-.23-.16-.34-.07l-3.62,2.58c.5,.62,.91,1.31,1.23,2.05l8.26-3.92Zm1.3,3.32l.42,2.41c.02,.14-.07,.26-.21,.28l-9.11,.85c-.04-.82-.2-1.6-.45-2.34l4.28-1.18c.13-.04,.27,.05,.29,.19l.13,.74,4.34-1.13c.13-.03,.27,.05,.29,.19h0Zm-.41,8.85c.13,.03,.27-.05,.29-.19l.42-2.41c.02-.14-.07-.26-.21-.28l-4.47-.42,.13-.74c.02-.14-.07-.26-.21-.28l-4.43-.36c-.02,.82-.15,1.61-.38,2.36l8.85,2.31h0Zm-2.36,5.5c-.07,.12-.23,.15-.34,.08l-7.53-5.2c.48-.63,.87-1.33,1.16-2.08l4.04,1.85c.13,.06,.18,.21,.11,.33l-.38,.65,4.06,1.92c.12,.06,.17,.21,.1,.33l-1.22,2.12h0Zm-10.07-3.07l5.29,7.46c.08,.11,.24,.13,.34,.04l1.87-1.57c.11-.09,.11-.25,.02-.35l-3.15-3.19,.58-.48c.11-.09,.11-.25,.01-.35l-3.17-3.12c-.53,.6-1.13,1.13-1.8,1.56h0Zm-.05,10.16c-.13,.05-.27-.03-.31-.16l-2.42-8.82c.79-.18,1.54-.47,2.23-.85l1.91,4.01c.06,.13,0,.28-.13,.32l-.71,.26,1.87,4.08c.06,.13,0,.27-.13,.32l-2.3,.84h0Z" fill="#191919" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3 KiB |
|
|
@ -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 (
|
||||
<div className="flex flex-col items-center justify-center w-full">
|
||||
<div className="mb-6 flex flex-col items-center">
|
||||
|
|
@ -71,36 +92,28 @@ export const LoginForm = ({ callbackUrl, error, enabledMethods }: LoginFormProps
|
|||
)}
|
||||
<DividerSet
|
||||
elements={[
|
||||
...(enabledMethods.github || enabledMethods.google ? [
|
||||
<>
|
||||
{enabledMethods.github && (
|
||||
...(oauthProviders.length > 0 ? [
|
||||
<div key="oauth-providers" className="w-full space-y-3">
|
||||
{oauthProviders.map((provider) => {
|
||||
const providerInfo = getAuthProviderInfo(provider.id);
|
||||
return (
|
||||
<ProviderButton
|
||||
key="github"
|
||||
name="GitHub"
|
||||
logo={getCodeHostIcon("github")!}
|
||||
key={provider.id}
|
||||
name={providerInfo.displayName}
|
||||
logo={providerInfo.icon}
|
||||
onClick={() => {
|
||||
captureEvent("wa_login_with_github", {});
|
||||
onSignInWithOauth("github")
|
||||
captureEvent(getLoginEventName(provider.id), {});
|
||||
onSignInWithOauth(provider.id);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{enabledMethods.google && (
|
||||
<ProviderButton
|
||||
key="google"
|
||||
name="Google"
|
||||
logo={{ src: googleLogo }}
|
||||
onClick={() => {
|
||||
captureEvent("wa_login_with_google", {});
|
||||
onSignInWithOauth("google")
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
] : []),
|
||||
...(enabledMethods.magicLink ? [
|
||||
...(hasMagicLink ? [
|
||||
<MagicLinkForm key="magic-link" callbackUrl={callbackUrl} />
|
||||
] : []),
|
||||
...(enabledMethods.credentials ? [
|
||||
...(hasCredentials ? [
|
||||
<CredentialsForm key="credentials" callbackUrl={callbackUrl} />
|
||||
] : [])
|
||||
]}
|
||||
|
|
@ -120,7 +133,7 @@ const ProviderButton = ({
|
|||
className,
|
||||
}: {
|
||||
name: string;
|
||||
logo: { src: string, className?: string };
|
||||
logo: { src: string, className?: string } | null;
|
||||
onClick: () => void;
|
||||
className?: string;
|
||||
}) => {
|
||||
|
|
|
|||
|
|
@ -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) {
|
|||
<LoginForm
|
||||
callbackUrl={searchParams.callbackUrl}
|
||||
error={searchParams.error}
|
||||
enabledMethods={{
|
||||
github: providerMap.some(provider => 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}
|
||||
/>
|
||||
</div>
|
||||
<Footer />
|
||||
|
|
|
|||
|
|
@ -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: {},
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue