mirror of
https://github.com/RayLabsHQ/gitea-mirror.git
synced 2025-12-17 03:43:46 +03:00
Added Header Authentication
This commit is contained in:
135
src/lib/auth-header.ts
Normal file
135
src/lib/auth-header.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import { db, users } from "./db";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
export interface HeaderAuthConfig {
|
||||
enabled: boolean;
|
||||
userHeader: string;
|
||||
emailHeader?: string;
|
||||
nameHeader?: string;
|
||||
autoProvision: boolean;
|
||||
allowedDomains?: string[];
|
||||
}
|
||||
|
||||
// Default configuration - DISABLED by default
|
||||
export const defaultHeaderAuthConfig: HeaderAuthConfig = {
|
||||
enabled: false,
|
||||
userHeader: "X-Authentik-Username", // Common header name
|
||||
emailHeader: "X-Authentik-Email",
|
||||
nameHeader: "X-Authentik-Name",
|
||||
autoProvision: false,
|
||||
allowedDomains: [],
|
||||
};
|
||||
|
||||
// Get header auth config from environment or database
|
||||
export function getHeaderAuthConfig(): HeaderAuthConfig {
|
||||
// Check environment variables for header auth config
|
||||
const envConfig: Partial<HeaderAuthConfig> = {
|
||||
enabled: process.env.HEADER_AUTH_ENABLED === "true",
|
||||
userHeader: process.env.HEADER_AUTH_USER_HEADER || defaultHeaderAuthConfig.userHeader,
|
||||
emailHeader: process.env.HEADER_AUTH_EMAIL_HEADER || defaultHeaderAuthConfig.emailHeader,
|
||||
nameHeader: process.env.HEADER_AUTH_NAME_HEADER || defaultHeaderAuthConfig.nameHeader,
|
||||
autoProvision: process.env.HEADER_AUTH_AUTO_PROVISION === "true",
|
||||
allowedDomains: process.env.HEADER_AUTH_ALLOWED_DOMAINS?.split(",").map(d => d.trim()),
|
||||
};
|
||||
|
||||
return {
|
||||
...defaultHeaderAuthConfig,
|
||||
...envConfig,
|
||||
};
|
||||
}
|
||||
|
||||
// Check if header authentication is enabled
|
||||
export function isHeaderAuthEnabled(): boolean {
|
||||
const config = getHeaderAuthConfig();
|
||||
return config.enabled === true;
|
||||
}
|
||||
|
||||
// Extract user info from headers
|
||||
export function extractUserFromHeaders(headers: Headers): {
|
||||
username?: string;
|
||||
email?: string;
|
||||
name?: string;
|
||||
} | null {
|
||||
const config = getHeaderAuthConfig();
|
||||
|
||||
if (!config.enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const username = headers.get(config.userHeader);
|
||||
const email = config.emailHeader ? headers.get(config.emailHeader) : undefined;
|
||||
const name = config.nameHeader ? headers.get(config.nameHeader) : undefined;
|
||||
|
||||
if (!username) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If allowed domains are configured, check email domain
|
||||
if (config.allowedDomains && config.allowedDomains.length > 0 && email) {
|
||||
const domain = email.split("@")[1];
|
||||
if (!config.allowedDomains.includes(domain)) {
|
||||
console.warn(`Header auth rejected: email domain ${domain} not in allowed list`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return { username, email, name };
|
||||
}
|
||||
|
||||
// Find or create user from header auth
|
||||
export async function authenticateWithHeaders(headers: Headers) {
|
||||
const userInfo = extractUserFromHeaders(headers);
|
||||
|
||||
if (!userInfo || !userInfo.username) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const config = getHeaderAuthConfig();
|
||||
|
||||
// Try to find existing user by username or email
|
||||
let existingUser = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.username, userInfo.username))
|
||||
.limit(1);
|
||||
|
||||
if (existingUser.length === 0 && userInfo.email) {
|
||||
existingUser = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.email, userInfo.email))
|
||||
.limit(1);
|
||||
}
|
||||
|
||||
if (existingUser.length > 0) {
|
||||
return existingUser[0];
|
||||
}
|
||||
|
||||
// If auto-provisioning is disabled, don't create new users
|
||||
if (!config.autoProvision) {
|
||||
console.warn(`Header auth: User ${userInfo.username} not found and auto-provisioning is disabled`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create new user if auto-provisioning is enabled
|
||||
try {
|
||||
const newUser = {
|
||||
id: nanoid(),
|
||||
username: userInfo.username,
|
||||
email: userInfo.email || `${userInfo.username}@header-auth.local`,
|
||||
emailVerified: true, // Trust the auth provider
|
||||
name: userInfo.name || userInfo.username,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
await db.insert(users).values(newUser);
|
||||
console.log(`Header auth: Auto-provisioned new user ${userInfo.username}`);
|
||||
|
||||
return newUser;
|
||||
} catch (error) {
|
||||
console.error("Failed to auto-provision user from header auth:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user