Compare commits

...
14 Commits
Author SHA1 Message Date
Max Deichmann 84a0ad8dfb chore: release v2.93.5
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests-web-sync (node20, pg12) (push) Waiting to run
CI/CD / tests-web-sync (node20, pg15) (push) Waiting to run
CI/CD / tests-web-async (node20, pg12, mode) (push) Waiting to run
CI/CD / tests-web-async (node20, pg15, mode) (push) Waiting to run
CI/CD / tests-web-async (node20, pg12, mode-azure) (push) Waiting to run
CI/CD / tests-web-async (node20, pg15, mode-azure) (push) Waiting to run
CI/CD / tests-worker (node20, pg12, mode) (push) Waiting to run
CI/CD / tests-worker (node20, pg15, mode) (push) Waiting to run
CI/CD / tests-worker (node20, pg12, mode-azure) (push) Waiting to run
CI/CD / tests-worker (node20, pg15, mode-azure) (push) Waiting to run
CI/CD / e2e-tests (push) Waiting to run
CI/CD / e2e-server-tests (push) Waiting to run
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
2024-12-12 14:06:33 +01:00
Max DeichmannandGitHub 69466fd43b security: upgrade next-auth (#4702) 2024-12-12 14:06:12 +01:00
Marc Klingen 324e078c85 chore: release v2.93.4
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests-web-sync (node20, pg12) (push) Waiting to run
CI/CD / tests-web-sync (node20, pg15) (push) Waiting to run
CI/CD / tests-web-async (node20, pg12, mode) (push) Waiting to run
CI/CD / tests-web-async (node20, pg15, mode) (push) Waiting to run
CI/CD / tests-web-async (node20, pg12, mode-azure) (push) Waiting to run
CI/CD / tests-web-async (node20, pg15, mode-azure) (push) Waiting to run
CI/CD / tests-worker (node20, pg12, mode) (push) Waiting to run
CI/CD / tests-worker (node20, pg15, mode) (push) Waiting to run
CI/CD / tests-worker (node20, pg12, mode-azure) (push) Waiting to run
CI/CD / tests-worker (node20, pg15, mode-azure) (push) Waiting to run
CI/CD / e2e-tests (push) Waiting to run
CI/CD / e2e-server-tests (push) Waiting to run
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
2024-12-04 00:07:38 +01:00
Marc Klingen 7385fc4529 fix: disable x frame options header on Hugging Face (#4558) 2024-12-04 00:07:02 +01:00
Marc Klingen 66d1fa427f chore: release v2.93.3
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests-web-sync (node20, pg12) (push) Waiting to run
CI/CD / tests-web-sync (node20, pg15) (push) Waiting to run
CI/CD / tests-web-async (node20, pg12, mode) (push) Waiting to run
CI/CD / tests-web-async (node20, pg15, mode) (push) Waiting to run
CI/CD / tests-web-async (node20, pg12, mode-azure) (push) Waiting to run
CI/CD / tests-web-async (node20, pg15, mode-azure) (push) Waiting to run
CI/CD / tests-worker (node20, pg12, mode) (push) Waiting to run
CI/CD / tests-worker (node20, pg15, mode) (push) Waiting to run
CI/CD / tests-worker (node20, pg12, mode-azure) (push) Waiting to run
CI/CD / tests-worker (node20, pg15, mode-azure) (push) Waiting to run
CI/CD / e2e-tests (push) Waiting to run
CI/CD / e2e-server-tests (push) Waiting to run
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
2024-12-03 23:37:22 +01:00
bee396a433 feat(auth): add KeyCloak authentication option (#2866)
---------

Co-authored-by: RTae <natthanan.bhu@doctorasa.co>
Co-authored-by: Marc Klingen <git@marcklingen.com>
2024-12-03 23:35:01 +01:00
jay0129andMarc Klingen 8727a52931 feat(auth): add GitHub Enterprise Authentication Provider (#4463)
feat: Add GitHub Enterprise Authentication Provider

Co-authored-by: Marc Klingen <git@marcklingen.com>
2024-12-03 23:34:31 +01:00
Marc Klingen ed5c076a5a fix(auth): add nonce check for Cognito NextAuth provider (#4401) 2024-12-03 23:34:15 +01:00
Marc Klingen db5c575ae0 chore: release v2.93.2
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests-web-sync (node20, pg12) (push) Waiting to run
CI/CD / tests-web-sync (node20, pg15) (push) Waiting to run
CI/CD / tests-web-async (node20, pg12, mode) (push) Waiting to run
CI/CD / tests-web-async (node20, pg15, mode) (push) Waiting to run
CI/CD / tests-web-async (node20, pg12, mode-azure) (push) Waiting to run
CI/CD / tests-web-async (node20, pg15, mode-azure) (push) Waiting to run
CI/CD / tests-worker (node20, pg12, mode) (push) Waiting to run
CI/CD / tests-worker (node20, pg15, mode) (push) Waiting to run
CI/CD / tests-worker (node20, pg12, mode-azure) (push) Waiting to run
CI/CD / tests-worker (node20, pg15, mode-azure) (push) Waiting to run
CI/CD / e2e-tests (push) Waiting to run
CI/CD / e2e-server-tests (push) Waiting to run
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
2024-12-03 17:53:55 +01:00
Marc Klingen 2a0f482578 fix: remove LANGFUSE_CSP_DISABLE (did not work) and disable csp headers on HF Spaces (#4545) 2024-12-03 17:51:50 +01:00
Marc Klingen c041cf371a chore: release v2.93.1
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests-web-sync (node20, pg12) (push) Waiting to run
CI/CD / tests-web-sync (node20, pg15) (push) Waiting to run
CI/CD / tests-web-async (node20, pg12, mode) (push) Waiting to run
CI/CD / tests-web-async (node20, pg15, mode) (push) Waiting to run
CI/CD / tests-web-async (node20, pg12, mode-azure) (push) Waiting to run
CI/CD / tests-web-async (node20, pg15, mode-azure) (push) Waiting to run
CI/CD / tests-worker (node20, pg12, mode) (push) Waiting to run
CI/CD / tests-worker (node20, pg15, mode) (push) Waiting to run
CI/CD / tests-worker (node20, pg12, mode-azure) (push) Waiting to run
CI/CD / tests-worker (node20, pg15, mode-azure) (push) Waiting to run
CI/CD / e2e-tests (push) Waiting to run
CI/CD / e2e-server-tests (push) Waiting to run
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
2024-12-03 01:25:53 +01:00
Marc Klingen c6daf09cd2 [cherry-pick][2.93.x] chore: add LANGFUSE_CSP_DISABLE to .env.prod.example (#4533) 2024-12-03 01:25:04 +01:00
Marc KlingenandGitHub 7296e2e012 [cherry-pick][2.93.x] feat: optionally disable csp headers via LANGFUSE_CSP_DISABLE=true (#4532) 2024-12-03 01:22:11 +01:00
steffen911 43bf176ef7 chore: release v2.93.0 2024-11-26 10:04:45 +01:00
17 changed files with 521 additions and 388 deletions
+8
View File
@@ -67,6 +67,10 @@ OTEL_SERVICE_NAME="langfuse"
# AUTH_GITHUB_CLIENT_ID=
# AUTH_GITHUB_CLIENT_SECRET=
# AUTH_GITHUB_ALLOW_ACCOUNT_LINKING=false
# AUTH_GITHUB_ENTERPRISE_CLIENT_ID=
# AUTH_GITHUB_ENTERPRISE_CLIENT_SECRET=
# AUTH_GITHUB_ENTERPRISE_BASE_URL=
# AUTH_GITHUB_ENTERPRISE_ALLOW_ACCOUNT_LINKING=false
# AUTH_GITLAB_CLIENT_ID=
# AUTH_GITLAB_CLIENT_SECRET=
# AUTH_GITLAB_ALLOW_ACCOUNT_LINKING=false
@@ -87,6 +91,10 @@ OTEL_SERVICE_NAME="langfuse"
# AUTH_COGNITO_CLIENT_SECRET=
# AUTH_COGNITO_ISSUER=
# AUTH_COGNITO_ALLOW_ACCOUNT_LINKING=false
# AUTH_KEYCLOAK_CLIENT_ID=
# AUTH_KEYCLOAK_CLIENT_SECRET=
# AUTH_KEYCLOAK_ISSUER=
# AUTH_KEYCLOAK_ALLOW_ACCOUNT_LINKING=false
# AUTH_CUSTOM_CLIENT_ID=
# AUTH_CUSTOM_CLIENT_SECRET=
# AUTH_CUSTOM_ISSUER=
+1 -1
View File
@@ -28,7 +28,7 @@
"@opentelemetry/api": ">=1.0.0 <1.10.0",
"axios": "^1.7.7",
"next": "^14.2.15",
"next-auth": "^4.24.7",
"next-auth": "^4.24.11",
"zod": "^3.23.8"
},
"devDependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "langfuse",
"version": "2.92.0",
"version": "2.93.5",
"author": "engineering@langfuse.com",
"license": "MIT",
"private": true,
+1 -1
View File
@@ -81,7 +81,7 @@
"langchain": "^0.3.6",
"langfuse-langchain": "3.30.3",
"lodash": "^4.17.21",
"next-auth": "^4.24.7",
"next-auth": "^4.24.11",
"nodemailer": "^6.9.15",
"prisma-extension-kysely": "^2.1.0",
"uuid": "^9.0.1",
@@ -0,0 +1,65 @@
import type { OAuthConfig, OAuthUserConfig } from "next-auth/providers/oauth";
import type { GithubProfile, GithubEmail } from "next-auth/providers/github";
export function GitHubEnterpriseProvider<P extends GithubProfile>(
options: OAuthUserConfig<P> & {
enterprise?: {
baseUrl?: string;
};
}
): OAuthConfig<P> {
const baseUrl = options?.enterprise?.baseUrl ?? "https://github.com"
const apiBaseUrl = options?.enterprise?.baseUrl
? `${options?.enterprise?.baseUrl}/api/v3`
: "https://api.github.com"
return {
id: "github-enterprise",
name: "GitHub Enterprise",
type: "oauth",
authorization: {
url: `${baseUrl}/login/oauth/authorize`,
params: { scope: "read:user user:email" },
},
token: `${baseUrl}/login/oauth/access_token`,
userinfo: {
url: `${apiBaseUrl}/user`,
async request({ client, tokens }) {
const profile = await client.userinfo(tokens.access_token!)
if (!profile.email) {
// If the user does not have a public email, get another via the GitHub API
// See https://docs.github.com/en/rest/users/emails#list-email-addresses-for-the-authenticated-user
const res = await fetch(`${apiBaseUrl}/user/emails`, {
headers: { Authorization: `token ${tokens.access_token}` },
})
if (res.ok) {
const emails: GithubEmail[] = await res.json()
profile.email = (emails.find((e) => e.primary) ?? emails[0]).email
}
}
return profile
},
},
profile(profile) {
return {
id: profile.id.toString(),
name: profile.name ?? profile.login,
email: profile.email,
image: profile.avatar_url,
}
},
style: {
logo: "https://raw.githubusercontent.com/nextauthjs/next-auth/main/packages/next-auth/provider-logos/github.svg",
logoDark:
"https://raw.githubusercontent.com/nextauthjs/next-auth/main/packages/next-auth/provider-logos/github-dark.svg",
bg: "#fff",
bgDark: "#000",
text: "#000",
textDark: "#fff",
},
options,
}
}
+1
View File
@@ -6,6 +6,7 @@ export * from "./services/PromptService";
export * from "./services/traces-ui-table-service";
export * from "./auth/apiKeys";
export * from "./auth/customSsoProvider";
export * from "./auth/gitHubEnterpriseProvider";
export * from "./llm/fetchLLMCompletion";
export * from "./llm/types";
export * from "./utils/DatabaseReadStream";
+245 -356
View File
File diff suppressed because it is too large Load Diff
+39 -20
View File
@@ -28,6 +28,9 @@ const cspHeader = `
${env.SENTRY_CSP_REPORT_URI ? `report-uri ${env.SENTRY_CSP_REPORT_URI}; report-to csp-endpoint;` : ""}
`;
// Match rules for Hugging Face
const huggingFaceHosts = ["huggingface.co", ".*\\.hf\\.space$"];
const reportToHeader = {
key: "Report-To",
value: JSON.stringify({
@@ -78,10 +81,6 @@ const nextConfig = {
{
source: "/:path*",
headers: [
{
key: "x-frame-options",
value: "SAMEORIGIN",
},
{
key: "X-Content-Type-Options",
value: "nosniff",
@@ -97,6 +96,21 @@ const nextConfig = {
...(env.SENTRY_CSP_REPORT_URI ? [reportToHeader] : []),
],
},
{
source: "/:path*",
headers: [
{
key: "x-frame-options",
value: "SAMEORIGIN",
},
],
// Disable x-frame-options on Hugging Face to allow for embedded use of Langfuse
missing: huggingFaceHosts.map((host) => ({
type: "host",
value: host,
})),
},
// CSP header
{
source: "/:path((?!api).*)*",
headers: [
@@ -105,26 +119,31 @@ const nextConfig = {
value: cspHeader.replace(/\n/g, ""),
},
],
// Disable CSP on Hugging Face to allow for embedded use of Langfuse
missing: huggingFaceHosts.map((host) => ({
type: "host",
value: host,
})),
},
// Required to check authentication status from langfuse.com
...(env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION !== undefined
? [
{
source: "/api/auth/session",
headers: [
{
key: "Access-Control-Allow-Origin",
value: "https://langfuse.com",
},
{ key: "Access-Control-Allow-Credentials", value: "true" },
{ key: "Access-Control-Allow-Methods", value: "GET,POST" },
{
key: "Access-Control-Allow-Headers",
value: "Content-Type, Authorization",
},
],
},
]
{
source: "/api/auth/session",
headers: [
{
key: "Access-Control-Allow-Origin",
value: "https://langfuse.com",
},
{ key: "Access-Control-Allow-Credentials", value: "true" },
{ key: "Access-Control-Allow-Methods", value: "GET,POST" },
{
key: "Access-Control-Allow-Headers",
value: "Content-Type, Authorization",
},
],
},
]
: []),
// all files in /public/generated are public and can be accessed from any origin, e.g. to render an API reference based on our openapi schema
{
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "web",
"version": "2.92.0",
"version": "2.93.5",
"private": true,
"license": "MIT",
"engines": {
@@ -117,7 +117,7 @@
"lodash": "^4.17.21",
"lucide-react": "^0.447.0",
"next": "^14.2.15",
"next-auth": "^4.24.7",
"next-auth": "^4.24.11",
"next-query-params": "^5.0.1",
"next-themes": "^0.3.0",
"posthog-js": "^1.176.0",
+1 -1
View File
@@ -1 +1 @@
export const VERSION = "v2.92.0";
export const VERSION = "v2.93.5";
@@ -28,6 +28,20 @@ export const GithubProviderSchema = base.extend({
.nullish(),
});
export const GithubEnterpriseProviderSchema = base.extend({
authProvider: z.literal("github-enterprise"),
authConfig: z
.object({
clientId: z.string(),
clientSecret: z.string(),
enterprise: z.object({
baseUrl: z.string().url(),
}),
allowDangerousEmailAccountLinking: z.boolean().optional().default(false),
})
.nullish(),
});
export const GitlabProviderSchema = base.extend({
authProvider: z.literal("gitlab"),
authConfig: z
@@ -88,6 +102,18 @@ export const CognitoProviderSchema = base.extend({
.nullish(),
});
export const KeycloakProviderSchema = base.extend({
authProvider: z.literal("keycloak"),
authConfig: z
.object({
clientId: z.string(),
clientSecret: z.string(),
issuer: z.string(),
allowDangerousEmailAccountLinking: z.boolean().optional().default(false),
})
.nullish(),
});
export const CustomProviderSchema = base.extend({
authProvider: z.literal("custom"),
authConfig: z
@@ -104,21 +130,25 @@ export const CustomProviderSchema = base.extend({
export type GoogleProviderSchema = z.infer<typeof GoogleProviderSchema>;
export type GithubProviderSchema = z.infer<typeof GithubProviderSchema>;
export type GithubEnterpriseProviderSchema = z.infer<typeof GithubEnterpriseProviderSchema>;
export type GitlabProviderSchema = z.infer<typeof GitlabProviderSchema>;
export type Auth0ProviderSchema = z.infer<typeof Auth0ProviderSchema>;
export type OktaProviderSchema = z.infer<typeof OktaProviderSchema>;
export type AzureAdProviderSchema = z.infer<typeof AzureAdProviderSchema>;
export type CognitoProviderSchema = z.infer<typeof CognitoProviderSchema>;
export type KeycloakProviderSchema = z.infer<typeof KeycloakProviderSchema>;
export type CustomProviderSchema = z.infer<typeof CustomProviderSchema>;
export const SsoProviderSchema = z.discriminatedUnion("authProvider", [
GoogleProviderSchema,
GithubProviderSchema,
GithubEnterpriseProviderSchema,
GitlabProviderSchema,
Auth0ProviderSchema,
OktaProviderSchema,
AzureAdProviderSchema,
CognitoProviderSchema,
KeycloakProviderSchema,
CustomProviderSchema,
]);
@@ -4,6 +4,7 @@ import GitHubProvider from "next-auth/providers/github";
import GitLabProvider from "next-auth/providers/gitlab";
import OktaProvider from "next-auth/providers/okta";
import CognitoProvider from "next-auth/providers/cognito";
import KeycloakProvider from "next-auth/providers/keycloak";
import Auth0Provider from "next-auth/providers/auth0";
import AzureADProvider from "next-auth/providers/azure-ad";
import { isEeEnabled } from "@/src/ee/utils/isEeEnabled";
@@ -12,6 +13,7 @@ import { decrypt } from "@langfuse/shared/encryption";
import { SsoProviderSchema } from "./types";
import {
CustomSSOProvider,
GitHubEnterpriseProvider,
logger,
traceException,
} from "@langfuse/shared/src/server";
@@ -188,6 +190,12 @@ const dbToNextAuthProvider = (provider: SsoProviderSchema): Provider | null => {
...provider.authConfig,
clientSecret: decrypt(provider.authConfig.clientSecret),
});
else if (provider.authProvider === "keycloak")
return KeycloakProvider({
id: getAuthProviderIdForSsoConfig(provider), // use the domain as the provider id as we use domain-specific credentials
...provider.authConfig,
clientSecret: decrypt(provider.authConfig.clientSecret),
});
else if (provider.authProvider === "custom")
return CustomSSOProvider({
id: getAuthProviderIdForSsoConfig(provider), // use the domain as the provider id as we use domain-specific credentials
@@ -197,6 +205,15 @@ const dbToNextAuthProvider = (provider: SsoProviderSchema): Provider | null => {
params: { scope: provider.authConfig.scope ?? "openid email profile" },
},
});
else if (provider.authProvider === "github-enterprise")
return GitHubEnterpriseProvider({
id: getAuthProviderIdForSsoConfig(provider), // use the domain as the provider id as we use domain-specific credentials
...provider.authConfig,
clientSecret: decrypt(provider.authConfig.clientSecret),
enterprise: {
baseUrl: provider.authConfig.enterprise.baseUrl,
},
});
else {
// Type check to ensure we handle all providers
// eslint-disable-next-line @typescript-eslint/no-unused-vars
+19 -1
View File
@@ -46,7 +46,7 @@ export const env = createEnv({
LANGFUSE_DEFAULT_PROJECT_ROLE: z
.enum(["OWNER", "ADMIN", "MEMBER", "VIEWER"])
.optional(),
LANGFUSE_CSP_ENFORCE_HTTPS: z.enum(["true", "false"]).optional(),
LANGFUSE_CSP_ENFORCE_HTTPS: z.enum(["true", "false"]).optional().default("false"),
// Telemetry
TELEMETRY_ENABLED: z.enum(["true", "false"]).optional(),
// AUTH
@@ -57,6 +57,10 @@ export const env = createEnv({
AUTH_GITHUB_CLIENT_ID: z.string().optional(),
AUTH_GITHUB_CLIENT_SECRET: z.string().optional(),
AUTH_GITHUB_ALLOW_ACCOUNT_LINKING: z.enum(["true", "false"]).optional(),
AUTH_GITHUB_ENTERPRISE_CLIENT_ID: z.string().optional(),
AUTH_GITHUB_ENTERPRISE_CLIENT_SECRET: z.string().optional(),
AUTH_GITHUB_ENTERPRISE_BASE_URL: z.string().optional(),
AUTH_GITHUB_ENTERPRISE_ALLOW_ACCOUNT_LINKING: z.enum(["true", "false"]).optional(),
AUTH_GITLAB_CLIENT_ID: z.string().optional(),
AUTH_GITLAB_CLIENT_SECRET: z.string().optional(),
AUTH_GITLAB_ALLOW_ACCOUNT_LINKING: z.enum(["true", "false"]).optional(),
@@ -77,6 +81,10 @@ export const env = createEnv({
AUTH_COGNITO_CLIENT_SECRET: z.string().optional(),
AUTH_COGNITO_ISSUER: z.string().url().optional(),
AUTH_COGNITO_ALLOW_ACCOUNT_LINKING: z.enum(["true", "false"]).optional(),
AUTH_KEYCLOAK_CLIENT_ID: z.string().optional(),
AUTH_KEYCLOAK_CLIENT_SECRET: z.string().optional(),
AUTH_KEYCLOAK_ISSUER: z.string().optional(),
AUTH_KEYCLOAK_ALLOW_ACCOUNT_LINKING: z.enum(["true", "false"]).optional(),
AUTH_CUSTOM_CLIENT_ID: z.string().optional(),
AUTH_CUSTOM_CLIENT_SECRET: z.string().optional(),
AUTH_CUSTOM_ISSUER: z.string().url().optional(),
@@ -291,6 +299,11 @@ export const env = createEnv({
AUTH_GITHUB_CLIENT_SECRET: process.env.AUTH_GITHUB_CLIENT_SECRET,
AUTH_GITHUB_ALLOW_ACCOUNT_LINKING:
process.env.AUTH_GITHUB_ALLOW_ACCOUNT_LINKING,
AUTH_GITHUB_ENTERPRISE_CLIENT_ID: process.env.AUTH_GITHUB_ENTERPRISE_CLIENT_ID,
AUTH_GITHUB_ENTERPRISE_CLIENT_SECRET: process.env.AUTH_GITHUB_ENTERPRISE_CLIENT_SECRET,
AUTH_GITHUB_ENTERPRISE_BASE_URL: process.env.AUTH_GITHUB_ENTERPRISE_BASE_URL,
AUTH_GITHUB_ENTERPRISE_ALLOW_ACCOUNT_LINKING:
process.env.AUTH_GITHUB_ENTERPRISE_ALLOW_ACCOUNT_LINKING,
AUTH_GITLAB_ISSUER: process.env.AUTH_GITLAB_ISSUER,
AUTH_GITLAB_CLIENT_ID: process.env.AUTH_GITLAB_CLIENT_ID,
AUTH_GITLAB_CLIENT_SECRET: process.env.AUTH_GITLAB_CLIENT_SECRET,
@@ -316,6 +329,11 @@ export const env = createEnv({
AUTH_COGNITO_ISSUER: process.env.AUTH_COGNITO_ISSUER,
AUTH_COGNITO_ALLOW_ACCOUNT_LINKING:
process.env.AUTH_COGNITO_ALLOW_ACCOUNT_LINKING,
AUTH_KEYCLOAK_CLIENT_ID: process.env.AUTH_KEYCLOAK_CLIENT_ID,
AUTH_KEYCLOAK_CLIENT_SECRET: process.env.AUTH_KEYCLOAK_CLIENT_SECRET,
AUTH_KEYCLOAK_ISSUER: process.env.AUTH_KEYCLOAK_ISSUER,
AUTH_KEYCLOAK_ALLOW_ACCOUNT_LINKING:
process.env.AUTH_KEYCLOAK_ALLOW_ACCOUNT_LINKING,
AUTH_CUSTOM_CLIENT_ID: process.env.AUTH_CUSTOM_CLIENT_ID,
AUTH_CUSTOM_CLIENT_SECRET: process.env.AUTH_CUSTOM_CLIENT_SECRET,
AUTH_CUSTOM_ISSUER: process.env.AUTH_CUSTOM_ISSUER,
+33 -1
View File
@@ -14,7 +14,7 @@ import { env } from "@/src/env.mjs";
import { zodResolver } from "@hookform/resolvers/zod";
import { FcGoogle } from "react-icons/fc";
import { FaGithub, FaGitlab } from "react-icons/fa";
import { SiOkta, SiAuth0, SiAmazoncognito } from "react-icons/si";
import { SiOkta, SiAuth0, SiAmazoncognito, SiKeycloak } from "react-icons/si";
import { TbBrandAzure, TbBrandOauth } from "react-icons/tb";
import { signIn } from "next-auth/react";
import Head from "next/head";
@@ -46,11 +46,13 @@ export type PageProps = {
credentials: boolean;
google: boolean;
github: boolean;
githubEnterprise: boolean;
gitlab: boolean;
okta: boolean;
azureAd: boolean;
auth0: boolean;
cognito: boolean;
keycloak: boolean;
custom:
| {
name: string;
@@ -74,6 +76,10 @@ export const getServerSideProps: GetServerSideProps<PageProps> = async () => {
github:
env.AUTH_GITHUB_CLIENT_ID !== undefined &&
env.AUTH_GITHUB_CLIENT_SECRET !== undefined,
githubEnterprise:
env.AUTH_GITHUB_ENTERPRISE_CLIENT_ID !== undefined &&
env.AUTH_GITHUB_ENTERPRISE_CLIENT_SECRET !== undefined &&
env.AUTH_GITHUB_ENTERPRISE_BASE_URL !== undefined,
gitlab:
env.AUTH_GITLAB_CLIENT_ID !== undefined &&
env.AUTH_GITLAB_CLIENT_SECRET !== undefined,
@@ -94,6 +100,10 @@ export const getServerSideProps: GetServerSideProps<PageProps> = async () => {
env.AUTH_COGNITO_CLIENT_ID !== undefined &&
env.AUTH_COGNITO_CLIENT_SECRET !== undefined &&
env.AUTH_COGNITO_ISSUER !== undefined,
keycloak:
env.AUTH_KEYCLOAK_CLIENT_ID !== undefined &&
env.AUTH_KEYCLOAK_CLIENT_SECRET !== undefined &&
env.AUTH_KEYCLOAK_ISSUER !== undefined,
custom:
env.AUTH_CUSTOM_CLIENT_ID !== undefined &&
env.AUTH_CUSTOM_CLIENT_SECRET !== undefined &&
@@ -165,6 +175,16 @@ export function SSOButtons({
Github
</Button>
)}
{authProviders.githubEnterprise && (
<Button
onClick={() => handleSignIn("github-enterprise")}
variant="secondary"
loading={providerSigningIn === "github-enterprise"}
>
<FaGithub className="mr-3" size={18} />
Github Enterprise
</Button>
)}
{authProviders.gitlab && (
<Button
onClick={() => handleSignIn("gitlab")}
@@ -215,6 +235,18 @@ export function SSOButtons({
Cognito
</Button>
)}
{authProviders.keycloak && (
<Button
onClick={() => {
capture("sign_in:button_click", { provider: "keycloak" });
void signIn("keycloak");
}}
variant="secondary"
>
<SiKeycloak className="mr-3" size={18} />
Keycloak
</Button>
)}
{authProviders.custom && (
<Button
onClick={() => handleSignIn("custom")}
+56 -2
View File
@@ -11,7 +11,6 @@ import { verifyPassword } from "@/src/features/auth-credentials/lib/credentialsS
import { parseFlags } from "@/src/features/feature-flags/utils";
import { env } from "@/src/env.mjs";
import { createProjectMembershipsOnSignup } from "@/src/features/auth/lib/createProjectMembershipsOnSignup";
import { type Adapter } from "next-auth/adapters";
// Providers
import CredentialsProvider from "next-auth/providers/credentials";
@@ -23,6 +22,7 @@ import EmailProvider from "next-auth/providers/email";
import Auth0Provider from "next-auth/providers/auth0";
import CognitoProvider from "next-auth/providers/cognito";
import AzureADProvider from "next-auth/providers/azure-ad";
import KeycloakProvider from "next-auth/providers/keycloak";
import { type Provider } from "next-auth/providers/index";
import { getCookieName, getCookieOptions } from "./utils/cookies";
import {
@@ -33,6 +33,7 @@ import { z } from "zod";
import { CloudConfigSchema } from "@langfuse/shared";
import {
CustomSSOProvider,
GitHubEnterpriseProvider,
traceException,
sendResetPasswordVerificationRequest,
instrumentAsync,
@@ -40,6 +41,11 @@ import {
} from "@langfuse/shared/src/server";
import { getOrganizationPlan } from "@/src/features/entitlements/server/getOrganizationPlan";
import { projectRoleAccessRights } from "@/src/features/rbac/constants/projectAccessRights";
import {
type AdapterUser,
type Adapter,
type AdapterAccount,
} from "next-auth/adapters";
function canCreateOrganizations(userEmail: string | null): boolean {
// if no allowlist is set or no active EE key, allow all users to create organizations
@@ -227,6 +233,22 @@ if (env.AUTH_GITHUB_CLIENT_ID && env.AUTH_GITHUB_CLIENT_SECRET)
}),
);
if (
env.AUTH_GITHUB_ENTERPRISE_CLIENT_ID &&
env.AUTH_GITHUB_ENTERPRISE_CLIENT_SECRET &&
env.AUTH_GITHUB_ENTERPRISE_BASE_URL
) {
staticProviders.push(
GitHubEnterpriseProvider({
clientId: env.AUTH_GITHUB_ENTERPRISE_CLIENT_ID,
clientSecret: env.AUTH_GITHUB_ENTERPRISE_CLIENT_SECRET,
enterprise: { baseUrl: env.AUTH_GITHUB_ENTERPRISE_BASE_URL },
allowDangerousEmailAccountLinking:
env.AUTH_GITHUB_ENTERPRISE_ALLOW_ACCOUNT_LINKING === "true",
}),
);
}
if (env.AUTH_GITLAB_CLIENT_ID && env.AUTH_GITLAB_CLIENT_SECRET)
staticProviders.push(
GitLabProvider({
@@ -263,16 +285,32 @@ if (
clientId: env.AUTH_COGNITO_CLIENT_ID,
clientSecret: env.AUTH_COGNITO_CLIENT_SECRET,
issuer: env.AUTH_COGNITO_ISSUER,
checks: "nonce",
allowDangerousEmailAccountLinking:
env.AUTH_COGNITO_ALLOW_ACCOUNT_LINKING === "true",
}),
);
if (
env.AUTH_KEYCLOAK_CLIENT_ID &&
env.AUTH_KEYCLOAK_CLIENT_SECRET &&
env.AUTH_KEYCLOAK_ISSUER
)
staticProviders.push(
KeycloakProvider({
clientId: env.AUTH_KEYCLOAK_CLIENT_ID,
clientSecret: env.AUTH_KEYCLOAK_CLIENT_SECRET,
issuer: env.AUTH_KEYCLOAK_ISSUER,
allowDangerousEmailAccountLinking:
env.AUTH_KEYCLOAK_ALLOW_ACCOUNT_LINKING === "true",
}),
);
// Extend Prisma Adapter
const prismaAdapter = PrismaAdapter(prisma);
const extendedPrismaAdapter: Adapter = {
...prismaAdapter,
async createUser(profile) {
async createUser(profile: Omit<AdapterUser, "id">) {
if (!prismaAdapter.createUser)
throw new Error("createUser not implemented");
if (
@@ -294,6 +332,22 @@ const extendedPrismaAdapter: Adapter = {
return user;
},
async linkAccount(data: AdapterAccount) {
if (!prismaAdapter.linkAccount)
throw new Error("NextAuth: prismaAdapter.linkAccount not implemented");
// Keycloak returns incompatible data with the nextjs-auth schema
// (refresh_expires_in and not-before-policy in).
// So, we need to remove this data from the payload before linking an account.
// https://github.com/nextauthjs/next-auth/issues/7655
if (data.provider === "keycloak") {
delete data["refresh_expires_in"];
delete data["not-before-policy"];
}
await prismaAdapter.linkAccount(data);
},
};
/**
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "worker",
"version": "2.92.0",
"version": "2.93.5",
"description": "",
"license": "MIT",
"private": true,
+1 -1
View File
@@ -1 +1 @@
export const VERSION = "v2.92.0";
export const VERSION = "v2.93.5";