mirror of
https://github.com/RayLabsHQ/gitea-mirror.git
synced 2025-12-17 03:43:46 +03:00
more SSO and OIDC fixes
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { createAuthClient } from "better-auth/react";
|
||||
import { oidcClient } from "better-auth/client/plugins";
|
||||
import { ssoClient } from "better-auth/client/plugins";
|
||||
import { ssoClient } from "@better-auth/sso/client";
|
||||
import type { Session as BetterAuthSession, User as BetterAuthUser } from "better-auth";
|
||||
|
||||
export const authClient = createAuthClient({
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import { betterAuth } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import { oidcProvider } from "better-auth/plugins";
|
||||
import { sso } from "better-auth/plugins/sso";
|
||||
import type { BunSQLiteDatabase } from "drizzle-orm/bun-sqlite";
|
||||
|
||||
// This function will be called with the actual database instance
|
||||
export function createAuth(db: BunSQLiteDatabase) {
|
||||
return betterAuth({
|
||||
// Database configuration
|
||||
database: drizzleAdapter(db, {
|
||||
provider: "sqlite",
|
||||
usePlural: true, // Our tables use plural names (users, not user)
|
||||
}),
|
||||
|
||||
// Base URL configuration
|
||||
baseURL: process.env.BETTER_AUTH_URL || "http://localhost:3000",
|
||||
|
||||
// Authentication methods
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
requireEmailVerification: false, // We'll enable this later
|
||||
sendResetPassword: async ({ user, url, token }, request) => {
|
||||
// TODO: Implement email sending for password reset
|
||||
console.log("Password reset requested for:", user.email);
|
||||
console.log("Reset URL:", url);
|
||||
},
|
||||
},
|
||||
|
||||
// Session configuration
|
||||
session: {
|
||||
cookieName: "better-auth-session",
|
||||
updateSessionCookieAge: true,
|
||||
expiresIn: 60 * 60 * 24 * 30, // 30 days
|
||||
},
|
||||
|
||||
// User configuration
|
||||
user: {
|
||||
additionalFields: {
|
||||
// We can add custom fields here if needed
|
||||
},
|
||||
},
|
||||
|
||||
// Plugins for OIDC/SSO support
|
||||
plugins: [
|
||||
// SSO plugin for OIDC client support
|
||||
sso({
|
||||
provisionUser: async (data) => {
|
||||
// Custom user provisioning logic for SSO users
|
||||
console.log("Provisioning SSO user:", data);
|
||||
return data;
|
||||
},
|
||||
}),
|
||||
|
||||
// OIDC Provider plugin (for future use when we want to be an OIDC provider)
|
||||
oidcProvider({
|
||||
loginPage: "/signin",
|
||||
consentPage: "/oauth/consent",
|
||||
metadata: {
|
||||
issuer: process.env.BETTER_AUTH_URL || "http://localhost:3000",
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
// Trusted origins for CORS
|
||||
trustedOrigins: [
|
||||
process.env.BETTER_AUTH_URL || "http://localhost:3000",
|
||||
],
|
||||
});
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
/**
|
||||
* Example OIDC/SSO Configuration for Better Auth
|
||||
*
|
||||
* This file demonstrates how to enable OIDC and SSO features in Gitea Mirror.
|
||||
* To use: Copy this file to auth-oidc-config.ts and update the auth.ts import.
|
||||
*/
|
||||
|
||||
import { betterAuth } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import { sso } from "better-auth/plugins/sso";
|
||||
import { oidcProvider } from "better-auth/plugins/oidc";
|
||||
import type { BunSQLiteDatabase } from "drizzle-orm/bun-sqlite";
|
||||
|
||||
export function createAuthWithOIDC(db: BunSQLiteDatabase) {
|
||||
return betterAuth({
|
||||
// Database configuration
|
||||
database: drizzleAdapter(db, {
|
||||
provider: "sqlite",
|
||||
usePlural: true,
|
||||
}),
|
||||
|
||||
// Base configuration
|
||||
baseURL: process.env.BETTER_AUTH_URL || "http://localhost:3000",
|
||||
basePath: "/api/auth",
|
||||
|
||||
// Email/Password authentication
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
requireEmailVerification: false,
|
||||
},
|
||||
|
||||
// Session configuration
|
||||
session: {
|
||||
cookieName: "better-auth-session",
|
||||
updateSessionCookieAge: true,
|
||||
expiresIn: 60 * 60 * 24 * 30, // 30 days
|
||||
},
|
||||
|
||||
// User configuration with additional fields
|
||||
user: {
|
||||
additionalFields: {
|
||||
username: {
|
||||
type: "string",
|
||||
required: true,
|
||||
defaultValue: "user",
|
||||
input: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// OAuth2 providers (examples)
|
||||
socialProviders: {
|
||||
github: {
|
||||
enabled: !!process.env.GITHUB_OAUTH_CLIENT_ID,
|
||||
clientId: process.env.GITHUB_OAUTH_CLIENT_ID!,
|
||||
clientSecret: process.env.GITHUB_OAUTH_CLIENT_SECRET!,
|
||||
},
|
||||
google: {
|
||||
enabled: !!process.env.GOOGLE_OAUTH_CLIENT_ID,
|
||||
clientId: process.env.GOOGLE_OAUTH_CLIENT_ID!,
|
||||
clientSecret: process.env.GOOGLE_OAUTH_CLIENT_SECRET!,
|
||||
},
|
||||
},
|
||||
|
||||
// Plugins
|
||||
plugins: [
|
||||
// SSO Plugin - For OIDC/SAML client functionality
|
||||
sso({
|
||||
// Auto-provision users from SSO providers
|
||||
provisionUser: async (data) => {
|
||||
console.log("Provisioning SSO user:", data.email);
|
||||
|
||||
// Custom logic to set username from email
|
||||
const username = data.email.split('@')[0];
|
||||
|
||||
return {
|
||||
...data,
|
||||
username,
|
||||
};
|
||||
},
|
||||
|
||||
// Organization provisioning for enterprise SSO
|
||||
organizationProvisioning: {
|
||||
disabled: false,
|
||||
defaultRole: "member",
|
||||
getRole: async (user) => {
|
||||
// Custom logic to determine user role
|
||||
// For admin emails, grant admin role
|
||||
if (user.email?.endsWith('@admin.example.com')) {
|
||||
return 'admin';
|
||||
}
|
||||
return 'member';
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
// OIDC Provider Plugin - Makes Gitea Mirror an OIDC provider
|
||||
oidcProvider({
|
||||
// Login page for OIDC authentication flow
|
||||
loginPage: "/login",
|
||||
|
||||
// Consent page for OAuth2 authorization
|
||||
consentPage: "/oauth/consent",
|
||||
|
||||
// Allow dynamic client registration
|
||||
allowDynamicClientRegistration: false,
|
||||
|
||||
// OIDC metadata configuration
|
||||
metadata: {
|
||||
issuer: process.env.BETTER_AUTH_URL || "http://localhost:3000",
|
||||
authorization_endpoint: "/api/auth/oauth2/authorize",
|
||||
token_endpoint: "/api/auth/oauth2/token",
|
||||
userinfo_endpoint: "/api/auth/oauth2/userinfo",
|
||||
jwks_uri: "/api/auth/jwks",
|
||||
},
|
||||
|
||||
// Additional user info claims
|
||||
getAdditionalUserInfoClaim: (user, scopes) => {
|
||||
const claims: Record<string, any> = {};
|
||||
|
||||
// Add custom claims based on scopes
|
||||
if (scopes.includes('profile')) {
|
||||
claims.username = user.username;
|
||||
claims.preferred_username = user.username;
|
||||
}
|
||||
|
||||
if (scopes.includes('gitea')) {
|
||||
// Add Gitea-specific claims
|
||||
claims.gitea_admin = false; // Customize based on your logic
|
||||
claims.gitea_repos = []; // Could fetch user's repositories
|
||||
}
|
||||
|
||||
return claims;
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
// Trusted origins for CORS
|
||||
trustedOrigins: [
|
||||
process.env.BETTER_AUTH_URL || "http://localhost:3000",
|
||||
// Add your OIDC client domains here
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
// Environment variables needed:
|
||||
/*
|
||||
# OAuth2 Providers (optional)
|
||||
GITHUB_OAUTH_CLIENT_ID=your-github-client-id
|
||||
GITHUB_OAUTH_CLIENT_SECRET=your-github-client-secret
|
||||
GOOGLE_OAUTH_CLIENT_ID=your-google-client-id
|
||||
GOOGLE_OAUTH_CLIENT_SECRET=your-google-client-secret
|
||||
|
||||
# SSO Configuration (when registering providers)
|
||||
SSO_PROVIDER_ISSUER=https://idp.example.com
|
||||
SSO_PROVIDER_CLIENT_ID=your-client-id
|
||||
SSO_PROVIDER_CLIENT_SECRET=your-client-secret
|
||||
*/
|
||||
|
||||
// Example: Registering an SSO provider programmatically
|
||||
/*
|
||||
import { authClient } from "./auth-client";
|
||||
|
||||
// Register corporate SSO
|
||||
await authClient.sso.register({
|
||||
issuer: "https://login.microsoftonline.com/tenant-id/v2.0",
|
||||
domain: "company.com",
|
||||
clientId: process.env.AZURE_CLIENT_ID!,
|
||||
clientSecret: process.env.AZURE_CLIENT_SECRET!,
|
||||
providerId: "azure-ad",
|
||||
mapping: {
|
||||
id: "sub",
|
||||
email: "email",
|
||||
emailVerified: "email_verified",
|
||||
name: "name",
|
||||
image: "picture",
|
||||
},
|
||||
});
|
||||
*/
|
||||
@@ -1,7 +1,7 @@
|
||||
import { betterAuth } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import { oidcProvider } from "better-auth/plugins";
|
||||
import { sso } from "better-auth/plugins/sso";
|
||||
import { sso } from "@better-auth/sso";
|
||||
import { db, users } from "./db";
|
||||
import * as schema from "./db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
@@ -25,7 +25,7 @@ export const auth = betterAuth({
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
requireEmailVerification: false, // We'll enable this later
|
||||
sendResetPassword: async ({ user, url, token }, request) => {
|
||||
sendResetPassword: async ({ user, url }) => {
|
||||
// TODO: Implement email sending for password reset
|
||||
console.log("Password reset requested for:", user.email);
|
||||
console.log("Reset URL:", url);
|
||||
@@ -60,6 +60,8 @@ export const auth = betterAuth({
|
||||
consentPage: "/oauth/consent",
|
||||
// Allow dynamic client registration for flexibility
|
||||
allowDynamicClientRegistration: true,
|
||||
// Note: trustedClients would be configured here if Better Auth supports it
|
||||
// For now, we'll use dynamic registration
|
||||
// Customize user info claims based on scopes
|
||||
getAdditionalUserInfoClaim: (user, scopes) => {
|
||||
const claims: Record<string, any> = {};
|
||||
@@ -73,19 +75,32 @@ export const auth = betterAuth({
|
||||
// SSO plugin - allows users to authenticate with external OIDC providers
|
||||
sso({
|
||||
// Provision new users when they sign in with SSO
|
||||
provisionUser: async (user) => {
|
||||
provisionUser: async ({ user }: { user: any, userInfo: any }) => {
|
||||
// Derive username from email if not provided
|
||||
const username = user.name || user.email?.split('@')[0] || 'user';
|
||||
return {
|
||||
...user,
|
||||
username,
|
||||
};
|
||||
|
||||
// Update user in database if needed
|
||||
await db.update(users)
|
||||
.set({ username })
|
||||
.where(eq(users.id, user.id))
|
||||
.catch(() => {}); // Ignore errors if user doesn't exist yet
|
||||
},
|
||||
// Organization provisioning settings
|
||||
organizationProvisioning: {
|
||||
disabled: false,
|
||||
defaultRole: "member",
|
||||
getRole: async ({ user, userInfo }: { user: any, userInfo: any }) => {
|
||||
// Check if user has admin attribute from SSO provider
|
||||
const isAdmin = userInfo.attributes?.role === 'admin' ||
|
||||
userInfo.attributes?.groups?.includes('admins');
|
||||
|
||||
return isAdmin ? "admin" : "member";
|
||||
},
|
||||
},
|
||||
// Override user info with provider data by default
|
||||
defaultOverrideUserInfo: true,
|
||||
// Allow implicit sign up for new users
|
||||
disableImplicitSignUp: false,
|
||||
}),
|
||||
],
|
||||
|
||||
|
||||
Reference in New Issue
Block a user