Files
gitea-mirror/src/lib/auth.ts
Arunavo Ray de314cf174 Fixed Tests
2025-07-27 19:09:56 +05:30

116 lines
3.8 KiB
TypeScript

import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { oidcProvider } from "better-auth/plugins";
import { sso } from "@better-auth/sso";
import { db, users } from "./db";
import * as schema from "./db/schema";
import { eq } from "drizzle-orm";
export const auth = betterAuth({
// Database configuration
database: drizzleAdapter(db, {
provider: "sqlite",
usePlural: true, // Our tables use plural names (users, not user)
schema, // Pass the schema explicitly
}),
// Secret for signing tokens
secret: process.env.BETTER_AUTH_SECRET,
// Base URL configuration
baseURL: process.env.BETTER_AUTH_URL || "http://localhost:4321",
basePath: "/api/auth", // Specify the base path for auth endpoints
// Trusted origins for OAuth flows
trustedOrigins: [
"http://localhost:4321",
"http://localhost:8080", // Keycloak
process.env.BETTER_AUTH_URL || "http://localhost:4321"
].filter(Boolean),
// Authentication methods
emailAndPassword: {
enabled: true,
requireEmailVerification: false, // We'll enable this later
sendResetPassword: async ({ user, url }) => {
// 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: {
// Keep the username field from our existing schema
username: {
type: "string",
required: false,
input: false, // Don't show in signup form - we'll derive from email
}
},
},
// Plugins configuration
plugins: [
// OIDC Provider plugin - allows this app to act as an OIDC provider
oidcProvider({
loginPage: "/login",
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> = {};
if (scopes.includes("profile")) {
claims.username = user.username;
}
return claims;
},
}),
// SSO plugin - allows users to authenticate with external OIDC providers
sso({
// Provision new users when they sign in with SSO
provisionUser: async ({ user }: { user: any, userInfo: any }) => {
// Derive username from email if not provided
const username = user.name || user.email?.split('@')[0] || 'user';
// 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 ({ 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,
}),
],
});
// Export type for use in other parts of the app
export type Auth = typeof auth;