feat(auth): add WorkOS and Keycloak SSO providers
This commit is contained in:
48
apps/web/src/lib/sso-providers.test.ts
Normal file
48
apps/web/src/lib/sso-providers.test.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { getEnabledSsoProviders, getSsoProvider } from './sso-providers';
|
||||
|
||||
describe('sso-providers', () => {
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
it('returns the enabled providers in login button order', () => {
|
||||
vi.stubEnv('NEXT_PUBLIC_WORKOS_ENABLED', 'true');
|
||||
vi.stubEnv('NEXT_PUBLIC_KEYCLOAK_ENABLED', 'true');
|
||||
|
||||
expect(getEnabledSsoProviders()).toEqual([
|
||||
{
|
||||
id: 'workos',
|
||||
buttonLabel: 'Continue with WorkOS',
|
||||
description: 'Enterprise SSO via WorkOS',
|
||||
enabled: true,
|
||||
href: '/auth/provider/workos',
|
||||
},
|
||||
{
|
||||
id: 'keycloak',
|
||||
buttonLabel: 'Continue with Keycloak',
|
||||
description: 'Enterprise SSO via Keycloak',
|
||||
enabled: true,
|
||||
href: '/auth/provider/keycloak',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('marks disabled providers without exposing them in the enabled list', () => {
|
||||
vi.stubEnv('NEXT_PUBLIC_WORKOS_ENABLED', 'true');
|
||||
vi.stubEnv('NEXT_PUBLIC_KEYCLOAK_ENABLED', 'false');
|
||||
|
||||
expect(getEnabledSsoProviders().map((provider) => provider.id)).toEqual(['workos']);
|
||||
expect(getSsoProvider('keycloak')).toEqual({
|
||||
id: 'keycloak',
|
||||
buttonLabel: 'Continue with Keycloak',
|
||||
description: 'Enterprise SSO via Keycloak',
|
||||
enabled: false,
|
||||
href: '/auth/provider/keycloak',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns null for unknown providers', () => {
|
||||
expect(getSsoProvider('authentik')).toBeNull();
|
||||
});
|
||||
});
|
||||
53
apps/web/src/lib/sso-providers.ts
Normal file
53
apps/web/src/lib/sso-providers.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
export type SsoProviderId = 'workos' | 'keycloak';
|
||||
|
||||
export interface SsoProvider {
|
||||
id: SsoProviderId;
|
||||
buttonLabel: string;
|
||||
description: string;
|
||||
enabled: boolean;
|
||||
href: string;
|
||||
}
|
||||
|
||||
const PROVIDER_METADATA: Record<SsoProviderId, Omit<SsoProvider, 'enabled' | 'href'>> = {
|
||||
workos: {
|
||||
id: 'workos',
|
||||
buttonLabel: 'Continue with WorkOS',
|
||||
description: 'Enterprise SSO via WorkOS',
|
||||
},
|
||||
keycloak: {
|
||||
id: 'keycloak',
|
||||
buttonLabel: 'Continue with Keycloak',
|
||||
description: 'Enterprise SSO via Keycloak',
|
||||
},
|
||||
};
|
||||
|
||||
export function getEnabledSsoProviders(): SsoProvider[] {
|
||||
return (Object.keys(PROVIDER_METADATA) as SsoProviderId[])
|
||||
.map((providerId) => getSsoProvider(providerId))
|
||||
.filter((provider): provider is SsoProvider => provider?.enabled === true);
|
||||
}
|
||||
|
||||
export function getSsoProvider(providerId: string): SsoProvider | null {
|
||||
if (!isSsoProviderId(providerId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
...PROVIDER_METADATA[providerId],
|
||||
enabled: isSsoProviderEnabled(providerId),
|
||||
href: `/auth/provider/${providerId}`,
|
||||
};
|
||||
}
|
||||
|
||||
function isSsoProviderId(value: string): value is SsoProviderId {
|
||||
return value === 'workos' || value === 'keycloak';
|
||||
}
|
||||
|
||||
function isSsoProviderEnabled(providerId: SsoProviderId): boolean {
|
||||
switch (providerId) {
|
||||
case 'workos':
|
||||
return process.env['NEXT_PUBLIC_WORKOS_ENABLED'] === 'true';
|
||||
case 'keycloak':
|
||||
return process.env['NEXT_PUBLIC_KEYCLOAK_ENABLED'] === 'true';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user