forked from nowaythatworked/auth-astro
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver.ts
140 lines (126 loc) · 3.88 KB
/
server.ts
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/**
* > **caution**
* > `auth-astro` is currently experimental. Be aware of breaking changes between versions.
*
*
* Astro Auth is the unofficial Astro integration for Auth.js.
* It provides a simple way to add authentication to your Astro site in a few lines of code.
*
* ## Installation
*
* `auth-astro` requires building your site in `server` mode with a platform adaper like `@astrojs/node`.
* ```js
* // astro.config.mjs
* export default defineConfig({
* output: "server",
* adapter: node({
* mode: 'standalone'
* })
* });
* ```
*
* ```bash npm2yarn2pnpm
* npm install @auth/core @auth/astro
* ```
*/
import { Auth } from '@auth/core'
import type { AuthAction, Session } from '@auth/core/types'
import { type Cookie, parseString, splitCookiesString } from 'set-cookie-parser'
import { serialize } from 'cookie'
import authConfig from 'auth:config'
const actions: AuthAction[] = [
'providers',
'session',
'csrf',
'signin',
'signout',
'callback',
'verify-request',
'error',
]
// solves the same issue that exists in @auth/solid-js
const getSetCookieCallback = (cook?: string | null): Cookie | undefined => {
if (!cook) return
const splitCookie = splitCookiesString(cook)
for (const cookName of [
'__Secure-next-auth.session-token',
'next-auth.session-token',
'next-auth.pkce.code_verifier',
'__Secure-next-auth.pkce.code_verifier',
]) {
const temp = splitCookie.find((e) => e.startsWith(`${cookName}=`))
if (temp) {
return parseString(temp)
}
}
return parseString(splitCookie?.[0] ?? '') // just return the first cookie if no session token is found
}
function AstroAuthHandler(prefix: string, options = authConfig) {
return async ({ request }: { request: Request }) => {
const url = new URL(request.url)
const action = url.pathname.slice(prefix.length + 1).split('/')[0] as AuthAction
if (!actions.includes(action) || !url.pathname.startsWith(prefix + '/')) return
const res = await Auth(request, options)
if (['callback', 'signin', 'signout'].includes(action)) {
const parsedCookie = getSetCookieCallback(res.clone().headers.get('Set-Cookie'))
if (parsedCookie) {
res.headers.set(
'Set-Cookie',
serialize(parsedCookie.name, parsedCookie.value, parsedCookie as any)
)
}
}
return res
}
}
/**
* Creates a set of Astro endpoints for authentication.
*
* @example
* ```ts
* export const { GET, POST } = AstroAuth({
* providers: [
* GitHub({
* clientId: process.env.GITHUB_ID!,
* clientSecret: process.env.GITHUB_SECRET!,
* }),
* ],
* debug: false,
* })
* ```
* @param config The configuration for authentication providers and other options.
* @returns An object with `GET` and `POST` methods that can be exported in an Astro endpoint.
*/
export function AstroAuth(options = authConfig) {
// @ts-ignore
const { AUTH_SECRET, AUTH_TRUST_HOST, VERCEL, NODE_ENV } = import.meta.env
options.secret ??= AUTH_SECRET
options.trustHost ??= !!(AUTH_TRUST_HOST ?? VERCEL ?? NODE_ENV !== 'production')
const { prefix = '/api/auth', ...authOptions } = options
const handler = AstroAuthHandler(prefix, authOptions)
return {
async get(event: any) {
return await handler(event)
},
async post(event: any) {
return await handler(event)
},
}
}
/**
* Fetches the current session.
* @param req The request object.
* @returns The current session, or `null` if there is no session.
*/
export async function getSession(req: Request, options = authConfig): Promise<Session | null> {
// @ts-ignore
options.secret ??= import.meta.env.AUTH_SECRET
options.trustHost ??= true
const url = new URL(`${options.prefix}/session`, req.url)
const response = await Auth(new Request(url, { headers: req.headers }), options)
const { status = 200 } = response
const data = await response.json()
if (!data || !Object.keys(data).length) return null
if (status === 200) return data
throw new Error(data.message)
}