- Implemented `useDesignPersistence` composable for managing design records. - Enhanced `useSlipmatDesigner` to support loading designs from JSON. - Created global authentication middleware for route protection. - Added Supabase client plugin for database interactions. - Developed API endpoints for fetching, saving, and retrieving designs. - Introduced utility functions for Auth0 token verification and Supabase client retrieval. - Updated Nuxt configuration to include Auth0 and Supabase environment variables. - Added necessary dependencies for Auth0 and Supabase. - Enhanced TypeScript configuration for improved type support.
83 lines
2.1 KiB
TypeScript
83 lines
2.1 KiB
TypeScript
import { createError, getHeader, type H3Event } from "h3";
|
|
import { useRuntimeConfig } from "#imports";
|
|
import {
|
|
createRemoteJWKSet,
|
|
jwtVerify,
|
|
type JWTPayload,
|
|
type JWTVerifyOptions,
|
|
} from "jose";
|
|
|
|
const globalKey = Symbol.for("slipmatz.auth0.jwks");
|
|
|
|
type GlobalWithJwks = typeof globalThis & {
|
|
[globalKey]?: ReturnType<typeof createRemoteJWKSet>;
|
|
};
|
|
|
|
const getJwks = (issuer: string) => {
|
|
const globalScope = globalThis as GlobalWithJwks;
|
|
if (!globalScope[globalKey]) {
|
|
globalScope[globalKey] = createRemoteJWKSet(
|
|
new URL(`${issuer}.well-known/jwks.json`)
|
|
);
|
|
}
|
|
return globalScope[globalKey]!;
|
|
};
|
|
|
|
export type Auth0TokenPayload = JWTPayload & {
|
|
sub: string;
|
|
scope?: string;
|
|
permissions?: string[];
|
|
};
|
|
|
|
export const verifyAccessToken = async (token: string): Promise<Auth0TokenPayload> => {
|
|
const config = useRuntimeConfig();
|
|
const domain = config.public.auth0?.domain;
|
|
|
|
if (!domain) {
|
|
throw createError({
|
|
statusCode: 500,
|
|
statusMessage: "Auth0 domain is not configured.",
|
|
});
|
|
}
|
|
|
|
const issuer = `https://${domain}/`;
|
|
const jwks = getJwks(issuer);
|
|
|
|
const options: JWTVerifyOptions = {
|
|
issuer,
|
|
};
|
|
|
|
if (config.public.auth0?.audience) {
|
|
options.audience = config.public.auth0.audience;
|
|
}
|
|
|
|
const { payload } = await jwtVerify(token, jwks, options);
|
|
if (!payload.sub) {
|
|
throw createError({
|
|
statusCode: 401,
|
|
statusMessage: "Invalid access token payload.",
|
|
});
|
|
}
|
|
|
|
return payload as Auth0TokenPayload;
|
|
};
|
|
|
|
export const requireAuth0User = async (event: H3Event): Promise<Auth0TokenPayload> => {
|
|
const header = getHeader(event, "authorization");
|
|
if (!header) {
|
|
throw createError({ statusCode: 401, statusMessage: "Authorization header missing." });
|
|
}
|
|
|
|
const match = header.match(/^Bearer\s+(.+)$/i);
|
|
if (!match) {
|
|
throw createError({ statusCode: 401, statusMessage: "Invalid authorization format." });
|
|
}
|
|
|
|
const token = match[1];
|
|
try {
|
|
return await verifyAccessToken(token);
|
|
} catch (error) {
|
|
throw createError({ statusCode: 401, statusMessage: "Access token verification failed." });
|
|
}
|
|
};
|