=> {
+ const { cookies } = event;
+ // Read the cookie
+ const encrypted = cookies.get("atproto_session");
+ if (encrypted === undefined) {
+ return;
+ }
+ // Parse and validate or delete
+ let data;
+ try {
+ const decrypted = await decryptText(encrypted, env.PRIVATE_COOKIE_KEY);
+ data = JSON.parse(decrypted);
+ } catch {
+ cookies.delete("atproto_session", { path: "/" });
+ return;
+ }
+ // [TODO] validate data type?
+ try {
+ if (
+ isDid(data.did) === false ||
+ isHandle(data.handle) === false
+ ) {
+ throw new Error();
+ }
+ const session = await oAuthClient.restore(data.did);
+ const client = new Client({ handler: session });
+ event.locals.user = {
+ ...data,
+ client,
+ session,
+ };
+ } catch {
+ cookies.delete("atproto_session", { path: "/" });
+ return;
+ }
+};
diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts
new file mode 100644
index 0000000..b3930e8
--- /dev/null
+++ b/src/routes/+layout.server.ts
@@ -0,0 +1,15 @@
+import type { LayoutServerLoad } from "./$types";
+
+export const load: LayoutServerLoad = (event) => {
+ let user = undefined;
+ if (event.locals.user) {
+ user = {
+ handle: event.locals.user.handle,
+ displayName: event.locals.user.displayName,
+ avatar: event.locals.user.avatar,
+ };
+ }
+ return {
+ user,
+ };
+};
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index ccc155c..2ccd9ab 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -1,11 +1,5 @@
-
-
-
-
{@render children()}
diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts
index aa1f94d..fff2eb6 100644
--- a/src/routes/+page.server.ts
+++ b/src/routes/+page.server.ts
@@ -1,14 +1,17 @@
import { type Actions, fail, redirect } from "@sveltejs/kit";
import { isActorIdentifier } from "@atcute/lexicons/syntax";
-// import crypto from "node:crypto";
import { oAuthClient } from "$lib/server/oauth.ts";
-// import { dev } from "$app/environment";
+import { destroySession } from "../lib/server/session.ts";
+import { dev } from "$app/environment";
export const actions = {
- login: async ({ request }) => {
+ logout: async (event) => {
+ await destroySession(event);
+ redirect(303, "/");
+ },
+ login: async ({ cookies, request }) => {
const formData = await request.formData();
const handle = formData.get("handle");
-
if (isActorIdentifier(handle) === false) {
return fail(400, { handle, invalid: true });
}
@@ -17,24 +20,19 @@ export const actions = {
"type": "account",
identifier: handle,
},
- // scope: [
- // "atproto",
- // ].join(" "),
});
- // [TODO] delete / handled by @atcute?
- // cookies.set(
- // "atproto_oauth_request",
- // crypto.createHash("sha256")
- // .update(stateId, "utf8")
- // .digest("hex"),
- // {
- // httpOnly: true,
- // maxAge: 60 * 5,
- // path: "/",
- // secure: !dev,
- // sameSite: "lax",
- // },
- // );
+ // [TODO] encrypt handle?
+ cookies.set(
+ "atproto_handle",
+ handle,
+ {
+ httpOnly: true,
+ maxAge: 60 * 5,
+ path: "/",
+ sameSite: "lax",
+ secure: !dev,
+ },
+ );
redirect(303, url);
},
} satisfies Actions;
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index e073d50..1a98d2a 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -5,12 +5,20 @@
let handle = $derived(form?.handle ?? "");
-
+{#if data.user}
+ Hello, {data.user.displayName}!
+
+{:else}
+
+{/if}
diff --git a/src/routes/oauth/callback/+server.ts b/src/routes/oauth/callback/+server.ts
index 3c4bc8e..4bbf236 100644
--- a/src/routes/oauth/callback/+server.ts
+++ b/src/routes/oauth/callback/+server.ts
@@ -1,24 +1,22 @@
+import type { RequestHandler } from "./$types";
+import type { OAuthSession } from "@atcute/oauth-node-client";
import { AppBskyActorGetProfile } from "@atcute/bluesky";
import { Client, ok } from "@atcute/client";
import { redirect } from "@sveltejs/kit";
import { oAuthClient } from "$lib/server/oauth.ts";
-import type { RequestHandler } from "./$types";
-import type { OAuthSession } from "@atcute/oauth-node-client";
+import { encryptText } from "$lib/server/crypto.ts";
+import { env } from "$env/dynamic/private";
+import { dev } from "$app/environment";
export const GET: RequestHandler = async (event) => {
const { url, cookies } = event;
// [TODO] delete / handled by @atcute?
- // const state = cookies.get("atproto_oauth_request");
- // if (state === undefined) {
- // return redirect(303, "/?error=expired");
- // }
- // cookies.delete(
- // "atproto_oauth_request",
- // { path: "/" },
- // );
-
- console.log(...url.searchParams);
+ const handle = cookies.get("atproto_handle");
+ if (handle === undefined) {
+ return redirect(303, "/?error=expired");
+ }
+ cookies.delete("atproto_handle", { path: "/" });
let session: OAuthSession;
try {
@@ -27,23 +25,51 @@ export const GET: RequestHandler = async (event) => {
console.error(err);
redirect(303, "/?error=session");
}
- console.log(session);
- const rpc = new Client({ handler: session });
- const profile = await ok(
- rpc.call(AppBskyActorGetProfile, {
- params: { actor: session.did },
- }),
- );
+ // [TODO] remember handle from login form
+ const data = {
+ handle,
+ did: session.did,
+ displayName: "",
+ avatar: "",
+ };
+
+ try {
+ const rpc = new Client({ handler: session });
+ const profile = await ok(
+ rpc.call(AppBskyActorGetProfile, {
+ params: { actor: session.did },
+ }),
+ );
+ // if (profile.handle) {
+ // data.handle = profile.handle;
+ // }
+ if (profile.displayName) {
+ data.displayName = profile.displayName;
+ }
+ if (profile.avatar) {
+ data.avatar = profile.avatar;
+ }
+ } catch {
+ // No Bluesky account?
+ }
- console.log(profile);
+ const encrypted = await encryptText(
+ JSON.stringify(data),
+ env.PRIVATE_COOKIE_KEY,
+ );
- /**
- * [TODO]
- * parse session params
- * encrypt session cookie
- * redirect to?
- */
+ cookies.set(
+ "atproto_session",
+ encrypted,
+ {
+ httpOnly: true,
+ maxAge: 60 * 60 * 24,
+ path: "/",
+ sameSite: "lax",
+ secure: !dev,
+ },
+ );
redirect(303, "/?success");
};
diff --git a/static/robots.txt b/static/robots.txt
index b6dd667..1f53798 100644
--- a/static/robots.txt
+++ b/static/robots.txt
@@ -1,3 +1,2 @@
-# allow crawling everything by default
User-agent: *
-Disallow:
+Disallow: /
diff --git a/svelte.config.js b/svelte.config.js
index 1c5c46a..b178593 100644
--- a/svelte.config.js
+++ b/svelte.config.js
@@ -10,6 +10,10 @@ const config = {
alias: {
$lib: "src/lib",
},
+ env: {
+ publicPrefix: "PUBLIC",
+ privatePrefix: "PRIVATE",
+ },
},
};