Skip to content

Commit 6db7aa3

Browse files
committed
properly handle sso idps from config
1 parent e047eb0 commit 6db7aa3

File tree

9 files changed

+47
-47
lines changed

9 files changed

+47
-47
lines changed

packages/web/src/app/components/authMethodSelector.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import { CredentialsForm } from "@/app/login/components/credentialsForm";
88
import { DividerSet } from "@/app/components/dividerSet";
99
import { ProviderButton } from "@/app/components/providerButton";
1010
import { AuthSecurityNotice } from "@/app/components/authSecurityNotice";
11-
import type { AuthProvider } from "@/lib/authProviders";
11+
import type { IdentityProviderMetadata } from "@/lib/authProviders";
1212

1313
interface AuthMethodSelectorProps {
14-
providers: AuthProvider[];
14+
providers: IdentityProviderMetadata[];
1515
callbackUrl?: string;
1616
context: "login" | "signup";
1717
onProviderClick?: (providerId: string) => void;
@@ -35,11 +35,11 @@ export const AuthMethodSelector = ({
3535
}, [callbackUrl, onProviderClick]);
3636

3737
// Separate OAuth providers from special auth methods
38-
const oauthProviders = providers.filter(p =>
38+
const oauthProviders = providers.filter(p => p.purpose === "sso" &&
3939
!["credentials", "nodemailer"].includes(p.id)
4040
);
41-
const hasCredentials = providers.some(p => p.id === "credentials");
42-
const hasMagicLink = providers.some(p => p.id === "nodemailer");
41+
const hasCredentials = providers.some(p => p.purpose === "sso" && p.id === "credentials");
42+
const hasMagicLink = providers.some(p => p.purpose === "sso" && p.id === "nodemailer");
4343

4444
return (
4545
<>

packages/web/src/app/invite/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
77
import { SourcebotLogo } from "@/app/components/sourcebotLogo";
88
import { AuthMethodSelector } from "@/app/components/authMethodSelector";
99
import { LogoutEscapeHatch } from "@/app/components/logoutEscapeHatch";
10-
import { getAuthProviders } from "@/lib/authProviders";
10+
import { getIdentityProviderMetadata, IdentityProviderMetadata } from "@/lib/authProviders";
1111
import { JoinOrganizationCard } from "@/app/components/joinOrganizationCard";
1212

1313
interface InvitePageProps {
@@ -30,7 +30,7 @@ export default async function InvitePage(props: InvitePageProps) {
3030

3131
const session = await auth();
3232
if (!session) {
33-
const providers = getAuthProviders();
33+
const providers = getIdentityProviderMetadata();
3434
return <WelcomeCard inviteLinkId={inviteLinkId} providers={providers} />;
3535
}
3636

@@ -57,7 +57,7 @@ export default async function InvitePage(props: InvitePageProps) {
5757
);
5858
}
5959

60-
function WelcomeCard({ inviteLinkId, providers }: { inviteLinkId: string; providers: import("@/lib/authProviders").AuthProvider[] }) {
60+
function WelcomeCard({ inviteLinkId, providers }: { inviteLinkId: string; providers: IdentityProviderMetadata[] }) {
6161
return (
6262
<div className="min-h-screen bg-gradient-to-br from-[var(--background)] to-[var(--accent)]/30 flex items-center justify-center p-6">
6363
<Card className="w-full max-w-md">

packages/web/src/app/login/components/loginForm.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ import { SourcebotLogo } from "@/app/components/sourcebotLogo";
66
import { AuthMethodSelector } from "@/app/components/authMethodSelector";
77
import useCaptureEvent from "@/hooks/useCaptureEvent";
88
import Link from "next/link";
9-
import type { AuthProvider } from "@/lib/authProviders";
9+
import type { IdentityProviderMetadata } from "@/lib/authProviders";
1010

1111
interface LoginFormProps {
1212
callbackUrl?: string;
1313
error?: string;
14-
providers: AuthProvider[];
14+
providers: IdentityProviderMetadata[];
1515
context: "login" | "signup";
1616
}
1717

packages/web/src/app/login/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { auth } from "@/auth";
22
import { LoginForm } from "./components/loginForm";
33
import { redirect } from "next/navigation";
44
import { Footer } from "@/app/components/footer";
5-
import { getAuthProviders } from "@/lib/authProviders";
5+
import { getIdentityProviderMetadata } from "@/lib/authProviders";
66
import { getOrgFromDomain } from "@/data/org";
77
import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants";
88

@@ -25,7 +25,7 @@ export default async function Login(props: LoginProps) {
2525
return redirect("/onboard");
2626
}
2727

28-
const providers = getAuthProviders();
28+
const providers = getIdentityProviderMetadata();
2929
return (
3030
<div className="flex flex-col min-h-screen bg-backgroundSecondary">
3131
<div className="flex-1 flex flex-col items-center p-4 sm:p-12 w-full">

packages/web/src/app/onboard/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Button } from "@/components/ui/button"
66
import { AuthMethodSelector } from "@/app/components/authMethodSelector"
77
import { SourcebotLogo } from "@/app/components/sourcebotLogo"
88
import { auth } from "@/auth";
9-
import { getAuthProviders } from "@/lib/authProviders";
9+
import { getIdentityProviderMetadata } from "@/lib/authProviders";
1010
import { OrganizationAccessSettings } from "@/app/components/organizationAccessSettings";
1111
import { CompleteOnboardingButton } from "./components/completeOnboardingButton";
1212
import { getOrgFromDomain } from "@/data/org";
@@ -41,7 +41,7 @@ interface ResourceCard {
4141

4242
export default async function Onboarding(props: OnboardingProps) {
4343
const searchParams = await props.searchParams;
44-
const providers = getAuthProviders();
44+
const providers = getIdentityProviderMetadata();
4545
const org = await getOrgFromDomain(SINGLE_TENANT_ORG_DOMAIN);
4646
const session = await auth();
4747

packages/web/src/app/signup/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { LoginForm } from "../login/components/loginForm";
33
import { redirect } from "next/navigation";
44
import { Footer } from "@/app/components/footer";
55
import { createLogger } from "@sourcebot/logger";
6-
import { getAuthProviders } from "@/lib/authProviders";
6+
import { getIdentityProviderMetadata } from "@/lib/authProviders";
77
import { getOrgFromDomain } from "@/data/org";
88
import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants";
99

@@ -29,7 +29,7 @@ export default async function Signup(props: LoginProps) {
2929
return redirect("/onboard");
3030
}
3131

32-
const providers = getAuthProviders();
32+
const providers = getIdentityProviderMetadata();
3333
return (
3434
<div className="flex flex-col min-h-screen bg-backgroundSecondary">
3535
<div className="flex-1 flex flex-col items-center p-4 sm:p-12 w-full">

packages/web/src/auth.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,22 @@ import { createTransport } from 'nodemailer';
1313
import { render } from '@react-email/render';
1414
import MagicLinkEmail from './emails/magicLinkEmail';
1515
import bcrypt from 'bcryptjs';
16-
import { getSSOProviders } from '@/ee/features/sso/sso';
16+
import { getEEIdentityProviders } from '@/ee/features/sso/sso';
1717
import { hasEntitlement } from '@sourcebot/shared';
1818
import { onCreateUser } from '@/lib/authUtils';
1919
import { getAuditService } from '@/ee/features/audit/factory';
2020
import { SINGLE_TENANT_ORG_ID } from './lib/constants';
2121

2222
const auditService = getAuditService();
23-
const ssoProviders = hasEntitlement("sso") ? await getSSOProviders() : [];
23+
const eeIdentityProviders = hasEntitlement("sso") ? await getEEIdentityProviders() : [];
2424

2525
export const runtime = 'nodejs';
2626

27+
export type IdentityProvider = {
28+
provider: Provider;
29+
purpose: "sso" | "integration";
30+
}
31+
2732
declare module 'next-auth' {
2833
interface Session {
2934
user: {
@@ -33,16 +38,16 @@ declare module 'next-auth' {
3338
}
3439

3540
declare module 'next-auth/jwt' {
36-
interface JWT {
41+
interface JWT {
3742
userId: string
3843
}
3944
}
4045

4146
export const getProviders = () => {
42-
const providers: Provider[] = ssoProviders;
47+
const providers: IdentityProvider[] = eeIdentityProviders;
4348

4449
if (env.SMTP_CONNECTION_URL && env.EMAIL_FROM_ADDRESS && env.AUTH_EMAIL_CODE_LOGIN_ENABLED === 'true') {
45-
providers.push(EmailProvider({
50+
providers.push({ provider: EmailProvider({
4651
server: env.SMTP_CONNECTION_URL,
4752
from: env.EMAIL_FROM_ADDRESS,
4853
maxAge: 60 * 10,
@@ -66,11 +71,11 @@ export const getProviders = () => {
6671
throw new Error(`Email(s) (${failed.join(", ")}) could not be sent`);
6772
}
6873
}
69-
}));
74+
}), purpose: "sso"});
7075
}
7176

7277
if (env.AUTH_CREDENTIALS_LOGIN_ENABLED === 'true') {
73-
providers.push(Credentials({
78+
providers.push({ provider: Credentials({
7479
credentials: {
7580
email: {},
7681
password: {}
@@ -123,7 +128,7 @@ export const getProviders = () => {
123128
};
124129
}
125130
}
126-
}));
131+
}), purpose: "sso"});
127132
}
128133

129134
return providers;
@@ -193,7 +198,7 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
193198
return session;
194199
},
195200
},
196-
providers: getProviders(),
201+
providers: getProviders().map((provider) => provider.provider),
197202
pages: {
198203
signIn: "/login",
199204
// We set redirect to false in signInOptions so we can pass the email is as a param

packages/web/src/ee/features/sso/sso.ts

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { Provider } from "next-auth/providers";
21
import { env } from "@/env.mjs";
32
import GitHub from "next-auth/providers/github";
43
import Google from "next-auth/providers/google";
@@ -14,68 +13,63 @@ import { onCreateUser } from "@/lib/authUtils";
1413
import { createLogger } from "@sourcebot/logger";
1514
import { hasEntitlement, loadConfig } from "@sourcebot/shared";
1615
import { getTokenFromConfig } from "@sourcebot/crypto";
16+
import type { IdentityProvider } from "@/auth";
1717
import { GCPIAPIdentityProviderConfig, GitHubIdentityProviderConfig, GitLabIdentityProviderConfig, GoogleIdentityProviderConfig, KeycloakIdentityProviderConfig, MicrosoftEntraIDIdentityProviderConfig, OktaIdentityProviderConfig } from "@sourcebot/schemas/v3/index.type";
1818

1919
const logger = createLogger('web-sso');
2020

21-
export const getSSOProviders = async (): Promise<Provider[]> => {
22-
const providers: Provider[] = [];
21+
export const getEEIdentityProviders = async (): Promise<IdentityProvider[]> => {
22+
const providers: IdentityProvider[] = [];
2323

2424
const config = env.CONFIG_PATH ? await loadConfig(env.CONFIG_PATH) : undefined;
2525
const identityProviders = config?.identityProviders ?? [];
2626

2727
for (const identityProvider of identityProviders) {
2828
if (identityProvider.provider === "github") {
2929
const providerConfig = identityProvider as GitHubIdentityProviderConfig;
30-
if (providerConfig.purpose !== "sso") {
31-
continue;
32-
}
3330
const clientId = await getTokenFromConfig(providerConfig.clientId);
3431
const clientSecret = await getTokenFromConfig(providerConfig.clientSecret);
3532
const baseUrl = providerConfig.baseUrl ? await getTokenFromConfig(providerConfig.baseUrl) : undefined;
36-
providers.push(createGitHubProvider(clientId, clientSecret, baseUrl));
33+
providers.push({ provider: createGitHubProvider(clientId, clientSecret, baseUrl), purpose: providerConfig.purpose });
3734
}
3835
if (identityProvider.provider === "gitlab") {
3936
const providerConfig = identityProvider as GitLabIdentityProviderConfig;
40-
if (providerConfig.purpose !== "sso") {
41-
continue;
42-
}
4337
const clientId = await getTokenFromConfig(providerConfig.clientId);
4438
const clientSecret = await getTokenFromConfig(providerConfig.clientSecret);
4539
const baseUrl = providerConfig.baseUrl ? await getTokenFromConfig(providerConfig.baseUrl) : undefined;
46-
providers.push(createGitLabProvider(clientId, clientSecret, baseUrl));
40+
providers.push({ provider: createGitLabProvider(clientId, clientSecret, baseUrl), purpose: providerConfig.purpose });
4741
}
4842
if (identityProvider.provider === "google") {
4943
const providerConfig = identityProvider as GoogleIdentityProviderConfig;
5044
const clientId = await getTokenFromConfig(providerConfig.clientId);
5145
const clientSecret = await getTokenFromConfig(providerConfig.clientSecret);
52-
providers.push(createGoogleProvider(clientId, clientSecret));
46+
providers.push({ provider: createGoogleProvider(clientId, clientSecret), purpose: "sso"});
5347
}
5448
if (identityProvider.provider === "okta") {
5549
const providerConfig = identityProvider as OktaIdentityProviderConfig;
5650
const clientId = await getTokenFromConfig(providerConfig.clientId);
5751
const clientSecret = await getTokenFromConfig(providerConfig.clientSecret);
5852
const issuer = await getTokenFromConfig(providerConfig.issuer);
59-
providers.push(createOktaProvider(clientId, clientSecret, issuer));
53+
providers.push({ provider: createOktaProvider(clientId, clientSecret, issuer), purpose: "sso"});
6054
}
6155
if (identityProvider.provider === "keycloak") {
6256
const providerConfig = identityProvider as KeycloakIdentityProviderConfig;
6357
const clientId = await getTokenFromConfig(providerConfig.clientId);
6458
const clientSecret = await getTokenFromConfig(providerConfig.clientSecret);
6559
const issuer = await getTokenFromConfig(providerConfig.issuer);
66-
providers.push(createKeycloakProvider(clientId, clientSecret, issuer));
60+
providers.push({ provider: createKeycloakProvider(clientId, clientSecret, issuer), purpose: "sso"});
6761
}
6862
if (identityProvider.provider === "microsoft-entra-id") {
6963
const providerConfig = identityProvider as MicrosoftEntraIDIdentityProviderConfig;
7064
const clientId = await getTokenFromConfig(providerConfig.clientId);
7165
const clientSecret = await getTokenFromConfig(providerConfig.clientSecret);
7266
const issuer = await getTokenFromConfig(providerConfig.issuer);
73-
providers.push(createMicrosoftEntraIDProvider(clientId, clientSecret, issuer));
67+
providers.push({ provider: createMicrosoftEntraIDProvider(clientId, clientSecret, issuer), purpose: "sso"});
7468
}
7569
if (identityProvider.provider === "gcp-iap") {
7670
const providerConfig = identityProvider as GCPIAPIdentityProviderConfig;
7771
const audience = await getTokenFromConfig(providerConfig.audience);
78-
providers.push(createGCPIAPProvider(audience));
72+
providers.push({ provider: createGCPIAPProvider(audience), purpose: "sso"});
7973
}
8074
}
8175

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import { getProviders } from "@/auth";
22

3-
export interface AuthProvider {
3+
export interface IdentityProviderMetadata {
44
id: string;
55
name: string;
6+
purpose: "sso" | "integration";
67
}
78

8-
export const getAuthProviders = (): AuthProvider[] => {
9+
export const getIdentityProviderMetadata = (): IdentityProviderMetadata[] => {
910
const providers = getProviders();
1011
return providers.map((provider) => {
11-
if (typeof provider === "function") {
12-
const providerInfo = provider();
13-
return { id: providerInfo.id, name: providerInfo.name };
12+
if (typeof provider.provider === "function") {
13+
const providerInfo = provider.provider();
14+
return { id: providerInfo.id, name: providerInfo.name, purpose: provider.purpose };
1415
} else {
15-
return { id: provider.id, name: provider.name };
16+
return { id: provider.provider.id, name: provider.provider.name, purpose: provider.purpose };
1617
}
1718
});
1819
};

0 commit comments

Comments
 (0)