ATlast — you'll never need to find your favorites on another platform again. Find your favs in the ATmosphere.
atproto
1import { Handler, HandlerEvent, HandlerResponse } from "@netlify/functions";
2import { CONFIG } from "./core/config/constants";
3
4export const handler: Handler = async (
5 event: HandlerEvent,
6): Promise<HandlerResponse> => {
7 try {
8 // Get the host that's requesting the metadata
9 // This will be different for production vs preview deploys vs dev --live
10 const requestHost = process.env.DEPLOY_URL
11 ? new URL(process.env.DEPLOY_URL).host
12 : event.headers["x-forwarded-host"] || event.headers.host;
13
14 if (!requestHost) {
15 return {
16 statusCode: 400,
17 headers: { "Content-Type": "application/json" },
18 body: JSON.stringify({ error: "Missing host header" }),
19 };
20 }
21
22 // Check if this is a loopback/development request
23 const isLoopback =
24 requestHost.startsWith("127.0.0.1") ||
25 requestHost.startsWith("[::1]") ||
26 requestHost === "localhost";
27
28 if (isLoopback) {
29 // For loopback clients, return minimal metadata
30 // NOTE: In practice, the OAuth server won't fetch this because
31 // loopback clients use hardcoded metadata on the server side
32 const appUrl = `http://${requestHost}`;
33 const redirectUri = `${appUrl}/.netlify/functions/oauth-callback`;
34
35 return {
36 statusCode: 200,
37 headers: {
38 "Content-Type": "application/json",
39 "Access-Control-Allow-Origin": "*",
40 },
41 body: JSON.stringify({
42 client_id: appUrl, // Just the origin for loopback
43 client_name: "ATlast (Local Dev)",
44 client_uri: appUrl,
45 redirect_uris: [redirectUri],
46 scope: CONFIG.OAUTH_SCOPES,
47 grant_types: ["authorization_code", "refresh_token"],
48 response_types: ["code"],
49 application_type: "web",
50 token_endpoint_auth_method: "none", // No auth for loopback
51 dpop_bound_access_tokens: true,
52 }),
53 };
54 }
55
56 // Production: Confidential client metadata
57 const redirectUri = `https://${requestHost}/.netlify/functions/oauth-callback`;
58 const appUrl = `https://${requestHost}`;
59 const jwksUri = `https://${requestHost}/.netlify/functions/jwks`;
60 const clientId = `https://${requestHost}/oauth-client-metadata.json`;
61 const logoUri = `https://${requestHost}/favicon.svg`;
62
63 const metadata = {
64 client_id: clientId,
65 client_name: "ATlast",
66 client_uri: appUrl,
67 redirect_uris: [redirectUri],
68 logo_uri: logoUri,
69 scope: CONFIG.OAUTH_SCOPES,
70 grant_types: ["authorization_code", "refresh_token"],
71 response_types: ["code"],
72 application_type: "web",
73 token_endpoint_auth_method: "private_key_jwt",
74 token_endpoint_auth_signing_alg: "ES256",
75 dpop_bound_access_tokens: true,
76 jwks_uri: jwksUri,
77 };
78
79 return {
80 statusCode: 200,
81 headers: {
82 "Content-Type": "application/json",
83 "Access-Control-Allow-Origin": "*",
84 "Cache-Control": "no-store",
85 },
86 body: JSON.stringify(metadata),
87 };
88 } catch (error) {
89 console.error("Client metadata error:", error);
90 return {
91 statusCode: 500,
92 headers: { "Content-Type": "application/json" },
93 body: JSON.stringify({ error: "Internal server error" }),
94 };
95 }
96};