108 lines
3.4 KiB
TypeScript
108 lines
3.4 KiB
TypeScript
import { createError, type H3Event } from 'h3'
|
|
|
|
export interface AuthRuntimeConfig {
|
|
issuer: string
|
|
audience: string
|
|
accessTtlSec: number
|
|
refreshTtlSec: number
|
|
passwordResetTtlSec: number
|
|
privateKeyPem: string
|
|
publicKeyPem: string
|
|
kid: string
|
|
refreshTokenPepper: string
|
|
passwordResetTokenPepper: string
|
|
passwordResetBaseUrl: string
|
|
}
|
|
|
|
/**
|
|
* Converte uma string para inteiro positivo e valida o resultado.
|
|
*
|
|
* @param value Valor recebido do runtime config.
|
|
* @param label Nome amigável do campo para mensagens de erro.
|
|
* @returns Número inteiro positivo.
|
|
* @throws {H3Error} Quando o valor não for um inteiro positivo.
|
|
*/
|
|
function parsePositiveInt(value: string, label: string): number {
|
|
const parsed = Number.parseInt(value, 10)
|
|
|
|
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
throw createError({
|
|
statusCode: 500,
|
|
statusMessage: `${label} must be a positive integer`
|
|
})
|
|
}
|
|
|
|
return parsed
|
|
}
|
|
|
|
/**
|
|
* Normaliza uma chave PEM salva em variável de ambiente.
|
|
* Troca `\\n` por quebra de linha real e remove espaços nas pontas.
|
|
*
|
|
* @param rawValue Valor bruto vindo do ambiente.
|
|
* @param label Nome amigável do campo para mensagens de erro.
|
|
* @returns Chave PEM pronta para uso.
|
|
* @throws {H3Error} Quando a chave estiver vazia.
|
|
*/
|
|
function normalizePem(rawValue: string, label: string): string {
|
|
const normalized = rawValue.replace(/\\n/g, '\n').trim()
|
|
|
|
if (!normalized) {
|
|
throw createError({
|
|
statusCode: 500,
|
|
statusMessage: `${label} is required`
|
|
})
|
|
}
|
|
|
|
return normalized
|
|
}
|
|
|
|
/**
|
|
* Lê e valida todas as configurações obrigatórias de autenticação.
|
|
*
|
|
* @param event Evento da requisição (opcional), usado para acessar runtime config.
|
|
* @returns Objeto com configurações de JWT, refresh token e recuperação de senha já validadas.
|
|
* @throws {H3Error} Quando algum campo obrigatório estiver ausente ou inválido.
|
|
*/
|
|
export function getAuthRuntimeConfig(event?: H3Event): AuthRuntimeConfig {
|
|
const runtimeConfig = useRuntimeConfig(event)
|
|
|
|
const issuer = String(runtimeConfig.jwtIssuer ?? '').trim()
|
|
const audience = String(runtimeConfig.jwtAudience ?? '').trim()
|
|
const kid = String(runtimeConfig.jwtKid ?? '').trim()
|
|
|
|
if (!issuer) {
|
|
throw createError({ statusCode: 500, statusMessage: 'JWT issuer is required' })
|
|
}
|
|
|
|
if (!audience) {
|
|
throw createError({ statusCode: 500, statusMessage: 'JWT audience is required' })
|
|
}
|
|
|
|
if (!kid) {
|
|
throw createError({ statusCode: 500, statusMessage: 'JWT kid is required' })
|
|
}
|
|
|
|
const refreshTokenPepper = String(runtimeConfig.refreshTokenPepper ?? '').trim()
|
|
const passwordResetTokenPepper =
|
|
String(runtimeConfig.passwordResetTokenPepper ?? '').trim() || refreshTokenPepper
|
|
const passwordResetBaseUrl = String(runtimeConfig.passwordResetBaseUrl ?? '').trim()
|
|
|
|
return {
|
|
issuer,
|
|
audience,
|
|
kid,
|
|
accessTtlSec: parsePositiveInt(String(runtimeConfig.jwtAccessTtlSec), 'JWT access TTL'),
|
|
refreshTtlSec: parsePositiveInt(String(runtimeConfig.jwtRefreshTtlSec), 'JWT refresh TTL'),
|
|
passwordResetTtlSec: parsePositiveInt(
|
|
String(runtimeConfig.passwordResetTtlSec),
|
|
'Password reset TTL'
|
|
),
|
|
privateKeyPem: normalizePem(String(runtimeConfig.jwtPrivateKeyPem ?? ''), 'JWT private key'),
|
|
publicKeyPem: normalizePem(String(runtimeConfig.jwtPublicKeyPem ?? ''), 'JWT public key'),
|
|
refreshTokenPepper,
|
|
passwordResetTokenPepper,
|
|
passwordResetBaseUrl
|
|
}
|
|
}
|