@better-auth/expo
Version:
Better Auth integration for Expo and React Native applications.
84 lines (81 loc) • 2.99 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 });
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 });
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;
try {
request.headers.set("origin", expoOrigin);
return { request };
} catch {
const newHeaders = new Headers(request.headers);
newHeaders.set("origin", expoOrigin);
return { request: new Request(request, { headers: newHeaders }) };
}
},
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;
let redirectURL;
try {
redirectURL = new URL(location);
} catch {
return;
}
if (redirectURL.protocol === "http:" || redirectURL.protocol === "https:") return;
if (!ctx.context.isTrustedOrigin(location)) return;
const cookie = headers?.get("set-cookie");
if (!cookie) return;
redirectURL.searchParams.set("cookie", cookie);
ctx.setHeader("location", redirectURL.toString());
})
}] },
endpoints: { expoAuthorizationProxy },
options
};
};
//#endregion
export { expo };
//# sourceMappingURL=index.js.map