1
Fork 0
mirror of https://github.com/Steffo99/festa.git synced 2024-12-23 15:14:23 +00:00
festa/utils/api/authenticator.ts

99 lines
3.1 KiB
TypeScript
Raw Normal View History

2022-06-11 03:08:49 +00:00
import { Response as Response } from "./throwables"
import { Token, User } from "@prisma/client"
import { database } from "../prismaClient"
/**
* Object containing multiple functions related to API authentication.
*
* @see {@link festaAPI} and its parameters {@link FestaAPI}.
*/
export type FestaAuthenticator<Config, Auth> = {
/**
* The value that the `WWW-Authenticate` header should be set to in a handler using this authenticator.
*/
header: string,
/**
* Async function which uses the value of the `Authorization` HTTP header to authenticate the user agent performing the API request.
*
* Any thrown {@link Error} should be caught by the handler, and its {@link Error.message} returned by the API.
*
* @param config - The current configuration of the request handler.
* @param header - The value of the `Authorization` HTTP header.
* @throws {ThrowableResponse}
*/
perform: (params: { config: Config, header: string | undefined }) => Promise<Auth>,
}
/**
* {@link FestaAuthenticator} which does not require any authentication.
*
* Never throws.
*/
export const festaNoAuth: FestaAuthenticator<any, {}> = {
header: "",
perform: async ({ }) => {
return {}
},
}
/**
* Structure of the authentication data returned by the Festa API.
*/
export type FestaToken = Token & { user: User }
/**
* {@link FestaAuthenticator} which authenticates the user agent based on the value of its Bearer token.
*
* Returns the {@link FestaToken} if successful, or `undefined` if the Authorization header is missing.
*
* Throws an HTTP 401 error if the header is malformed or the token is invalid or expired.
*/
export const festaBearerAuthOptional: FestaAuthenticator<any, FestaToken | undefined> = {
header: "Bearer",
perform: async ({ header }) => {
if (!header || header === "false") {
return undefined
}
const token = header.match(/^Bearer (\S+)$/)?.[1]
if (!token) {
throw Response.error({ status: 401, message: "Malformed Authorization header" })
}
const dbToken = await database.token.findUnique({ where: { token }, include: { user: true } })
if (!dbToken) {
throw Response.error({ status: 401, message: "Invalid Authorization token" })
}
const now = new Date()
if (dbToken.expiresAt.getTime() < now.getTime()) {
throw Response.error({ status: 401, message: "Expired Authorization token" })
}
return dbToken
}
}
/**
* {@link FestaAuthenticator} which authenticates the user agent based on the value of its Bearer token.
*
* Returns the {@link FestaToken} if successful.
*
* Throws an HTTP 401 error if the header is missing or malformed, or the token is invalid or expired.
*/
export const festaBearerAuthRequired: FestaAuthenticator<any, FestaToken> = {
header: "Bearer",
perform: async (params) => {
if (!params.header) {
throw Response.error({ status: 401, message: "Missing Authorization header" })
}
return await festaBearerAuthOptional.perform(params) as FestaToken
}
}