Created
January 4, 2023 17:37
-
-
Save sergiodxa/030468445814cb18d9e49d8518c6ab31 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Auth0 from "@auth/core/providers/auth0"; | |
import { RemixAuth } from "~/services/auth" | |
import { sessionStorage } from "~/services/session.server"; | |
export let { loader, action } = RemixAuth({ | |
sessionStorage: sessionStorage, // this does nothing yet | |
secret: process.env.AUTH_SECRET ?? "s3cr3t", | |
providers: [ | |
Auth0({ | |
clientId: process.env.AUTH0_CLIENT_ID, | |
clientSecret: process.env.AUTH0_CLIENT_SECRET, | |
issuer: process.env.AUTH0_ISSUER_BASE_URL, | |
}), | |
], | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { AuthAction, AuthConfig, Session } from "@auth/core/types"; | |
import type { | |
ActionFunction, | |
LoaderFunction, | |
SessionStorage, | |
} from "@remix-run/node"; | |
import type { Cookie } from "set-cookie-parser"; | |
import { Auth } from "@auth/core"; | |
import { serialize } from "cookie"; | |
import { parseString, splitCookiesString } from "set-cookie-parser"; | |
global.crypto = require("crypto"); // hack required because Auth.js uses global.crypto even in Node | |
export interface RemixAuthConfig extends AuthConfig { | |
/** | |
* Defines the base path for the auth routes. | |
* @default '/api/auth' | |
*/ | |
prefix?: string; | |
sessionStorage: SessionStorage; | |
} | |
const actions: Set<AuthAction> = new Set([ | |
"providers", | |
"session", | |
"csrf", | |
"signin", | |
"signout", | |
"callback", | |
"verify-request", | |
"error", | |
]); | |
// Copied from Solid implementation, ideally we use SessionStorage | |
const getSetCookieCallback = (cook?: string | null): Cookie | undefined => { | |
if (!cook) return; | |
let splitCookie = splitCookiesString(cook); | |
for (let cookName of [ | |
"__Secure-next-auth.session-token", | |
"next-auth.session-token", | |
"next-auth.pkce.code_verifier", | |
"__Secure-next-auth.pkce.code_verifier", | |
]) { | |
let 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 RemixAuthHandler( | |
prefix: string, | |
authOptions: RemixAuthConfig | |
): LoaderFunction | ActionFunction { | |
return async ({ request, params }) => { | |
let { pathname } = new URL(request.url); | |
let action = params["*"].split("/").at(0) as AuthAction; | |
if (!actions.has(action) || !pathname.startsWith(prefix + "/")) { | |
return null; | |
} | |
let res = await Auth(request, authOptions); | |
if (["callback", "signin", "signout"].includes(action)) { | |
let parsedCookie = getSetCookieCallback(res.headers.get("Set-Cookie")); | |
if (parsedCookie) { | |
res.headers.set( | |
"Set-Cookie", | |
serialize(parsedCookie.name, parsedCookie.value, parsedCookie as any) | |
); | |
} | |
} | |
return res; | |
}; | |
} | |
export function RemixAuth(config: RemixAuthConfig) { | |
let { prefix = "/api/auth", ...authOptions } = config; | |
authOptions.secret ??= process.env.AUTH_SECRET; | |
authOptions.trustHost ??= !!( | |
process.env.AUTH_TRUST_HOST ?? | |
process.env.VERCEL ?? | |
process.env.NODE_ENV !== "production" | |
); | |
let handler = RemixAuthHandler(prefix, authOptions); | |
let result: { loader: LoaderFunction; action: ActionFunction } = { | |
async loader(args) { | |
return handler(args); | |
}, | |
async action(args) { | |
return handler(args); | |
}, | |
}; | |
return result; | |
} | |
export type GetSessionResult = Promise<Session | null>; | |
export async function getSession( | |
req: Request, | |
options: AuthConfig | |
): GetSessionResult { | |
options.secret ??= process.env.AUTH_SECRET; | |
options.trustHost ??= true; | |
let url = new URL("/api/auth/session", req.url); | |
let response = await Auth( | |
new Request(url, { headers: req.headers }), | |
options | |
); | |
let { status = 200 } = response; | |
let data = await response.json(); | |
if (!data || Object.keys(data).length === 0) return null; | |
if (status === 200) return data as Session; | |
throw new Error((data as { message: string }).message); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment