···5566export async function getIdentityData() {
77 let cookieStore = await cookies();
88- let auth_token = cookieStore.get("auth_token")?.value;
88+ let auth_token =
99+ cookieStore.get("auth_token")?.value ||
1010+ cookieStore.get("external_auth_token");
911 let auth_res = auth_token
1012 ? await supabaseServerClient
1113 .from("email_auth_tokens")
+5
app/api/auth/logout/route.ts
···11import { NextRequest } from "next/server";
22+import { supabaseServerClient } from "supabase/serverClient";
23export const runtime = "edge";
34export const preferredRegion = [];
55+export const dynamic = "force-dynamic";
4657export async function GET(req: NextRequest) {
68 const host = req.headers.get("host");
···13151416 // Get the base domain from the host
1517 const domain = host?.includes(":") ? host.split(":")[0] : host;
1818+ let token = req.cookies.get("auth_token");
1919+ if (token)
2020+ supabaseServerClient.from("email_auth_tokens").delete().eq("id", token);
16211722 // Clear the auth_token cookie on both the base domain and the domain with a leading dot
1823 response.headers.append(
+85
middleware.ts
···2020 process.env.NEXT_PUBLIC_SUPABASE_API_URL as string,
2121 process.env.SUPABASE_SERVICE_ROLE_KEY as string,
2222);
2323+2424+const auth_callback_route = "/auth_callback";
2525+const receive_auth_callback_route = "/receive_auth_callback";
2326export default async function middleware(req: NextRequest) {
2427 let hostname = req.headers.get("host")!;
2828+ if (req.nextUrl.pathname === auth_callback_route) return authCallback(req);
2929+ if (req.nextUrl.pathname === receive_auth_callback_route)
3030+ return receiveAuthCallback(req);
3131+2532 if (hostname === "leaflet.pub") return;
2633 if (req.nextUrl.pathname === "/not-found") return;
2734 let { data: routes } = await supabase
···34413542 let pub = routes?.publication_domains[0]?.publications;
3643 if (pub) {
4444+ let cookie = req.cookies.get("external_auth_token");
4545+ if (!cookie) {
4646+ return initiateAuthCallback(req);
4747+ }
3748 let aturi = new AtUri(pub?.uri);
3849 return NextResponse.rewrite(
3950 new URL(
···5566 }
5667 }
5768}
6969+7070+type CROSS_SITE_AUTH_REQUEST = { redirect: string };
7171+type CROSS_SITE_AUTH_RESPONSE = { redirect: string; auth_token: string | null };
7272+async function initiateAuthCallback(req: NextRequest) {
7373+ let token: CROSS_SITE_AUTH_REQUEST = { redirect: req.url };
7474+ let payload = btoa(JSON.stringify(token));
7575+ let signature = signCrossSiteToken(payload);
7676+ return NextResponse.redirect(
7777+ `https://leaflet.pub${auth_callback_route}?payload=${payload}&signature=${signature}`,
7878+ );
7979+}
8080+8181+async function authCallback(req: NextRequest) {
8282+ let payload = req.nextUrl.searchParams.get("payload");
8383+ let signature = req.nextUrl.searchParams.get("signature");
8484+8585+ if (typeof payload !== "string")
8686+ return new NextResponse(null, { status: 401 });
8787+8888+ let verifySig = await signCrossSiteToken(payload);
8989+ if (verifySig !== signature) return new NextResponse(null, { status: 401 });
9090+9191+ let token: CROSS_SITE_AUTH_REQUEST = JSON.parse(atob(payload));
9292+ let auth_token = req.cookies.get("auth_token")?.value || null;
9393+ let redirect_url = new URL(token.redirect);
9494+ let response_token: CROSS_SITE_AUTH_RESPONSE = {
9595+ redirect: token.redirect,
9696+ auth_token,
9797+ };
9898+9999+ let response_payload = btoa(JSON.stringify(response_token));
100100+ let sig = signCrossSiteToken(response_payload);
101101+ return NextResponse.redirect(
102102+ `https://${redirect_url.host}${receive_auth_callback_route}?payload=${response_payload}&signature=${sig}`,
103103+ );
104104+}
105105+106106+async function receiveAuthCallback(req: NextRequest) {
107107+ let payload = req.nextUrl.searchParams.get("payload");
108108+ let signature = req.nextUrl.searchParams.get("signature");
109109+110110+ if (typeof payload !== "string")
111111+ return new NextResponse(null, { status: 401 });
112112+113113+ let verifySig = await signCrossSiteToken(payload);
114114+ if (verifySig !== signature) return new NextResponse(null, { status: 401 });
115115+116116+ let token: CROSS_SITE_AUTH_RESPONSE = JSON.parse(atob(payload));
117117+118118+ let response = NextResponse.redirect(token.redirect);
119119+ response.cookies.set("external_auth_token", token.auth_token || "null");
120120+ return response;
121121+}
122122+123123+const signCrossSiteToken = async (input: string) => {
124124+ if (!process.env.CROSS_SITE_AUTH_SECRET)
125125+ throw new Error("Environment variable CROSS_SITE_AUTH_SECRET not set ");
126126+ const encoder = new TextEncoder();
127127+ const data = encoder.encode(input);
128128+ const secretKey = process.env.CROSS_SITE_AUTH_SECRET;
129129+ const keyData = encoder.encode(secretKey);
130130+131131+ const key = await crypto.subtle.importKey(
132132+ "raw",
133133+ keyData,
134134+ { name: "HMAC", hash: "SHA-256" },
135135+ false,
136136+ ["sign"],
137137+ );
138138+139139+ const signature = await crypto.subtle.sign("HMAC", key, data);
140140+141141+ return btoa(String.fromCharCode(...new Uint8Array(signature)));
142142+};