kopia lustrzana https://github.com/cloudflare/wildebeest
MOW-132: redirect to /first-login for unkown user
rodzic
c151a8aba6
commit
cd9551b58f
|
@ -36,9 +36,17 @@ describe('Mastodon APIs', () => {
|
|||
|
||||
let req = new Request('https://example.com/oauth/authorize')
|
||||
let res = await oauth_authorize.handleRequestPost(req, db, userKEK, accessDomain, accessAud)
|
||||
assert.equal(res.status, 401)
|
||||
|
||||
const headers = {
|
||||
'Cf-Access-Jwt-Assertion': TEST_JWT,
|
||||
}
|
||||
|
||||
req = new Request('https://example.com/oauth/authorize', { headers })
|
||||
res = await oauth_authorize.handleRequestPost(req, db, userKEK, accessDomain, accessAud)
|
||||
assert.equal(res.status, 400)
|
||||
|
||||
req = new Request('https://example.com/oauth/authorize?scope=foobar')
|
||||
req = new Request('https://example.com/oauth/authorize?scope=foobar', { headers })
|
||||
res = await oauth_authorize.handleRequestPost(req, db, userKEK, accessDomain, accessAud)
|
||||
assert.equal(res.status, 400)
|
||||
})
|
||||
|
@ -46,13 +54,17 @@ describe('Mastodon APIs', () => {
|
|||
test('authorize unsupported response_type', async () => {
|
||||
const db = await makeDB()
|
||||
|
||||
const headers = {
|
||||
'Cf-Access-Jwt-Assertion': TEST_JWT,
|
||||
}
|
||||
|
||||
const params = new URLSearchParams({
|
||||
redirect_uri: 'https://example.com',
|
||||
response_type: 'hein',
|
||||
client_id: 'client_id',
|
||||
})
|
||||
|
||||
const req = new Request('https://example.com/oauth/authorize?' + params)
|
||||
const req = new Request('https://example.com/oauth/authorize?' + params, { headers })
|
||||
const res = await oauth_authorize.handleRequestPost(req, db, userKEK, accessDomain, accessAud)
|
||||
assert.equal(res.status, 400)
|
||||
})
|
||||
|
|
|
@ -5,8 +5,9 @@ import { getClientById } from 'wildebeest/backend/src/mastodon/client'
|
|||
import { DocumentHead, loader$ } from '@builder.io/qwik-city'
|
||||
import { WildebeestLogo } from '~/components/MastodonLogo'
|
||||
import { Avatar } from '~/components/avatar'
|
||||
import { getPersonByEmail, Person } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import { getPersonByEmail } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import { getErrorHtml } from '~/utils/getErrorHtml/getErrorHtml'
|
||||
import { buildRedirect } from 'wildebeest/functions/oauth/authorize'
|
||||
|
||||
export const clientLoader = loader$<{ DATABASE: D1Database }, Promise<Client>>(async ({ platform, query, html }) => {
|
||||
const client_id = query.get('client_id') || ''
|
||||
|
@ -25,7 +26,7 @@ export const clientLoader = loader$<{ DATABASE: D1Database }, Promise<Client>>(a
|
|||
export const userLoader = loader$<
|
||||
{ DATABASE: D1Database; domain: string },
|
||||
Promise<{ email: string; avatar: URL; name: string; url: URL }>
|
||||
>(async ({ cookie, platform, html }) => {
|
||||
>(async ({ cookie, platform, html, request, redirect, text }) => {
|
||||
const jwt = cookie.get('CF_Authorization')
|
||||
if (jwt === null) {
|
||||
throw html(500, getErrorHtml('Missing Authorization'))
|
||||
|
@ -44,15 +45,15 @@ export const userLoader = loader$<
|
|||
throw html(500, getErrorHtml("The Access JWT doesn't contain an email"))
|
||||
}
|
||||
|
||||
let person: Person | null = null
|
||||
|
||||
try {
|
||||
person = await getPersonByEmail(platform.DATABASE, payload.email)
|
||||
if (!person) {
|
||||
throw new Error('Person not found')
|
||||
const person = await getPersonByEmail(platform.DATABASE, payload.email)
|
||||
if (person === null) {
|
||||
const isFirstLogin = true
|
||||
const res = await buildRedirect(platform.DATABASE, request as Request, isFirstLogin, jwt.value)
|
||||
if (res.status === 302) {
|
||||
throw redirect(302, res.headers.get('location') || '')
|
||||
} else {
|
||||
throw text(res.status, await res.body.text())
|
||||
}
|
||||
} catch {
|
||||
throw html(500, getErrorHtml(`Failed to fetch a person for the provided email (${payload.email})`))
|
||||
}
|
||||
|
||||
const name = person.name
|
||||
|
@ -60,7 +61,7 @@ export const userLoader = loader$<
|
|||
const url = person.url
|
||||
|
||||
if (!name || !avatar) {
|
||||
throw html(500, getErrorHtml("Error: The person associated with the Access JWT doesn't include a name or avatar"))
|
||||
throw html(500, getErrorHtml("The person associated with the Access JWT doesn't include a name or avatar"))
|
||||
}
|
||||
|
||||
return { email: payload.email, avatar, name, url }
|
||||
|
|
|
@ -15,21 +15,12 @@ export const onRequestPost: PagesFunction<Env, any, ContextData> = async ({ requ
|
|||
return handleRequestPost(request, env.DATABASE, env.userKEK, env.ACCESS_AUTH_DOMAIN, env.ACCESS_AUD)
|
||||
}
|
||||
|
||||
export async function handleRequestPost(
|
||||
request: Request,
|
||||
export async function buildRedirect(
|
||||
db: D1Database,
|
||||
userKEK: string,
|
||||
accessDomain: string,
|
||||
accessAud: string
|
||||
request: Request,
|
||||
isFirstLogin: boolean,
|
||||
jwt: string
|
||||
): Promise<Response> {
|
||||
if (request.method === 'OPTIONS') {
|
||||
const headers = {
|
||||
...cors(),
|
||||
'content-type': 'application/json',
|
||||
}
|
||||
return new Response('', { headers })
|
||||
}
|
||||
|
||||
const url = new URL(request.url)
|
||||
|
||||
if (
|
||||
|
@ -47,6 +38,8 @@ export async function handleRequestPost(
|
|||
return new Response('', { status: 400 })
|
||||
}
|
||||
|
||||
const state = url.searchParams.get('state')
|
||||
|
||||
const clientId = url.searchParams.get('client_id') || ''
|
||||
const client = await getClientById(db, clientId)
|
||||
if (client === null) {
|
||||
|
@ -58,9 +51,36 @@ export async function handleRequestPost(
|
|||
return new Response('', { status: 403 })
|
||||
}
|
||||
|
||||
const state = url.searchParams.get('state')
|
||||
const code = `${client.id}.${jwt}`
|
||||
const redirect = redirect_uri + `?code=${code}` + (state ? `&state=${state}` : '')
|
||||
|
||||
if (isFirstLogin) {
|
||||
url.pathname = '/first-login'
|
||||
url.searchParams.set('redirect_uri', encodeURIComponent(redirect))
|
||||
return URLsafeRedirect(url.toString())
|
||||
}
|
||||
return URLsafeRedirect(redirect)
|
||||
}
|
||||
|
||||
export async function handleRequestPost(
|
||||
request: Request,
|
||||
db: D1Database,
|
||||
userKEK: string,
|
||||
accessDomain: string,
|
||||
accessAud: string
|
||||
): Promise<Response> {
|
||||
if (request.method === 'OPTIONS') {
|
||||
const headers = {
|
||||
...cors(),
|
||||
'content-type': 'application/json',
|
||||
}
|
||||
return new Response('', { headers })
|
||||
}
|
||||
|
||||
const jwt = extractJWTFromRequest(request)
|
||||
if (!jwt) {
|
||||
return new Response('', { status: 401 })
|
||||
}
|
||||
const validate = access.generateValidator({ jwt, domain: accessDomain, aud: accessAud })
|
||||
await validate(request)
|
||||
|
||||
|
@ -69,19 +89,9 @@ export async function handleRequestPost(
|
|||
return new Response('', { status: 401 })
|
||||
}
|
||||
|
||||
const code = `${client.id}.${jwt}`
|
||||
const isFirstLogin = (await getPersonByEmail(db, identity.email)) === null
|
||||
|
||||
const redirect = redirect_uri + `?code=${code}` + (state ? `&state=${state}` : '')
|
||||
|
||||
const person = await getPersonByEmail(db, identity.email)
|
||||
if (person === null) {
|
||||
url.pathname = '/first-login'
|
||||
url.searchParams.set('email', identity.email)
|
||||
url.searchParams.set('redirect_uri', encodeURIComponent(redirect))
|
||||
return URLsafeRedirect(url.toString())
|
||||
}
|
||||
|
||||
return URLsafeRedirect(redirect)
|
||||
return buildRedirect(db, request, isFirstLogin, jwt)
|
||||
}
|
||||
|
||||
// Workaround bug EW-7148, constructing an URL with unknown protocols
|
||||
|
|
Ładowanie…
Reference in New Issue