···11import type { PrivateUserData } from "$lib/valibot.ts";
22import type { OAuthClient } from "@atcute/oauth-node-client";
3344-// See https://svelte.dev/docs/kit/types#app.d.ts
55-// for information about these interfaces
64declare global {
75 namespace App {
86 // interface Error {}
···1210 }
1311 // interface PageData {}
1412 // interface PageState {}
1515- // interface Platform {}
1313+ interface Platform {
1414+ env: {
1515+ ASSETS: {
1616+ fetch: typeof fetch;
1717+ };
1818+ ORIGIN: string;
1919+ PRIVATE_COOKIE_KEY: string;
2020+ PRIVATE_KEY_JWK?: string;
2121+ };
2222+ }
1623 }
1724}
1825
+1-1
src/lexicons/index.ts
···11-export * as SocialAtticActorProfile from "./types/social/attic/actor/profile.js";
11+export * as SocialAtticActorProfile from "./types/social/attic/actor/profile.ts";
+54-13
src/lib/server/oauth.ts
···11import { dev } from "$app/environment";
22-import { env } from "$env/dynamic/private";
32import {
43 OAUTH_COOKIE_PREFIX,
54 OAUTH_MAX_AGE,
···98import {
109 CompositeDidDocumentResolver,
1110 CompositeHandleResolver,
1111+ DohJsonHandleResolver,
1212 LocalActorResolver,
1313 PlcDidDocumentResolver,
1414 WebDidDocumentResolver,
1515 WellKnownHandleResolver,
1616} from "@atcute/identity-resolver";
1717import { NodeDnsHandleResolver } from "@atcute/identity-resolver-node";
1818-import { OAuthClient, scope, type Store } from "@atcute/oauth-node-client";
1818+import {
1919+ type ClientAssertionPrivateJwk,
2020+ OAuthClient,
2121+ type OAuthClientOptions,
2222+ scope,
2323+ type Store,
2424+} from "@atcute/oauth-node-client";
1925import type { Cookies } from "@sveltejs/kit";
2026import { Buffer } from "node:buffer";
21272228class CookieStore<K extends string, V> implements Store<K, V> {
2329 #cookies: Cookies;
3030+ #secret: string;
2431 #prefix = OAUTH_COOKIE_PREFIX;
2532 #maxAge = SESSION_MAX_AGE;
26332727- constructor(event: { cookies: Cookies }, options?: { maxAge?: number }) {
3434+ constructor(
3535+ event: { cookies: Cookies; platform: App.Platform },
3636+ options?: { maxAge?: number },
3737+ ) {
2838 this.#cookies = event.cookies;
3939+ this.#secret = event.platform.env.PRIVATE_COOKIE_KEY;
2940 if (options?.maxAge) {
3041 this.#maxAge = options.maxAge;
3142 }
···4556 try {
4657 const value = await decryptText(
4758 cookieValue,
4848- env.PRIVATE_COOKIE_KEY,
5959+ this.#secret,
4960 );
5061 return JSON.parse(value);
5162 } catch {
···5768 const cookieName = this.cookieName(key);
5869 const cookieValue = await encryptText(
5970 JSON.stringify(value),
6060- env.PRIVATE_COOKIE_KEY,
7171+ this.#secret,
6172 );
6273 if (cookieValue.length > 4000) {
6374 throw new Error("too large");
···90101}
9110292103export function createOAuthClient(
9393- event: { cookies: Cookies; locals: App.Locals },
104104+ event: { cookies: Cookies; locals: App.Locals; platform?: App.Platform },
94105): OAuthClient {
106106+ if (event.platform === undefined) {
107107+ throw new Error();
108108+ }
95109 if (event.locals.oAuthClient) {
96110 return event.locals.oAuthClient;
97111 }
981129999- // [TODO] dynamic hostname/port
100100- const redirectUri = `http://127.0.0.1:5173/oauth/callback`;
113113+ const dns = dev ? new NodeDnsHandleResolver() : new DohJsonHandleResolver({
114114+ dohUrl: "https://mozilla.cloudflare-dns.com/dns-query",
115115+ });
116116+117117+ const redirect = new URL("/oauth/callback", event.platform.env.ORIGIN);
118118+ const scopes = [scope.rpc({ lxm: ["app.bsky.actor.getProfile"], aud: "*" })];
119119+ let metadata: OAuthClientOptions["metadata"];
120120+ let keyset: ClientAssertionPrivateJwk[] | undefined;
121121+122122+ if (dev) {
123123+ metadata = {
124124+ redirect_uris: [redirect.href],
125125+ scope: scopes,
126126+ };
127127+ } else {
128128+ metadata = {
129129+ client_id:
130130+ new URL("/oauth-client-metadata.json", event.platform.env.ORIGIN).href,
131131+ redirect_uris: [redirect.href],
132132+ jwks_uri:
133133+ new URL("/.well-known/jwks.json", event.platform.env.ORIGIN).href,
134134+ scope: scopes,
135135+ };
136136+ if (event.platform.env.PRIVATE_KEY_JWK) {
137137+ keyset = [
138138+ JSON.parse(
139139+ event.platform.env.PRIVATE_KEY_JWK,
140140+ ) as ClientAssertionPrivateJwk,
141141+ ];
142142+ } else throw new Error();
143143+ }
101144102145 const client = new OAuthClient({
103103- metadata: {
104104- redirect_uris: [redirectUri],
105105- scope: [scope.rpc({ lxm: ["app.bsky.actor.getProfile"], aud: "*" })],
106106- },
146146+ metadata,
147147+ keyset,
107148 actorResolver: new LocalActorResolver({
108149 handleResolver: new CompositeHandleResolver({
109150 methods: {
110110- dns: new NodeDnsHandleResolver(),
151151+ dns,
111152 http: new WellKnownHandleResolver(),
112153 },
113154 }),
+8-3
src/lib/server/session.ts
···11import { dev } from "$app/environment";
22-import { env } from "$env/dynamic/private";
32import {
43 HANDLE_COOKIE,
54 OAUTH_MAX_AGE,
···6564 * Setup OAuth client from cookies
6665 */
6766export const restoreSession = async (event: RequestEvent): Promise<void> => {
6868- const { cookies } = event;
6767+ const { cookies, platform } = event;
6868+ if (platform?.env === undefined) {
6969+ throw new Error();
7070+ }
6971 const encrypted = cookies.get(SESSION_COOKIE);
7072 if (encrypted === undefined) {
7173 return;
···7375 // Parse and validate or delete cookie
7476 let data: PublicUserData;
7577 try {
7676- const decrypted = await decryptText(encrypted, env.PRIVATE_COOKIE_KEY);
7878+ const decrypted = await decryptText(
7979+ encrypted,
8080+ platform?.env.PRIVATE_COOKIE_KEY,
8181+ );
7782 data = parsePublicUser(JSON.parse(decrypted));
7883 } catch {
7984 cookies.delete(SESSION_COOKIE, { path: "/" });