UNPKG

@better-auth/expo

Version:

Better Auth integration for Expo and React Native applications.

72 lines (69 loc) 2.72 kB
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 };