Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions apps/blog-next/app/api/login/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@ export async function POST(request: Request) {
})
}

const headers = new Headers()
for (const cookie of session.cookies) {
headers.append('set-cookie', cookie)
}

return Response.json(submission.success({
message: session.emailVerificationRequired
? 'Signed in. Verify your email address to continue.'
Expand All @@ -40,7 +35,5 @@ export async function POST(request: Request) {
? session.emailVerificationRoute ?? '/verify-email'
: '/admin',
user: session.user,
}), {
headers,
})
}))
}
8 changes: 1 addition & 7 deletions apps/blog-next/app/api/logout/route.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import { logout, user } from '@holo-js/auth'

export async function POST() {
const signedOut = await logout()
const headers = new Headers()
for (const cookie of signedOut.cookies) {
headers.append('set-cookie', cookie)
}
await logout()

return Response.json({
ok: true,
authenticated: false,
message: 'Signed out successfully.',
user: await user(),
}, {
headers,
})
}
6 changes: 0 additions & 6 deletions apps/blog-next/app/api/register/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@ export async function POST(request: Request) {
}

const session = await loginUsing(created)
const headers = new Headers()
for (const cookie of session.cookies) {
headers.append('set-cookie', cookie)
}

return Response.json(submission.success({
message: session.emailVerificationRequired
? 'Account created. Check your inbox to verify your email address.'
Expand All @@ -43,6 +38,5 @@ export async function POST(request: Request) {
user: session.user,
}, 201), {
status: 201,
headers,
})
}
21 changes: 21 additions & 0 deletions apps/blog-next/app/auth/github/callback/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { redirect } from 'next/navigation'
import auth from '@holo-js/auth'
import { callback } from '@holo-js/auth-social'

export function GET(request: Request): Promise<Response> {
return handleCallback(request)
}

async function handleCallback(request: Request): Promise<Response> {
const result = await callback('github', request)
if (!result.ok) {
return Response.json({
message: result.message,
}, {
status: result.status,
})
}

await auth.guard(result.guard).loginUsing(result.user)
redirect('/admin')
}
5 changes: 5 additions & 0 deletions apps/blog-next/app/auth/github/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { redirect } from '@holo-js/auth-social'

export function GET(request: Request): Promise<Response> {
return redirect('github', request)
}
21 changes: 21 additions & 0 deletions apps/blog-next/app/auth/google/callback/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { redirect } from 'next/navigation'
import auth from '@holo-js/auth'
import { callback } from '@holo-js/auth-social'

export function GET(request: Request): Promise<Response> {
return handleCallback(request)
}

async function handleCallback(request: Request): Promise<Response> {
const result = await callback('google', request)
if (!result.ok) {
return Response.json({
message: result.message,
}, {
status: result.status,
})
}

await auth.guard(result.guard).loginUsing(result.user)
redirect('/admin')
}
5 changes: 5 additions & 0 deletions apps/blog-next/app/auth/google/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { redirect } from '@holo-js/auth-social'

export function GET(request: Request): Promise<Response> {
return redirect('google', request)
}
5 changes: 5 additions & 0 deletions apps/blog-next/app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ export default function LoginPage() {
<p style={{ margin: 0, color: '#94a3b8' }}>Use your email address and password to access the admin area.</p>
</div>

<div style={{ display: 'grid', gap: '0.65rem' }}>
<a href="/auth/google" style={{ color: '#e5e7eb', textDecoration: 'none' }}>Continue with Google</a>
<a href="/auth/github" style={{ color: '#e5e7eb', textDecoration: 'none' }}>Continue with GitHub</a>
</div>

<form onSubmit={(event) => { event.preventDefault(); form.submit() }} style={{ display: 'grid', gap: '0.9rem' }}>
<label style={{ display: 'grid', gap: '0.35rem' }}>
<span>Email</span>
Expand Down
7 changes: 7 additions & 0 deletions apps/blog-nuxt/app/pages/login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ const form = useForm(loginForm, {
<p>Use your email address and password to access the admin area.</p>
</div>

<div class="social-links">
<a href="/auth/google">Continue with Google</a>
<a href="/auth/github">Continue with GitHub</a>
</div>

<form class="stack" @submit.prevent="form.submit()">
<label class="field">
<span>Email</span>
Expand Down Expand Up @@ -72,6 +77,8 @@ const form = useForm(loginForm, {
.copy p { margin: 0; color: #94a3b8; }
.stack, .field { display: grid; gap: 0.35rem; }
.stack { gap: 0.9rem; }
.social-links { display: grid; gap: 0.65rem; }
.social-links a { color: #e5e7eb; text-decoration: none; }
.remember, .links { display: flex; gap: 0.75rem; align-items: center; flex-wrap: wrap; }
.error { color: #fca5a5; }
.success { color: #86efac; }
Expand Down
2 changes: 0 additions & 2 deletions apps/blog-nuxt/server/api/login.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ export default defineEventHandler(async (event) => {
}
}

event.node.res.setHeader('set-cookie', [...session.cookies])

return submission.success({
message: session.emailVerificationRequired
? 'Signed in. Verify your email address to continue.'
Expand Down
5 changes: 2 additions & 3 deletions apps/blog-nuxt/server/api/logout.post.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { logout, user } from '@holo-js/auth'

export default defineEventHandler(async (event) => {
const signedOut = await logout()
event.node.res.setHeader('set-cookie', [...signedOut.cookies])
export default defineEventHandler(async () => {
await logout()

return {
ok: true,
Expand Down
1 change: 0 additions & 1 deletion apps/blog-nuxt/server/api/register.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export default defineEventHandler(async (event) => {
}

const session = await loginUsing(created)
event.node.res.setHeader('set-cookie', [...session.cookies])
setResponseStatus(event, 201)
return submission.success({
message: session.emailVerificationRequired
Expand Down
15 changes: 15 additions & 0 deletions apps/blog-nuxt/server/lib/request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { getHeaders, getRequestURL, type H3Event } from 'h3'

export function toWebRequest(event: H3Event): Request {
const headers = new Headers()
for (const [key, value] of Object.entries(getHeaders(event))) {
if (typeof value === 'string') {
headers.set(key, value)
}
}

return new Request(getRequestURL(event), {
method: event.method,
headers,
})
}
7 changes: 7 additions & 0 deletions apps/blog-nuxt/server/routes/auth/github.get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { redirect } from '@holo-js/auth-social'

import { toWebRequest } from '../../lib/request'

export default defineEventHandler((event) => {
return redirect('github', toWebRequest(event))
})
15 changes: 15 additions & 0 deletions apps/blog-nuxt/server/routes/auth/github/callback.get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import auth from '@holo-js/auth'
import { callback } from '@holo-js/auth-social'

import { toWebRequest } from '../../../lib/request'

export default defineEventHandler(async (event) => {
const result = await callback('github', toWebRequest(event))
if (!result.ok) {
setResponseStatus(event, result.status)
return { message: result.message }
}

await auth.guard(result.guard).loginUsing(result.user)
return sendRedirect(event, '/admin', 303)
})
7 changes: 7 additions & 0 deletions apps/blog-nuxt/server/routes/auth/google.get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { redirect } from '@holo-js/auth-social'

import { toWebRequest } from '../../lib/request'

export default defineEventHandler((event) => {
return redirect('google', toWebRequest(event))
})
15 changes: 15 additions & 0 deletions apps/blog-nuxt/server/routes/auth/google/callback.get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import auth from '@holo-js/auth'
import { callback } from '@holo-js/auth-social'

import { toWebRequest } from '../../../lib/request'

export default defineEventHandler(async (event) => {
const result = await callback('google', toWebRequest(event))
if (!result.ok) {
setResponseStatus(event, result.status)
return { message: result.message }
}

await auth.guard(result.guard).loginUsing(result.user)
return sendRedirect(event, '/admin', 303)
})
9 changes: 1 addition & 8 deletions apps/blog-sveltekit/src/routes/api/login/+server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@ export async function POST({ request }: { request: Request }) {
})
}

const headers = new Headers()
for (const cookie of session.cookies) {
headers.append('set-cookie', cookie)
}

return json(submission.success({
message: session.emailVerificationRequired
? 'Signed in. Verify your email address to continue.'
Expand All @@ -41,7 +36,5 @@ export async function POST({ request }: { request: Request }) {
? session.emailVerificationRoute ?? '/verify-email'
: '/admin',
user: session.user,
}), {
headers,
})
}))
}
8 changes: 1 addition & 7 deletions apps/blog-sveltekit/src/routes/api/logout/+server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,12 @@ import { json } from '@sveltejs/kit'
import { logout, user } from '@holo-js/auth'

export async function POST() {
const signedOut = await logout()
const headers = new Headers()
for (const cookie of signedOut.cookies) {
headers.append('set-cookie', cookie)
}
await logout()

return json({
ok: true,
authenticated: false,
message: 'Signed out successfully.',
user: await user(),
}, {
headers,
})
}
6 changes: 0 additions & 6 deletions apps/blog-sveltekit/src/routes/api/register/+server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@ export async function POST({ request }: { request: Request }) {
}

const session = await loginUsing(created)
const headers = new Headers()
for (const cookie of session.cookies) {
headers.append('set-cookie', cookie)
}

return json(submission.success({
message: session.emailVerificationRequired
? 'Account created. Check your inbox to verify your email address.'
Expand All @@ -44,6 +39,5 @@ export async function POST({ request }: { request: Request }) {
user: session.user,
}, 201), {
status: 201,
headers,
})
}
5 changes: 5 additions & 0 deletions apps/blog-sveltekit/src/routes/auth/github/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { redirect } from '@holo-js/auth-social'

export function GET({ request }: { request: Request }): Promise<Response> {
return redirect('github', request)
}
21 changes: 21 additions & 0 deletions apps/blog-sveltekit/src/routes/auth/github/callback/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { json, redirect } from '@sveltejs/kit'
import auth from '@holo-js/auth'
import { callback } from '@holo-js/auth-social'

export function GET({ request }: { request: Request }): Promise<Response> {
return handleCallback(request)
}

async function handleCallback(request: Request): Promise<Response> {
const result = await callback('github', request)
if (!result.ok) {
return json({
message: result.message,
}, {
status: result.status,
})
}

await auth.guard(result.guard).loginUsing(result.user)
throw redirect(303, '/admin')
}
5 changes: 5 additions & 0 deletions apps/blog-sveltekit/src/routes/auth/google/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { redirect } from '@holo-js/auth-social'

export function GET({ request }: { request: Request }): Promise<Response> {
return redirect('google', request)
}
21 changes: 21 additions & 0 deletions apps/blog-sveltekit/src/routes/auth/google/callback/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { json, redirect } from '@sveltejs/kit'
import auth from '@holo-js/auth'
import { callback } from '@holo-js/auth-social'

export function GET({ request }: { request: Request }): Promise<Response> {
return handleCallback(request)
}

async function handleCallback(request: Request): Promise<Response> {
const result = await callback('google', request)
if (!result.ok) {
return json({
message: result.message,
}, {
status: result.status,
})
}

await auth.guard(result.guard).loginUsing(result.user)
throw redirect(303, '/admin')
Comment on lines +19 to +20
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the SvelteKit blog app's hooks.server.ts for runWithSvelteKitRequestEvent coverage
fd -e ts 'hooks.server' apps/blog-sveltekit --exec cat {}

Repository: cobraprojects/holo-js

Length of output: 409


🏁 Script executed:

# Find the `@holo-js/auth` package implementation of guestOnly and authOnly
fd -type f -name '*.ts' | xargs rg -l 'export.*guestOnly|export.*authOnly' | head -20

Repository: cobraprojects/holo-js

Length of output: 488


🏁 Script executed:

# Verify the exact file path of the callback handler
find apps/blog-sveltekit -type f -name '+server.ts' | grep -i callback

Repository: cobraprojects/holo-js

Length of output: 191


🏁 Script executed:

# Check if there are any other references to the callback route or auth routes in hooks
rg 'auth/google|callback' apps/blog-sveltekit

Repository: cobraprojects/holo-js

Length of output: 753


🏁 Script executed:

# Read the guestOnly and authOnly implementations
cat packages/auth/src/sveltekit/server.ts

Repository: cobraprojects/holo-js

Length of output: 6098


🏁 Script executed:

# Find the adapter-sveltekit implementation
fd -type f -name '*.ts' -path '*/adapter-sveltekit/*' | head -20

Repository: cobraprojects/holo-js

Length of output: 236


🏁 Script executed:

# Search for appendResponseCookie implementation
rg -A 10 'appendResponseCookie' packages/auth/src/sveltekit

Repository: cobraprojects/holo-js

Length of output: 47


🏁 Script executed:

# Search for appendResponseCookie more broadly
rg 'appendResponseCookie' --max-count=50

Repository: cobraprojects/holo-js

Length of output: 2259


🏁 Script executed:

# Find all files in packages/auth
find packages/auth -type f -name '*.ts' | head -20

Repository: cobraprojects/holo-js

Length of output: 708


🏁 Script executed:

# Read the adapter-sveltekit implementation
cat packages/adapter-sveltekit/src/index.ts

Repository: cobraprojects/holo-js

Length of output: 5695


🏁 Script executed:

# Read the actual callback handler to confirm the flow
cat apps/blog-sveltekit/src/routes/auth/google/callback/+server.ts

Repository: cobraprojects/holo-js

Length of output: 636


🏁 Script executed:

# Verify the loginUsing flow by checking auth/src/runtime.ts
rg -A 20 'async function appendResponseCookies' packages/auth/src/runtime.ts

Repository: cobraprojects/holo-js

Length of output: 572


🏁 Script executed:

# Check if there's any other mechanism that might populate ALS for the callback route
rg 'runWithSvelteKitRequestEvent' apps/blog-sveltekit

Repository: cobraprojects/holo-js

Length of output: 47


Add callback routes to guestOnly guard in hooks.server.ts.

The /auth/google/callback and /auth/github/callback routes are not covered by the guestOnly or authOnly handlers in hooks.server.ts. This means runWithSvelteKitRequestEvent is never called for these routes, so the AsyncLocalStorage context is not established. When loginUsing() calls appendResponseCookies(), the session cookie call reaches appendResponseCookie in the adapter, which silently returns without setting the cookie because getSvelteKitRequestEventStore().getStore() is undefined. The session is created server-side but the cookie is never sent to the client—the user is redirected to /admin but arrives unauthenticated.

Add both callback routes to the guestOnly routes list (or wrap the handler with runWithSvelteKitRequestEvent if they should be auth-required):

guestOnly({
  routes: ['/login', '/register', '/forgot-password', '/reset-password', '/auth/google/callback', '/auth/github/callback'],
  redirectTo: '/admin',
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/blog-sveltekit/src/routes/auth/google/callback/`+server.ts around lines
19 - 20, The guest-only guard is not covering the OAuth callback routes so
runWithSvelteKitRequestEvent is never invoked and AsyncLocalStorage isn’t
established; update the guestOnly configuration in hooks.server.ts to include
'/auth/google/callback' and '/auth/github/callback' in the routes array (e.g.,
add those strings to the routes passed to guestOnly) or alternatively ensure the
callback handler (the function that calls auth.guard(...).loginUsing and
redirect) is wrapped with runWithSvelteKitRequestEvent so appendResponseCookies
runs with a valid SvelteKit request event store.

}
7 changes: 7 additions & 0 deletions apps/blog-sveltekit/src/routes/login/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<p>Use your email address and password to access the admin area.</p>
</div>

<div class="social-links">
<a href="/auth/google">Continue with Google</a>
<a href="/auth/github">Continue with GitHub</a>
</div>

<form class="stack" on:submit={(event) => { event.preventDefault(); form.submit() }}>
<label class="field">
<span>Email</span>
Expand Down Expand Up @@ -89,6 +94,8 @@
.copy p { margin: 0; color: #94a3b8; }
.stack, .field { display: grid; gap: 0.35rem; }
.stack { gap: 0.9rem; }
.social-links { display: grid; gap: 0.65rem; }
.social-links a { color: #e5e7eb; text-decoration: none; }
.remember, .links { display: flex; gap: 0.75rem; align-items: center; flex-wrap: wrap; }
.error { color: #fca5a5; }
.success { color: #86efac; }
Expand Down
Loading