@better-auth/expo
Version:
Better Auth integration for Expo and React Native applications.
72 lines (69 loc) • 2.72 kB
JavaScript
import { createAuthMiddleware } from "@better-auth/core/api";
import { HIDE_METADATA } from "better-auth";
import { APIError, createAuthEndpoint } from "better-auth/api";
import * as z from "zod";
//#region src/routes.ts
const expoAuthorizationProxy = createAuthEndpoint("/expo-authorization-proxy", {
method: "GET",
query: z.object({
authorizationURL: z.string(),
oauthState: z.string().optional()
}),
metadata: HIDE_METADATA
}, async (ctx) => {
const { oauthState } = ctx.query;
if (oauthState) {
const oauthStateCookie = ctx.context.createAuthCookie("oauth_state", { maxAge: 600 * 1e3 });
ctx.setCookie(oauthStateCookie.name, oauthState, oauthStateCookie.attributes);
return ctx.redirect(ctx.query.authorizationURL);
}
const { authorizationURL } = ctx.query;
const state = new URL(authorizationURL).searchParams.get("state");
if (!state) throw new APIError("BAD_REQUEST", { message: "Unexpected error" });
const stateCookie = ctx.context.createAuthCookie("state", { maxAge: 300 * 1e3 });
await ctx.setSignedCookie(stateCookie.name, state, ctx.context.secret, stateCookie.attributes);
return ctx.redirect(ctx.query.authorizationURL);
});
//#endregion
//#region src/index.ts
const expo = (options) => {
return {
id: "expo",
init: (ctx) => {
return { options: { trustedOrigins: process.env.NODE_ENV === "development" ? ["exp://"] : [] } };
},
async onRequest(request, ctx) {
if (options?.disableOriginOverride || request.headers.get("origin")) return;
/**
* To bypass origin check from expo, we need to set the origin
* header to the expo-origin header
*/
const expoOrigin = request.headers.get("expo-origin");
if (!expoOrigin) return;
const req = request.clone();
req.headers.set("origin", expoOrigin);
return { request: req };
},
hooks: { after: [{
matcher(context) {
return !!(context.path?.startsWith("/callback") || context.path?.startsWith("/oauth2/callback") || context.path?.startsWith("/magic-link/verify") || context.path?.startsWith("/verify-email"));
},
handler: createAuthMiddleware(async (ctx) => {
const headers = ctx.context.responseHeaders;
const location = headers?.get("location");
if (!location) return;
if (location.includes("/oauth-proxy-callback")) return;
if (!ctx.context.trustedOrigins.filter((origin) => !origin.startsWith("http")).some((origin) => location?.startsWith(origin))) return;
const cookie = headers?.get("set-cookie");
if (!cookie) return;
const url = new URL(location);
url.searchParams.set("cookie", cookie);
ctx.setHeader("location", url.toString());
})
}] },
endpoints: { expoAuthorizationProxy },
options
};
};
//#endregion
export { expo };