kopia lustrzana https://github.com/cloudflare/wildebeest
121 wiersze
3.8 KiB
TypeScript
121 wiersze
3.8 KiB
TypeScript
import * as access from 'wildebeest/backend/src/access'
|
|
import * as actors from 'wildebeest/backend/src/activitypub/actors'
|
|
import type { Env } from 'wildebeest/backend/src/types/env'
|
|
import type { Identity, ContextData } from 'wildebeest/backend/src/types/context'
|
|
import * as errors from 'wildebeest/backend/src/errors'
|
|
import { loadLocalMastodonAccount } from 'wildebeest/backend/src/mastodon/account'
|
|
|
|
async function loadContextData(db: D1Database, clientId: string, email: string, ctx: any): Promise<boolean> {
|
|
const query = `
|
|
SELECT
|
|
actors.*,
|
|
(SELECT value FROM instance_config WHERE key='accessAud') as accessAud,
|
|
(SELECT value FROM instance_config WHERE key='accessDomain') as accessDomain
|
|
FROM actors
|
|
WHERE email=? AND type='Person'
|
|
`
|
|
const { results, success, error } = await db.prepare(query).bind(email).all()
|
|
if (!success) {
|
|
throw new Error('SQL error: ' + error)
|
|
}
|
|
|
|
if (!results || results.length === 0) {
|
|
console.warn('no results')
|
|
return false
|
|
}
|
|
|
|
const row: any = results[0]
|
|
|
|
if (!row.id) {
|
|
console.warn('person not found')
|
|
return false
|
|
}
|
|
if (!row.accessDomain || !row.accessAud) {
|
|
console.warn('access configuration not found')
|
|
return false
|
|
}
|
|
|
|
const person = actors.personFromRow(row)
|
|
|
|
ctx.data.connectedActor = person
|
|
ctx.data.identity = { email }
|
|
ctx.data.clientId = clientId
|
|
ctx.data.accessDomain = row.accessDomain
|
|
ctx.data.accessAud = row.accessAud
|
|
|
|
return true
|
|
}
|
|
|
|
export async function main(context: EventContext<Env, any, any>) {
|
|
if (context.request.method === 'OPTIONS') {
|
|
const headers = {
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Access-Control-Allow-Headers': 'content-type, authorization',
|
|
'Access-Control-Allow-Methods': 'GET, PUT, POST',
|
|
'content-type': 'application/json',
|
|
}
|
|
return new Response('', { headers })
|
|
}
|
|
|
|
const url = new URL(context.request.url)
|
|
if (
|
|
url.pathname === '/oauth/token' ||
|
|
url.pathname === '/oauth/authorize' || // Cloudflare Access runs on /oauth/authorize
|
|
url.pathname === '/api/v1/instance' ||
|
|
url.pathname === '/api/v2/instance' ||
|
|
url.pathname === '/api/v1/apps' ||
|
|
url.pathname === '/api/v1/timelines/public' ||
|
|
url.pathname === '/api/v1/custom_emojis' ||
|
|
url.pathname === '/.well-known/webfinger' ||
|
|
url.pathname === '/start-instance' || // Access is required by the handler
|
|
url.pathname === '/start-instance-test-access' || // Access is required by the handler
|
|
url.pathname.startsWith('/ap/') // all ActivityPub endpoints
|
|
) {
|
|
return context.next()
|
|
} else {
|
|
try {
|
|
const authorization = context.request.headers.get('Authorization') || ''
|
|
const token = authorization.replace('Bearer ', '')
|
|
|
|
if (token === '') {
|
|
return errors.notAuthorized('missing authorization')
|
|
}
|
|
|
|
const parts = token.split('.')
|
|
const [clientId, ...jwtParts] = parts
|
|
|
|
const jwt = jwtParts.join('.')
|
|
|
|
const payload = access.getPayload(jwt)
|
|
if (!payload.email) {
|
|
return errors.notAuthorized('missing email')
|
|
}
|
|
|
|
// Load the user associated with the email in the payload *before*
|
|
// verifying the JWT validity.
|
|
// This is because loading the context will also load the access
|
|
// configuration, which are used to verify the JWT.
|
|
if (!(await loadContextData(context.env.DATABASE, clientId, payload.email, context))) {
|
|
return errors.notAuthorized('failed to load context data')
|
|
}
|
|
|
|
const validatate = access.generateValidator({
|
|
jwt,
|
|
domain: context.data.accessDomain,
|
|
aud: context.data.accessAud,
|
|
})
|
|
await validatate(context.request)
|
|
|
|
const identity = await access.getIdentity({ jwt, domain: context.data.accessDomain })
|
|
if (!identity) {
|
|
return errors.notAuthorized('failed to load identity')
|
|
}
|
|
|
|
return context.next()
|
|
} catch (err: any) {
|
|
console.warn(err.stack)
|
|
return errors.notAuthorized('unknown error occurred')
|
|
}
|
|
}
|
|
}
|