diff --git a/src/lib/atproto/server/oauth.remote.ts b/src/lib/atproto/server/oauth.remote.ts index a3ca1cd..26bc9f7 100644 --- a/src/lib/atproto/server/oauth.remote.ts +++ b/src/lib/atproto/server/oauth.remote.ts @@ -51,6 +51,7 @@ export const oauthLogout = command(async () => { } cookies.delete('did', { path: '/' }); + cookies.delete('scope', { path: '/' }); return { ok: true }; }); diff --git a/src/lib/atproto/server/session.ts b/src/lib/atproto/server/session.ts index c0b27ae..8dfd03a 100644 --- a/src/lib/atproto/server/session.ts +++ b/src/lib/atproto/server/session.ts @@ -4,6 +4,7 @@ import type { Did } from '@atcute/lexicons'; import type { OAuthSession } from '@atcute/oauth-node-client'; import { createOAuthClient } from './oauth'; import { getSignedCookie } from './signed-cookie'; +import { scope } from '../metadata'; export type SessionLocals = { session: OAuthSession | null; @@ -26,6 +27,14 @@ export async function restoreSession( return { session: null, client: null, did: null }; } + // If permissions changed since login, invalidate the session + const savedScope = getSignedCookie(cookies, 'scope'); + if (savedScope !== null && savedScope !== scope) { + cookies.delete('did', { path: '/' }); + cookies.delete('scope', { path: '/' }); + return { session: null, client: null, did: null }; + } + try { const oauth = createOAuthClient(env); const session = await oauth.restore(did); @@ -38,6 +47,7 @@ export async function restoreSession( } catch (e) { console.error('Failed to restore session:', e); cookies.delete('did', { path: '/' }); + cookies.delete('scope', { path: '/' }); return { session: null, client: null, did: null }; } } diff --git a/src/routes/(oauth)/oauth/callback/+server.ts b/src/routes/(oauth)/oauth/callback/+server.ts index e2e64d6..ab50d36 100644 --- a/src/routes/(oauth)/oauth/callback/+server.ts +++ b/src/routes/(oauth)/oauth/callback/+server.ts @@ -1,6 +1,7 @@ import { redirect } from '@sveltejs/kit'; import { createOAuthClient } from '$lib/atproto/server/oauth'; import { setSignedCookie } from '$lib/atproto/server/signed-cookie'; +import { scope } from '$lib/atproto/metadata'; import { dev } from '$app/environment'; import type { RequestHandler } from './$types'; @@ -12,13 +13,16 @@ export const GET: RequestHandler = async ({ url, platform, cookies }) => { try { const { session } = await oauth.callback(url.searchParams); - setSignedCookie(cookies, 'did', session.did, { + const cookieOpts = { path: '/', httpOnly: true, secure: !dev, - sameSite: 'lax', + sameSite: 'lax' as const, maxAge: 60 * 60 * 24 * 180 // 180 days - }); + }; + + setSignedCookie(cookies, 'did', session.did, cookieOpts); + setSignedCookie(cookies, 'scope', scope, cookieOpts); } catch (e) { console.error('OAuth callback failed:', e); redirect(303, '/?error=auth_failed');