Skip to content
This repository was archived by the owner on Apr 13, 2026. It is now read-only.

Commit 0efb17f

Browse files
authored
Add GitHub and GitLab SSO support (#73)
### Motivation - Expand available SSO options so deployments can use GitHub or GitLab in addition to the existing Google/Keycloak/Okta providers. - Ensure environment schema, runtime bindings, sign-in UI, and docs are consistent with existing Auth.js/NextAuth patterns in the codebase. - Provide clear, generic guidance in the installation docs about configuring provider callback/redirect settings. ### Description - Register GitHub and GitLab providers with Auth.js and enable conditional provider registration when corresponding env vars are present in `src/server/auth/config.ts`. - Add `GITHUB_*` and `GITLAB_*` env entries to the validated server env schema and runtime bindings in `src/env.ts`. - Expose GitHub/GitLab toggles and buttons in the sign-in flow by updating `src/app/(public-routes)/auth/signin/page.tsx` and `src/features/shared/auth/sign-in-page.tsx`. - Update example env files (`.env.example-dev` and `deploy/docker/.env.example-prod`) with commented GitHub/GitLab variables and update `docs/installation.md` to list the new providers and provide generic SSO configuration guidance. ### Testing - Ran `npm run check` (ESLint + `tsc --project tsconfig.check.json`) which completed successfully. - Ran `npm run test` (Vitest) which failed due to a `PrismaClientInitializationError` because the test runner could not reach a PostgreSQL instance at `localhost:5432`. ------ [Codex Task](https://chatgpt.com/codex/tasks/task_e_696b4aacfffc8323b9bb93bb178c28ab)
1 parent 8569d37 commit 0efb17f

7 files changed

Lines changed: 56 additions & 6 deletions

File tree

‎.env.example-dev‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ AUTH_TRUST_HOST=true
2020
# SSO Configuration
2121
#GOOGLE_CLIENT_ID=
2222
#GOOGLE_CLIENT_SECRET=
23+
#GITHUB_CLIENT_ID=
24+
#GITHUB_CLIENT_SECRET=
25+
#GITLAB_CLIENT_ID=
26+
#GITLAB_CLIENT_SECRET=
2327
#KEYCLOAK_CLIENT_ID=
2428
#KEYCLOAK_CLIENT_SECRET=
2529
#KEYCLOAK_ISSUER=

‎deploy/docker/.env.example-prod‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ ENABLE_DEMO_MODE=false
1515
# SSO Configuration
1616
#GOOGLE_CLIENT_ID=
1717
#GOOGLE_CLIENT_SECRET=
18+
#GITHUB_CLIENT_ID=
19+
#GITHUB_CLIENT_SECRET=
20+
#GITLAB_CLIENT_ID=
21+
#GITLAB_CLIENT_SECRET=
1822
#KEYCLOAK_CLIENT_ID=
1923
#KEYCLOAK_CLIENT_SECRET=
2024
#KEYCLOAK_ISSUER=

‎docs/installation.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ Minimum values to edit:
2626

2727
### Choose authentication mode
2828

29-
RTAP supports SSO or a demo login button. Supported SSO providers today are Google, Keycloak, and Okta. If you need another provider, open an issue and we can add it.
29+
RTAP supports SSO or a demo login button. Supported SSO providers today are Google, GitHub, GitLab, Keycloak, and Okta. If you need another provider, open an issue and we can add it.
3030

31-
- **SSO (recommended):** configure your provider's details (like client ID/secret + issuer when required) using the variable names provided in the .env file.
31+
- **SSO (recommended):** configure your provider's details (client ID/secret, plus issuer for Keycloak/Okta) using the variable names provided in the .env file.
3232
- **Demo mode:** set `ENABLE_DEMO_MODE=true`. This exposes a “Sign in as Demo Admin” button and **anyone with access to the sign-in page can log in without an account**. Use only for isolated testing or demos.
3333

34-
For Google SSO, configure the following in the Google Cloud console:
34+
For any SSO provider, configure the following in your identity provider console:
3535

3636
- Authorized JavaScript origins: matches `AUTH_URL` from `.env`.
37-
- Authorized redirect URIs: `AUTH_URL` + `/api/auth/callback/google`.
37+
- Authorized redirect URIs: `AUTH_URL` + `/api/auth/callback/<provider>` (for example, `/api/auth/callback/github`).
3838

3939
### Start the containers
4040

‎src/app/(public-routes)/auth/signin/page.tsx‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,16 @@ export default async function SignInPage(props: { searchParams?: Promise<{ callb
1212
const { callbackUrl = "/", error } = (await props.searchParams) ?? {};
1313
const demoEnabled = env.ENABLE_DEMO_MODE === "true";
1414
const googleEnabled = Boolean(env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET);
15+
const githubEnabled = Boolean(env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET);
16+
const gitlabEnabled = Boolean(env.GITLAB_CLIENT_ID && env.GITLAB_CLIENT_SECRET);
1517
const keycloakEnabled = Boolean(env.KEYCLOAK_CLIENT_ID && env.KEYCLOAK_CLIENT_SECRET && env.KEYCLOAK_ISSUER);
1618
const oktaEnabled = Boolean(env.OKTA_CLIENT_ID && env.OKTA_CLIENT_SECRET && env.OKTA_ISSUER);
1719

1820
return (
1921
<SignInPageClient
2022
googleEnabled={googleEnabled}
23+
githubEnabled={githubEnabled}
24+
gitlabEnabled={gitlabEnabled}
2125
keycloakEnabled={keycloakEnabled}
2226
oktaEnabled={oktaEnabled}
2327
demoEnabled={demoEnabled}

‎src/env.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ export const env = createEnv({
2525
// Optional: Google OAuth client credentials (registers provider when present)
2626
GOOGLE_CLIENT_ID: z.string().optional(),
2727
GOOGLE_CLIENT_SECRET: z.string().optional(),
28+
// Optional: GitHub OAuth client credentials (registers provider when present)
29+
GITHUB_CLIENT_ID: z.string().optional(),
30+
GITHUB_CLIENT_SECRET: z.string().optional(),
31+
// Optional: GitLab OAuth client credentials (registers provider when present)
32+
GITLAB_CLIENT_ID: z.string().optional(),
33+
GITLAB_CLIENT_SECRET: z.string().optional(),
2834
// Optional: Keycloak OAuth client credentials (registers provider when present)
2935
KEYCLOAK_CLIENT_ID: z.string().optional(),
3036
KEYCLOAK_CLIENT_SECRET: z.string().optional(),
@@ -57,6 +63,10 @@ export const env = createEnv({
5763
ENABLE_DEMO_MODE: process.env.ENABLE_DEMO_MODE,
5864
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
5965
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
66+
GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID,
67+
GITHUB_CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET,
68+
GITLAB_CLIENT_ID: process.env.GITLAB_CLIENT_ID,
69+
GITLAB_CLIENT_SECRET: process.env.GITLAB_CLIENT_SECRET,
6070
KEYCLOAK_CLIENT_ID: process.env.KEYCLOAK_CLIENT_ID,
6171
KEYCLOAK_CLIENT_SECRET: process.env.KEYCLOAK_CLIENT_SECRET,
6272
KEYCLOAK_ISSUER: process.env.KEYCLOAK_ISSUER,

‎src/features/shared/auth/sign-in-page.tsx‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,29 @@ import { Button, Card, CardContent, CardHeader, CardTitle } from "@components/ui
77

88
interface Props {
99
googleEnabled: boolean;
10+
githubEnabled: boolean;
11+
gitlabEnabled: boolean;
1012
keycloakEnabled: boolean;
1113
oktaEnabled: boolean;
1214
demoEnabled: boolean;
1315
callbackUrl: string;
1416
initialError?: string;
1517
}
1618

17-
type OAuthProviderId = "google" | "keycloak" | "okta";
19+
type OAuthProviderId = "google" | "github" | "gitlab" | "keycloak" | "okta";
1820

1921
const oauthOptions: Array<{ id: OAuthProviderId; label: string }> = [
2022
{ id: "google", label: "Continue with Google" },
23+
{ id: "github", label: "Continue with GitHub" },
24+
{ id: "gitlab", label: "Continue with GitLab" },
2125
{ id: "keycloak", label: "Continue with Keycloak" },
2226
{ id: "okta", label: "Continue with Okta" },
2327
];
2428

2529
export default function SignInPageClient({
2630
googleEnabled,
31+
githubEnabled,
32+
gitlabEnabled,
2733
keycloakEnabled,
2834
oktaEnabled,
2935
demoEnabled,
@@ -79,6 +85,8 @@ export default function SignInPageClient({
7985

8086
const oauthEnabled: Record<OAuthProviderId, boolean> = {
8187
google: googleEnabled,
88+
github: githubEnabled,
89+
gitlab: gitlabEnabled,
8290
keycloak: keycloakEnabled,
8391
okta: oktaEnabled,
8492
};

‎src/server/auth/config.ts‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { type DefaultSession, type NextAuthConfig } from "next-auth";
22
import type { Adapter } from "next-auth/adapters";
33
import type { JWT as NextAuthJWT } from "next-auth/jwt";
4+
import GitHubProvider from "next-auth/providers/github";
5+
import GitLabProvider from "next-auth/providers/gitlab";
46
import GoogleProvider from "next-auth/providers/google";
57
import KeycloakProvider from "next-auth/providers/keycloak";
68
import OktaProvider from "next-auth/providers/okta";
@@ -42,7 +44,7 @@ declare module "@auth/core/adapters" {
4244
type AugmentedJWT = NextAuthJWT & { role?: UserRole };
4345

4446
const demoModeEnabled = env.ENABLE_DEMO_MODE === "true";
45-
const oauthProviders = new Set(["google", "keycloak", "okta"]);
47+
const oauthProviders = new Set(["google", "github", "gitlab", "keycloak", "okta"]);
4648

4749
const isRecord = (value: unknown): value is Record<string, unknown> =>
4850
typeof value === "object" && value !== null;
@@ -205,6 +207,24 @@ export const authConfig = {
205207
}),
206208
]
207209
: []),
210+
...(process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET
211+
? [
212+
GitHubProvider({
213+
clientId: process.env.GITHUB_CLIENT_ID,
214+
clientSecret: process.env.GITHUB_CLIENT_SECRET,
215+
allowDangerousEmailAccountLinking: true,
216+
}),
217+
]
218+
: []),
219+
...(process.env.GITLAB_CLIENT_ID && process.env.GITLAB_CLIENT_SECRET
220+
? [
221+
GitLabProvider({
222+
clientId: process.env.GITLAB_CLIENT_ID,
223+
clientSecret: process.env.GITLAB_CLIENT_SECRET,
224+
allowDangerousEmailAccountLinking: true,
225+
}),
226+
]
227+
: []),
208228
...(process.env.KEYCLOAK_CLIENT_ID && process.env.KEYCLOAK_CLIENT_SECRET && process.env.KEYCLOAK_ISSUER
209229
? [
210230
KeycloakProvider({

0 commit comments

Comments
 (0)