-
Notifications
You must be signed in to change notification settings - Fork 54
feat: Add APIContext object to Auth Config #81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,9 +25,11 @@ | |
| */ | ||
| import { Auth } from '@auth/core' | ||
| import type { AuthAction, Session } from '@auth/core/types' | ||
| import type { APIContext } from 'astro' | ||
| import type { APIContext, AstroGlobal } from 'astro' | ||
| import { parseString } from 'set-cookie-parser' | ||
| import authConfig from 'auth:config' | ||
| import type { UserAuthConfig } from './src/config' | ||
| import type { ActionAPIContext } from 'astro/dist/actions/runtime/store' | ||
|
|
||
| const actions: AuthAction[] = [ | ||
| 'providers', | ||
|
|
@@ -41,13 +43,15 @@ const actions: AuthAction[] = [ | |
| ] | ||
|
|
||
| function AstroAuthHandler(prefix: string, options = authConfig) { | ||
| return async ({ cookies, request }: APIContext) => { | ||
| return async (ctx: APIContext) => { | ||
| const { cookies, request } = ctx | ||
| 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) | ||
| const config = isUserConfigLazy(options) ? await options.config(ctx) : options | ||
| const res = await Auth(request, config) | ||
| if (['callback', 'signin', 'signout'].includes(action)) { | ||
| // Properly handle multiple Set-Cookie headers (they can't be concatenated in one) | ||
| const getSetCookie = res.headers.getSetCookie() | ||
|
|
@@ -86,17 +90,23 @@ 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(context: APIContext) { | ||
| const config = isUserConfigLazy(options) ? await options.config(context) : options | ||
| config.secret ??= AUTH_SECRET | ||
| config.trustHost ??= !!(AUTH_TRUST_HOST ?? VERCEL ?? NODE_ENV !== 'production') | ||
|
|
||
| const { prefix = '/api/auth', ...authOptions } = config | ||
| const handler = AstroAuthHandler(prefix, authOptions) | ||
| return await handler(context) | ||
| }, | ||
| async POST(context: APIContext) { | ||
| const config = isUserConfigLazy(options) ? await options.config(context) : options | ||
| config.secret ??= AUTH_SECRET | ||
| config.trustHost ??= !!(AUTH_TRUST_HOST ?? VERCEL ?? NODE_ENV !== 'production') | ||
|
|
||
| const { prefix = '/api/auth', ...authOptions } = config | ||
| const handler = AstroAuthHandler(prefix, authOptions) | ||
| return await handler(context) | ||
| }, | ||
| } | ||
|
|
@@ -108,10 +118,14 @@ export function AstroAuth(options = authConfig) { | |
| * @returns The current session, or `null` if there is no session. | ||
| */ | ||
| export async function getSession(req: Request, options = authConfig): Promise<Session | null> { | ||
| if (isUserConfigLazy(options)) { | ||
| throw new Error( | ||
| 'User Auth Configuration is Lazy. Fetch the session using getSessionByContext().' | ||
| ) | ||
| } | ||
|
Comment on lines
+121
to
+125
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since you're already checking for lazy config, we could have the functionality of |
||
| // @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 | ||
|
|
@@ -122,3 +136,20 @@ export async function getSession(req: Request, options = authConfig): Promise<Se | |
| if (status === 200) return data | ||
| throw new Error(data.message) | ||
| } | ||
|
|
||
| /** | ||
| * Fetches the current session when using a lazy auth config. | ||
| * @param ctx The Astro global object, or APIContext. | ||
| * @returns The current session, or `null` if there is no session. | ||
| */ | ||
| export async function getSessionByContext( | ||
| ctx: AstroGlobal | APIContext | ActionAPIContext, | ||
| options = authConfig | ||
| ): Promise<Session | null> { | ||
| const config = isUserConfigLazy(options) ? await options.config(ctx) : options | ||
| return await getSession(ctx.request, config) | ||
| } | ||
|
|
||
| export function isUserConfigLazy(config: UserAuthConfig) { | ||
| return 'config' in config | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,7 @@ | ||||||||||||||||||||||
| import type { PluginOption } from 'vite' | ||||||||||||||||||||||
| import type { AuthConfig } from '@auth/core/types' | ||||||||||||||||||||||
| import type { APIContext, AstroGlobal } from 'astro' | ||||||||||||||||||||||
| import type { ActionAPIContext } from 'astro/dist/actions/runtime/store' | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| export const virtualConfigModule = (configFile: string = './auth.config'): PluginOption => { | ||||||||||||||||||||||
| const virtualModuleId = 'auth:config' | ||||||||||||||||||||||
|
|
@@ -27,7 +29,7 @@ export interface AstroAuthConfig { | |||||||||||||||||||||
| */ | ||||||||||||||||||||||
| prefix?: string | ||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Defineds wether or not you want the integration to handle the API routes | ||||||||||||||||||||||
| * Defines whether or not you want the integration to handle the API routes | ||||||||||||||||||||||
| * @default true | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| injectEndpoints?: boolean | ||||||||||||||||||||||
|
|
@@ -43,3 +45,9 @@ export const defineConfig = (config: FullAuthConfig) => { | |||||||||||||||||||||
| config.basePath = config.prefix | ||||||||||||||||||||||
| return config | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| export type UserAuthConfig = | ||||||||||||||||||||||
| | { | ||||||||||||||||||||||
| config: (ctx: APIContext | AstroGlobal | ActionAPIContext) => FullAuthConfig | Promise<FullAuthConfig> | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| | FullAuthConfig | ||||||||||||||||||||||
|
Comment on lines
+49
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you guys think about this?
Suggested change
|
||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would be nice to have this a bit cleaner, not so repetitive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @nowaythatworked @JordanSekky
I think this can be moved to the
AstroAuthHandlerimplementation.Resolving config with
await options.config(context)is also could be a part of that handler.