feat(oidc): added in so we could use an oidc to login as well :D
This commit is contained in:
@@ -2,6 +2,7 @@ import { betterAuth } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import {
|
||||
admin as adminPlugin,
|
||||
genericOAuth,
|
||||
// apiKey,
|
||||
// createAuthMiddleware,
|
||||
//customSession,
|
||||
@@ -16,6 +17,46 @@ import { ac, admin, systemAdmin, user } from "./auth.permissions.js";
|
||||
import { allowedOrigins } from "./cors.utils.js";
|
||||
import { sendEmail } from "./sendEmail.utils.js";
|
||||
|
||||
function decodeJwtPayload<T = Record<string, unknown>>(jwt: string): T {
|
||||
const parts = jwt.split(".");
|
||||
if (parts.length < 2) {
|
||||
throw new Error("Invalid JWT");
|
||||
}
|
||||
|
||||
const payload = parts[1]?.replace(/-/g, "+").replace(/_/g, "/");
|
||||
|
||||
const padded = payload?.padEnd(
|
||||
payload.length + ((4 - (payload.length % 4)) % 4),
|
||||
"=",
|
||||
);
|
||||
|
||||
const json = Buffer.from(padded ?? "", "base64").toString("utf8");
|
||||
return JSON.parse(json) as T;
|
||||
}
|
||||
|
||||
function normalizeGroups(groups?: unknown): string[] {
|
||||
if (!Array.isArray(groups)) return [];
|
||||
|
||||
return groups
|
||||
.filter((g): g is string => typeof g === "string")
|
||||
.map((g) => g.trim().toLowerCase())
|
||||
.filter((g) => g.length > 0);
|
||||
}
|
||||
|
||||
type VoidAuthClaims = {
|
||||
sub: string;
|
||||
name?: string;
|
||||
preferred_username?: string;
|
||||
email?: string;
|
||||
email_verified?: boolean;
|
||||
groups?: string[];
|
||||
picture?: string;
|
||||
iss?: string;
|
||||
aud?: string;
|
||||
exp?: number;
|
||||
iat?: number;
|
||||
};
|
||||
|
||||
export const schema = {
|
||||
user: rawSchema.user,
|
||||
session: rawSchema.session,
|
||||
@@ -25,9 +66,73 @@ export const schema = {
|
||||
apiKey: rawSchema.apikey, // 🔑 rename to apiKey
|
||||
};
|
||||
|
||||
const hasOAuth =
|
||||
Boolean(process.env.PROVIDER) &&
|
||||
Boolean(process.env.CLIENT_ID) &&
|
||||
Boolean(process.env.CLIENT_SECRET) &&
|
||||
Boolean(process.env.DISCOVERY_URL);
|
||||
|
||||
if (!hasOAuth) {
|
||||
console.warn("Missing oauth data.");
|
||||
}
|
||||
|
||||
const oauthPlugins = hasOAuth
|
||||
? [
|
||||
genericOAuth({
|
||||
config: [
|
||||
{
|
||||
providerId: process.env.PROVIDER!,
|
||||
clientId: process.env.CLIENT_ID!,
|
||||
clientSecret: process.env.CLIENT_SECRET!,
|
||||
discoveryUrl: process.env.DISCOVERY_URL!,
|
||||
scopes: (process.env.CLIENT_SCOPES ?? "")
|
||||
.split(/[,\s]+/)
|
||||
.filter(Boolean),
|
||||
pkce: true,
|
||||
requireIssuerValidation: true,
|
||||
redirectURI: `${process.env.URL}/lst/api/auth/oauth2/callback/${process.env.PROVIDER!}`,
|
||||
getUserInfo: async (tokens) => {
|
||||
if (!tokens.idToken) {
|
||||
throw new Error("VoidAuth did not return an idToken");
|
||||
}
|
||||
|
||||
const claims = decodeJwtPayload<VoidAuthClaims>(tokens.idToken);
|
||||
const groups = normalizeGroups(claims.groups);
|
||||
|
||||
return {
|
||||
id: claims.sub,
|
||||
email: claims.email ?? "",
|
||||
name:
|
||||
claims.name ??
|
||||
claims.preferred_username ??
|
||||
claims.email ??
|
||||
"Unknown User",
|
||||
image: claims.picture ?? null,
|
||||
emailVerified: Boolean(claims.email_verified),
|
||||
groups,
|
||||
username: claims.preferred_username ?? null,
|
||||
} as any;
|
||||
},
|
||||
|
||||
mapProfileToUser: async (profile) => {
|
||||
return {
|
||||
name: profile.name,
|
||||
role: profile.groups?.includes("lst_admins")
|
||||
? "systemAdmin"
|
||||
: profile.groups?.includes("admins")
|
||||
? "admin"
|
||||
: "user",
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
]
|
||||
: [];
|
||||
|
||||
export const auth = betterAuth({
|
||||
appName: "lst",
|
||||
baseURL: process.env.URL,
|
||||
baseURL: `${process.env.URL}/lst/api/auth`,
|
||||
database: drizzleAdapter(db, {
|
||||
provider: "pg",
|
||||
schema,
|
||||
@@ -42,6 +147,14 @@ export const auth = betterAuth({
|
||||
},
|
||||
},
|
||||
},
|
||||
account: {
|
||||
encryptOAuthTokens: true,
|
||||
updateAccountOnSignIn: true,
|
||||
accountLinking: {
|
||||
enabled: true,
|
||||
trustedProviders: ["voidauth"],
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
jwt({ jwt: { expirationTime: "1h" } }),
|
||||
//apiKey(),
|
||||
@@ -63,6 +176,7 @@ export const auth = betterAuth({
|
||||
return true;
|
||||
},
|
||||
}),
|
||||
...oauthPlugins,
|
||||
|
||||
// customSession(async ({ user, session }) => {
|
||||
// const roles = await db
|
||||
@@ -121,7 +235,7 @@ export const auth = betterAuth({
|
||||
},
|
||||
},
|
||||
cookie: {
|
||||
path: "/lst/app",
|
||||
path: "/lst",
|
||||
sameSite: "lax",
|
||||
secure: false,
|
||||
httpOnly: true,
|
||||
|
||||
Reference in New Issue
Block a user