-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathauthorize-url.ts
More file actions
94 lines (86 loc) · 3.31 KB
/
authorize-url.ts
File metadata and controls
94 lines (86 loc) · 3.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import { randomUUID } from 'node:crypto'
import type { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync } from 'fastify'
import { getDb } from '../../../../db/index.js'
import { verification } from '../../../../db/schema/index.js'
import { env } from '../../../../lib/env.js'
import { hashToken } from '../../../../lib/jwt.js'
import {
getOAuthAllowedCallbackUrls,
resolveOAuthCallbackUrl,
} from '../../../../lib/oauth-shared.js'
import { ErrorResponseSchema } from '../../../schemas.js'
const AuthorizeUrlResponseSchema = Type.Object({
redirectUrl: Type.String(),
})
const AuthorizeUrlQuerystringSchema = Type.Object({
redirect_uri: Type.Optional(Type.String()),
})
const oauthAuthorizeUrlRoute: FastifyPluginAsync = async fastify => {
fastify.withTypeProvider<TypeBoxTypeProvider>().get(
'/authorize-url',
{
schema: {
operationId: 'oauthGithubAuthorizeUrl',
description: 'Return GitHub OAuth authorization URL for client-side redirect',
summary: 'GitHub OAuth authorize URL',
tags: ['auth'],
security: [],
querystring: AuthorizeUrlQuerystringSchema,
response: {
200: AuthorizeUrlResponseSchema,
400: ErrorResponseSchema,
503: ErrorResponseSchema,
},
},
},
async (request, reply) => {
const githubClientId = env.GITHUB_CLIENT_ID
const githubClientSecret = env.GITHUB_CLIENT_SECRET
const allowedUrls = getOAuthAllowedCallbackUrls({
urls: env.OAUTH_GITHUB_CALLBACK_URLS,
singleUrl: env.OAUTH_GITHUB_CALLBACK_URL,
})
const resolved = resolveOAuthCallbackUrl({
allowedUrls,
requestedRedirectUri: request.query.redirect_uri,
})
if (!resolved.ok)
return reply.status(resolved.error === 'NOT_CONFIGURED' ? 503 : 400).send({
code:
resolved.error === 'NOT_CONFIGURED' ? 'OAUTH_NOT_CONFIGURED' : 'INVALID_REDIRECT_URI',
message:
resolved.error === 'NOT_CONFIGURED'
? 'GitHub OAuth is not configured'
: 'redirect_uri must be one of the configured callback URLs',
})
if (!githubClientId || !githubClientSecret)
return reply.status(503).send({
code: 'OAUTH_NOT_CONFIGURED',
message: 'GitHub OAuth is not configured',
})
const { redirectUri } = resolved
const state = randomUUID() + randomUUID().replace(/-/g, '')
const stateHash = hashToken(state)
const expiresAt = new Date(Date.now() + 10 * 60 * 1000)
const db = await getDb()
await db.insert(verification).values({
id: randomUUID(),
type: 'oauth_state',
identifier: stateHash,
value: stateHash,
meta: { redirectUri },
expiresAt,
})
const redirectUrl = new URL('https://github.com/login/oauth/authorize')
redirectUrl.searchParams.set('client_id', githubClientId)
redirectUrl.searchParams.set('redirect_uri', redirectUri)
redirectUrl.searchParams.set('scope', 'user:email')
redirectUrl.searchParams.set('state', state)
return reply.status(200).send({ redirectUrl: redirectUrl.toString() })
},
)
}
export default oauthAuthorizeUrlRoute
export const prefixOverride = '/auth/oauth/github'