diff --git a/app/components/Header/AuthModal.client.vue b/app/components/Header/AuthModal.client.vue index 151921e74..2cb8a3c1e 100644 --- a/app/components/Header/AuthModal.client.vue +++ b/app/components/Header/AuthModal.client.vue @@ -3,15 +3,27 @@ import { useAtproto } from '~/composables/atproto/useAtproto' import { authRedirect } from '~/utils/atproto/helpers' const handleInput = shallowRef('') - +const route = useRoute() const { user, logout } = useAtproto() async function handleBlueskySignIn() { - await authRedirect('https://bsky.social') + await navigateTo( + { + path: '/api/auth/atproto', + query: { handle: 'https://bsky.social', returnTo: route.fullPath }, + }, + { external: true }, + ) } async function handleCreateAccount() { - await authRedirect('https://npmx.social', true) + await navigateTo( + { + path: '/api/auth/atproto', + query: { handle: 'https://npmx.social', create: 'true', returnTo: route.fullPath }, + }, + { external: true }, + ) } async function handleLogin() { diff --git a/server/api/auth/atproto.get.ts b/server/api/auth/atproto.get.ts index 581c91294..fbcfdd5c1 100644 --- a/server/api/auth/atproto.get.ts +++ b/server/api/auth/atproto.get.ts @@ -53,6 +53,19 @@ export default defineEventHandler(async event => { } const query = getQuery(event) + const rawReturnTo = query.returnTo?.toString() || '/' + // Validate returnTo is a safe relative path (prevent open redirect) + const isRelativePath = + rawReturnTo.startsWith('/') && !rawReturnTo.startsWith('//') && !rawReturnTo.includes(':') + const returnTo = isRelativePath ? rawReturnTo : '/' + + setCookie(event, 'auth_return_to', returnTo, { + maxAge: 60 * 5, + httpOnly: true, + // secure only if NOT in dev mode + secure: !import.meta.dev, + }) + const clientMetadata = getOauthClientMetadata() const session = await useServerSession(event) const { stateStore, sessionStore } = useOAuthStorage(session) @@ -126,5 +139,9 @@ export default defineEventHandler(async event => { }, }) } - return sendRedirect(event, '/') + + const returnToURL = getCookie(event, 'auth_return_to') || '/' + deleteCookie(event, 'auth_return_to') + + return sendRedirect(event, returnToURL) })