This commit is contained in:
@@ -1,35 +1,88 @@
|
||||
import { env } from '$env/dynamic/private';
|
||||
import { building, dev } from '$app/environment';
|
||||
import { betterAuth } from 'better-auth/minimal';
|
||||
import { drizzleAdapter } from 'better-auth/adapters/drizzle';
|
||||
import { genericOAuth } from 'better-auth/plugins';
|
||||
import { APIError, createAuthMiddleware } from 'better-auth/api';
|
||||
import { sveltekitCookies } from 'better-auth/svelte-kit';
|
||||
import { getRequestEvent } from '$app/server';
|
||||
import { db } from '$lib/server/db';
|
||||
import * as schema from '$lib/server/db/schema';
|
||||
import { error, type RequestEvent } from '@sveltejs/kit';
|
||||
import { osuProfile } from './osu-profile';
|
||||
export { isAdmin, requireAdmin, requireUser } from './authorization';
|
||||
|
||||
if (!building && (!env.BETTER_AUTH_SECRET || env.BETTER_AUTH_SECRET.length < 32)) {
|
||||
throw new Error('Set BETTER_AUTH_SECRET to a random secret of at least 32 characters');
|
||||
}
|
||||
if (!building && !dev && !env.BETTER_AUTH_URL && !env.ORIGIN) {
|
||||
throw new Error('Set BETTER_AUTH_URL to the public site origin');
|
||||
}
|
||||
export const authProviders = {
|
||||
google: !!(env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET),
|
||||
osu: !!(env.OSU_CLIENT_ID && env.OSU_CLIENT_SECRET)
|
||||
};
|
||||
export const auth = betterAuth({
|
||||
baseURL: env.ORIGIN ?? 'http://localhost:3000',
|
||||
secret: env.BETTER_AUTH_SECRET ?? 'build-time-placeholder',
|
||||
baseURL: env.BETTER_AUTH_URL || env.ORIGIN || 'http://localhost:5173',
|
||||
secret: env.BETTER_AUTH_SECRET || 'build-only-placeholder-not-a-runtime-secret',
|
||||
database: drizzleAdapter(db, { provider: 'pg', schema }),
|
||||
emailAndPassword: { enabled: true },
|
||||
socialProviders: {
|
||||
github: {
|
||||
clientId: env.GITHUB_CLIENT_ID,
|
||||
clientSecret: env.GITHUB_CLIENT_SECRET
|
||||
emailAndPassword: { enabled: true, minPasswordLength: 12, maxPasswordLength: 128 },
|
||||
user: {
|
||||
additionalFields: {
|
||||
role: { type: 'string', required: false, defaultValue: 'user', input: false }
|
||||
}
|
||||
},
|
||||
account: {
|
||||
accountLinking: {
|
||||
enabled: true,
|
||||
disableImplicitLinking: true,
|
||||
allowDifferentEmails: true,
|
||||
trustedProviders: ['osu']
|
||||
}
|
||||
},
|
||||
rateLimit: { enabled: true },
|
||||
socialProviders: authProviders.google
|
||||
? {
|
||||
google: { clientId: env.GOOGLE_CLIENT_ID!, clientSecret: env.GOOGLE_CLIENT_SECRET! }
|
||||
}
|
||||
: {},
|
||||
hooks: {
|
||||
before: createAuthMiddleware(async (ctx) => {
|
||||
// osu! has no email scope. Reserve its internal identifiers against credential signup.
|
||||
if (
|
||||
ctx.path === '/sign-up/email' &&
|
||||
typeof ctx.body?.email === 'string' &&
|
||||
ctx.body.email.toLowerCase().endsWith('.invalid')
|
||||
) {
|
||||
throw new APIError('BAD_REQUEST', { message: 'Please use a deliverable email address' });
|
||||
}
|
||||
})
|
||||
},
|
||||
plugins: [
|
||||
sveltekitCookies(getRequestEvent) // make sure this is the last plugin in the array
|
||||
genericOAuth({
|
||||
config: authProviders.osu
|
||||
? [
|
||||
{
|
||||
providerId: 'osu',
|
||||
clientId: env.OSU_CLIENT_ID!,
|
||||
clientSecret: env.OSU_CLIENT_SECRET!,
|
||||
authorizationUrl: 'https://osu.ppy.sh/oauth/authorize',
|
||||
tokenUrl: 'https://osu.ppy.sh/oauth/token',
|
||||
scopes: ['identify'],
|
||||
getUserInfo: async (tokens) => {
|
||||
const response = await fetch('https://osu.ppy.sh/api/v2/me', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens.accessToken}`,
|
||||
Accept: 'application/json'
|
||||
},
|
||||
signal: AbortSignal.timeout(10000)
|
||||
});
|
||||
if (!response.ok) throw new Error('Could not load osu! profile');
|
||||
return osuProfile(await response.json());
|
||||
}
|
||||
}
|
||||
]
|
||||
: []
|
||||
}),
|
||||
sveltekitCookies(getRequestEvent)
|
||||
]
|
||||
});
|
||||
|
||||
export function isAdmin(event: RequestEvent): boolean {
|
||||
const token = event.cookies.get('admin_token');
|
||||
return !!token && token === env.ADMIN_SECRET;
|
||||
}
|
||||
|
||||
export function requireAdmin(event: RequestEvent): void {
|
||||
if (!isAdmin(event)) {
|
||||
error(401, { message: 'Unauthorized' });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user