mirror of
https://github.com/whekin/household-bot.git
synced 2026-04-01 04:24:02 +00:00
feat(infra): add reminder scheduler jobs
This commit is contained in:
81
apps/bot/src/scheduler-auth.ts
Normal file
81
apps/bot/src/scheduler-auth.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { OAuth2Client } from 'google-auth-library'
|
||||
|
||||
interface IdTokenPayload {
|
||||
email?: string
|
||||
email_verified?: boolean
|
||||
}
|
||||
|
||||
interface IdTokenTicket {
|
||||
getPayload(): IdTokenPayload | undefined
|
||||
}
|
||||
|
||||
export interface IdTokenVerifier {
|
||||
verifyIdToken(input: { idToken: string; audience: string }): Promise<IdTokenTicket>
|
||||
}
|
||||
|
||||
const DEFAULT_VERIFIER: IdTokenVerifier = new OAuth2Client()
|
||||
|
||||
function bearerToken(request: Request): string | null {
|
||||
const header = request.headers.get('authorization')
|
||||
|
||||
if (!header?.startsWith('Bearer ')) {
|
||||
return null
|
||||
}
|
||||
|
||||
const token = header.slice('Bearer '.length).trim()
|
||||
return token.length > 0 ? token : null
|
||||
}
|
||||
|
||||
export function createSchedulerRequestAuthorizer(options: {
|
||||
sharedSecret?: string
|
||||
oidcAudience?: string
|
||||
oidcAllowedEmails?: readonly string[]
|
||||
verifier?: IdTokenVerifier
|
||||
}): {
|
||||
authorize: (request: Request) => Promise<boolean>
|
||||
} {
|
||||
const sharedSecret = options.sharedSecret?.trim()
|
||||
const oidcAudience = options.oidcAudience?.trim()
|
||||
const allowedEmails = new Set(
|
||||
(options.oidcAllowedEmails ?? []).map((email) => email.trim()).filter(Boolean)
|
||||
)
|
||||
const verifier = options.verifier ?? DEFAULT_VERIFIER
|
||||
|
||||
return {
|
||||
authorize: async (request) => {
|
||||
const customHeader = request.headers.get('x-household-scheduler-secret')
|
||||
if (sharedSecret && customHeader === sharedSecret) {
|
||||
return true
|
||||
}
|
||||
|
||||
const token = bearerToken(request)
|
||||
if (!token) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (sharedSecret && token === sharedSecret) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (!oidcAudience || allowedEmails.size === 0) {
|
||||
if (allowedEmails.size === 0) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const audience = oidcAudience ?? new URL(request.url).origin
|
||||
const ticket = await verifier.verifyIdToken({
|
||||
idToken: token,
|
||||
audience
|
||||
})
|
||||
const payload = ticket.getPayload()
|
||||
const email = payload?.email?.trim()
|
||||
|
||||
return payload?.email_verified === true && email !== undefined && allowedEmails.has(email)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user