Revert "merge: sync GitHub staging to Gitea"
Deploy staging to test server / deploy (push) Failing after 14m49s
Deploy staging to test server / deploy (push) Failing after 14m49s
This reverts commita55c69115d, reversing changes made to02c9c9f3d6.
This commit is contained in:
@@ -1,20 +1,18 @@
|
||||
import { createHmac } from "node:crypto";
|
||||
import type { Pool } from "pg";
|
||||
import { APIError, type BetterAuthOptions } from "better-auth";
|
||||
import type { BetterAuthOptions } from "better-auth";
|
||||
import { admin, emailOTP, type EmailOTPOptions } from "better-auth/plugins";
|
||||
|
||||
import type { SelfHostedIdentityConfig } from "./config.ts";
|
||||
import type { EmailOtpSender, IdentitySurface } from "./contracts.ts";
|
||||
import type { EmailOtpSender } from "./contracts.ts";
|
||||
import { identityModelMapping } from "./model.ts";
|
||||
|
||||
export type AdminUserAuthorizer = (userId: string) => Promise<boolean>;
|
||||
|
||||
interface BuildAuthOptionsInput {
|
||||
surface: IdentitySurface;
|
||||
config: SelfHostedIdentityConfig;
|
||||
database: Pool;
|
||||
emailSender: EmailOtpSender;
|
||||
authorizeAdminUser?: AdminUserAuthorizer;
|
||||
}
|
||||
|
||||
function otpIdempotencyKey(
|
||||
@@ -58,26 +56,17 @@ export function createEmailOtpOptions(
|
||||
}
|
||||
|
||||
export function buildAuthOptions({
|
||||
surface,
|
||||
config,
|
||||
database,
|
||||
emailSender,
|
||||
authorizeAdminUser,
|
||||
}: BuildAuthOptionsInput): BetterAuthOptions {
|
||||
if (surface === "admin" && !authorizeAdminUser) {
|
||||
throw new Error("admin user authorizer is required");
|
||||
}
|
||||
|
||||
const origin = surface === "user" ? config.userOrigin : config.adminOrigin;
|
||||
const secret = surface === "user" ? config.userSecret : config.adminSecret;
|
||||
|
||||
return {
|
||||
appName: "Jyotisha",
|
||||
baseURL: origin,
|
||||
baseURL: config.userOrigin,
|
||||
basePath: "/api/auth",
|
||||
secret,
|
||||
secret: config.userSecret,
|
||||
database,
|
||||
trustedOrigins: [origin],
|
||||
trustedOrigins: [config.userOrigin],
|
||||
telemetry: { enabled: false },
|
||||
user: identityModelMapping.user,
|
||||
session: identityModelMapping.session,
|
||||
@@ -91,8 +80,7 @@ export function buildAuthOptions({
|
||||
},
|
||||
advanced: {
|
||||
database: { generateId: "uuid" },
|
||||
cookiePrefix:
|
||||
surface === "user" ? "jyotisha-user" : "jyotisha-admin",
|
||||
cookiePrefix: "jyotisha-user",
|
||||
defaultCookieAttributes: {
|
||||
secure: true,
|
||||
httpOnly: true,
|
||||
@@ -108,31 +96,12 @@ export function buildAuthOptions({
|
||||
revokeSessionsOnPasswordReset: true,
|
||||
},
|
||||
plugins: [
|
||||
...(surface === "user"
|
||||
? [emailOTP(createEmailOtpOptions(emailSender, secret, false))]
|
||||
: []),
|
||||
emailOTP(createEmailOtpOptions(emailSender, config.userSecret, false)),
|
||||
admin({
|
||||
defaultRole: "user",
|
||||
adminRoles: ["admin"],
|
||||
schema: identityModelMapping.admin,
|
||||
}),
|
||||
],
|
||||
...(surface === "admin"
|
||||
? {
|
||||
databaseHooks: {
|
||||
session: {
|
||||
create: {
|
||||
async before(session: { userId: string }) {
|
||||
if (!(await authorizeAdminUser!(session.userId))) {
|
||||
throw new APIError("FORBIDDEN", {
|
||||
message: "Administrator access required",
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ interface AdminRoleRow {
|
||||
ban_expires: Date | null;
|
||||
}
|
||||
|
||||
export type IdentityAdminSurfaceRole = "admin" | "viewer";
|
||||
export type IdentityAdminSurfaceRole = "admin";
|
||||
|
||||
export function createIdentityPool(databaseUrl: string): Pool {
|
||||
return new Pool({
|
||||
@@ -68,19 +68,17 @@ export function createDatabaseAdminAuthorizer(
|
||||
export function createDatabaseAdminSurfaceAuthorizer(
|
||||
pool: Pool,
|
||||
): AdminUserAuthorizer {
|
||||
return createDatabaseRoleAuthorizer(pool, new Set(["admin", "viewer"]));
|
||||
return createDatabaseAdminAuthorizer(pool);
|
||||
}
|
||||
|
||||
export interface IdentityAuthServices {
|
||||
pool: Pool;
|
||||
user: ReturnType<typeof betterAuth>;
|
||||
admin: ReturnType<typeof betterAuth>;
|
||||
}
|
||||
|
||||
interface IdentityAuthDependencies {
|
||||
pool?: Pool;
|
||||
emailSender?: EmailOtpSender;
|
||||
authorizeAdminUser?: AdminUserAuthorizer;
|
||||
}
|
||||
|
||||
export function createIdentityAuthServices(
|
||||
@@ -94,28 +92,15 @@ export function createIdentityAuthServices(
|
||||
apiKey: config.resendApiKey,
|
||||
from: config.resendFrom,
|
||||
});
|
||||
const authorizeAdminUser =
|
||||
dependencies.authorizeAdminUser ?? createDatabaseAdminSurfaceAuthorizer(pool);
|
||||
|
||||
return {
|
||||
pool,
|
||||
user: betterAuth(
|
||||
buildAuthOptions({
|
||||
surface: "user",
|
||||
config,
|
||||
database: pool,
|
||||
emailSender,
|
||||
}),
|
||||
),
|
||||
admin: betterAuth(
|
||||
buildAuthOptions({
|
||||
surface: "admin",
|
||||
config,
|
||||
database: pool,
|
||||
emailSender,
|
||||
authorizeAdminUser,
|
||||
}),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,7 @@ export interface SelfHostedIdentityConfig {
|
||||
provider: "self-hosted";
|
||||
databaseUrl: string;
|
||||
userOrigin: string;
|
||||
adminOrigin: string;
|
||||
userSecret: string;
|
||||
adminSecret: string;
|
||||
resendApiKey: string;
|
||||
resendFrom: string;
|
||||
}
|
||||
@@ -95,24 +93,13 @@ export function readSelfHostedIdentityConfig(
|
||||
env: IdentityEnvironment,
|
||||
): SelfHostedIdentityConfig {
|
||||
const userOrigin = readOrigin(env, "AUTH_USER_ORIGIN");
|
||||
const adminOrigin = readOrigin(env, "AUTH_ADMIN_ORIGIN");
|
||||
if (userOrigin === adminOrigin) {
|
||||
throw new Error("user and admin origins must be different");
|
||||
}
|
||||
|
||||
const userSecret = readSecret(env, "BETTER_AUTH_USER_SECRET");
|
||||
const adminSecret = readSecret(env, "BETTER_AUTH_ADMIN_SECRET");
|
||||
if (userSecret === adminSecret) {
|
||||
throw new Error("user and admin secrets must be different");
|
||||
}
|
||||
|
||||
return {
|
||||
provider: "self-hosted",
|
||||
databaseUrl: readPostgresUrl(env),
|
||||
userOrigin,
|
||||
adminOrigin,
|
||||
userSecret,
|
||||
adminSecret,
|
||||
resendApiKey: required(env, "RESEND_API_KEY"),
|
||||
resendFrom: readSender(env),
|
||||
};
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
export type IdentitySurface = "user" | "admin";
|
||||
|
||||
export type EmailOtpType =
|
||||
| "sign-in"
|
||||
| "email-verification"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { SelfHostedIdentityConfig } from "./config.ts";
|
||||
import type { IdentitySurface } from "./contracts.ts";
|
||||
|
||||
export type IdentityRequestHandler = (
|
||||
request: Request,
|
||||
@@ -33,15 +32,11 @@ function normalizeHost(value: string | null): string | null {
|
||||
export function resolveIdentitySurface(
|
||||
hostHeader: string | null,
|
||||
config: SelfHostedIdentityConfig,
|
||||
): IdentitySurface | null {
|
||||
): "user" | null {
|
||||
const host = normalizeHost(hostHeader);
|
||||
if (!host) return null;
|
||||
|
||||
const userHost = new URL(config.userOrigin).host.toLowerCase();
|
||||
const adminHost = new URL(config.adminOrigin).host.toLowerCase();
|
||||
if (host === userHost) return "user";
|
||||
if (host === adminHost) return "admin";
|
||||
return null;
|
||||
return host === new URL(config.userOrigin).host.toLowerCase() ? "user" : null;
|
||||
}
|
||||
|
||||
function isAdminEndpoint(request: Request): boolean {
|
||||
@@ -55,7 +50,7 @@ function isAdminEndpoint(request: Request): boolean {
|
||||
|
||||
export function createHostIsolatedAuthHandlers(
|
||||
config: SelfHostedIdentityConfig,
|
||||
handlers: Record<IdentitySurface, IdentityAuthHandlers>,
|
||||
handlers: { user: IdentityAuthHandlers },
|
||||
): IdentityAuthHandlers {
|
||||
const dispatch =
|
||||
(method: keyof IdentityAuthHandlers): IdentityRequestHandler =>
|
||||
@@ -64,10 +59,10 @@ export function createHostIsolatedAuthHandlers(
|
||||
if (!surface) {
|
||||
return new Response("Unrecognized identity host", { status: 421 });
|
||||
}
|
||||
if (surface === "user" && isAdminEndpoint(request)) {
|
||||
if (isAdminEndpoint(request)) {
|
||||
return new Response("Not found", { status: 404 });
|
||||
}
|
||||
return handlers[surface][method](request);
|
||||
return handlers.user[method](request);
|
||||
};
|
||||
|
||||
return { GET: dispatch("GET"), POST: dispatch("POST") };
|
||||
|
||||
Reference in New Issue
Block a user