From c8b41925633d72ffd7f56df66e3f736ac2f38cbc Mon Sep 17 00:00:00 2001 From: Manav Date: Thu, 24 Oct 2024 23:00:50 +1100 Subject: [PATCH 1/4] fix(docs): wrap async return types in Promise (#1720) --- pages/sessions/basic-api/drizzle-orm.md | 4 ++-- pages/sessions/basic-api/mysql.md | 4 ++-- pages/sessions/basic-api/postgresql.md | 2 +- pages/sessions/basic-api/prisma.md | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pages/sessions/basic-api/drizzle-orm.md b/pages/sessions/basic-api/drizzle-orm.md index da4484b73..aad2acb11 100644 --- a/pages/sessions/basic-api/drizzle-orm.md +++ b/pages/sessions/basic-api/drizzle-orm.md @@ -166,7 +166,7 @@ import { sha256 } from "@oslojs/crypto/sha2"; // ... -export async function createSession(token: string, userId: number): Session { +export async function createSession(token: string, userId: number): Promise { const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token))); const session: Session = { id: sessionId, @@ -253,7 +253,7 @@ export function generateSessionToken(): string { return token; } -export async function createSession(token: string, userId: number): Session { +export async function createSession(token: string, userId: number): Promise { const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token))); const session: Session = { id: sessionId, diff --git a/pages/sessions/basic-api/mysql.md b/pages/sessions/basic-api/mysql.md index 68c12ef15..597ac99d6 100644 --- a/pages/sessions/basic-api/mysql.md +++ b/pages/sessions/basic-api/mysql.md @@ -103,7 +103,7 @@ import { sha256 } from "@oslojs/crypto/sha2"; // ... -export async function createSession(token: string, userId: number): Session { +export async function createSession(token: string, userId: number): Promise { const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token))); const session: Session = { id: sessionId, @@ -195,7 +195,7 @@ export function generateSessionToken(): string { return token; } -export async function createSession(token: string, userId: number): Session { +export async function createSession(token: string, userId: number): Promise { const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token))); const session: Session = { id: sessionId, diff --git a/pages/sessions/basic-api/postgresql.md b/pages/sessions/basic-api/postgresql.md index 1d35bc2f8..c7325e74f 100644 --- a/pages/sessions/basic-api/postgresql.md +++ b/pages/sessions/basic-api/postgresql.md @@ -103,7 +103,7 @@ import { sha256 } from "@oslojs/crypto/sha2"; // ... -export async function createSession(token: string, userId: number): Session { +export async function createSession(token: string, userId: number): Promise { const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token))); const session: Session = { id: sessionId, diff --git a/pages/sessions/basic-api/prisma.md b/pages/sessions/basic-api/prisma.md index 94fd824f0..4056e7fc1 100644 --- a/pages/sessions/basic-api/prisma.md +++ b/pages/sessions/basic-api/prisma.md @@ -95,7 +95,7 @@ import { sha256 } from "@oslojs/crypto/sha2"; // ... -export async function createSession(token: string, userId: number): Session { +export async function createSession(token: string, userId: number): Promise { const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token))); const session: Session = { id: sessionId, @@ -186,7 +186,7 @@ export function generateSessionToken(): string { return token; } -export async function createSession(token: string, userId: number): Session { +export async function createSession(token: string, userId: number): Promise { const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token))); const session: Session = { id: sessionId, From 3bcdb1dde32eb0c7ae22117aac968348abb84446 Mon Sep 17 00:00:00 2001 From: azez <79533966+azezsan@users.noreply.github.com> Date: Thu, 24 Oct 2024 19:50:47 +0300 Subject: [PATCH 2/4] Update sveltekit.md undocumented import from arctic (#1723) --- pages/tutorials/google-oauth/sveltekit.md | 1 + 1 file changed, 1 insertion(+) diff --git a/pages/tutorials/google-oauth/sveltekit.md b/pages/tutorials/google-oauth/sveltekit.md index a7c6d931b..63637032e 100644 --- a/pages/tutorials/google-oauth/sveltekit.md +++ b/pages/tutorials/google-oauth/sveltekit.md @@ -110,6 +110,7 @@ Create an API route in `routes/login/google/callback/+server.ts` to handle the c // routes/login/google/callback/+server.ts import { generateSessionToken, createSession, setSessionTokenCookie } from "$lib/server/session"; import { google } from "$lib/server/oauth"; +import { decodeIdToken } from "arctic"; import type { RequestEvent } from "@sveltejs/kit"; import type { OAuth2Tokens } from "arctic"; From e0914d3625749e064a52855eb3a62237d6a9dd51 Mon Sep 17 00:00:00 2001 From: Eliza Milburn <61363431+ERmilburn02@users.noreply.github.com> Date: Mon, 28 Oct 2024 13:15:55 +0100 Subject: [PATCH 3/4] Update Next.js: Refactor to Asynchronous `cookies()` API (#1726) --- pages/sessions/cookies/nextjs.md | 17 ++++++++++++----- pages/tutorials/github-oauth/nextjs.md | 12 +++++++----- pages/tutorials/google-oauth/nextjs.md | 16 +++++++++------- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/pages/sessions/cookies/nextjs.md b/pages/sessions/cookies/nextjs.md index 3b6025533..a0aa2f7e0 100644 --- a/pages/sessions/cookies/nextjs.md +++ b/pages/sessions/cookies/nextjs.md @@ -62,8 +62,9 @@ import { cookies } from "next/headers"; // ... -export function setSessionTokenCookie(token: string, expiresAt: Date): void { - cookies().set("session", token, { +export async function setSessionTokenCookie(token: string, expiresAt: Date): Promise { + const cookieStore = await cookies(); + cookieStore.set("session", token, { httpOnly: true, sameSite: "lax", secure: process.env.NODE_ENV === "production", @@ -72,8 +73,9 @@ export function setSessionTokenCookie(token: string, expiresAt: Date): void { }); } -export function deleteSessionTokenCookie(): void { - cookies().set("session", "", { +export async function deleteSessionTokenCookie(): Promise { + const cookieStore = await cookies(); + cookieStore.set("session", "", { httpOnly: true, sameSite: "lax", secure: process.env.NODE_ENV === "production", @@ -83,6 +85,8 @@ export function deleteSessionTokenCookie(): void { } ``` +> Before Next.js 15, `cookies()` was synchronous. If you are using an older version, you should replace `await cookies()` with `cookies()`. You should also switch the function return type to `void`, and remove the `async` keyword. + Since we can't extend set cookies insides server components due to a limitation with React, we recommend continuously extending the cookie expiration inside middleware. However, this comes with its own issue. We can't detect if a new cookie was set inside server actions or route handlers from middleware. This becomes an issue if we need to assign a new session inside server actions (e.g. after updating the password) as the middleware cookie will override it. As such, we'll only extend the cookie expiration on GET requests. > While Lucia v3 recommended setup extended session cookie lifetime, it did not avoid the revalidation issue. @@ -154,7 +158,8 @@ import { cache } from "react"; // ... export const getCurrentSession = cache(async (): Promise => { - const token = cookies().get("session")?.value ?? null; + const cookieStore = await cookies(); + const token = cookieStore.get("session")?.value ?? null; if (token === null) { return { session: null, user: null }; } @@ -163,6 +168,8 @@ export const getCurrentSession = cache(async (): Promise On versions of Next.js below 15, replace `await cookies()` with `cookies()`. + This function can be used in server components, server actions, and route handlers (but importantly not middleware). ```ts diff --git a/pages/tutorials/github-oauth/nextjs.md b/pages/tutorials/github-oauth/nextjs.md index e2d7b18db..d49a0bd32 100644 --- a/pages/tutorials/github-oauth/nextjs.md +++ b/pages/tutorials/github-oauth/nextjs.md @@ -84,7 +84,8 @@ export async function GET(): Promise { const state = generateState(); const url = github.createAuthorizationURL(state, []); - cookies().set("github_oauth_state", state, { + const cookieStore = await cookies(); + cookieStore.set("github_oauth_state", state, { path: "/", secure: process.env.NODE_ENV === "production", httpOnly: true, @@ -117,7 +118,8 @@ export async function GET(request: Request): Promise { const url = new URL(request.url); const code = url.searchParams.get("code"); const state = url.searchParams.get("state"); - const storedState = cookies().get("github_oauth_state")?.value ?? null; + const cookieStore = await cookies(); + const storedState = cookieStore.get("github_oauth_state")?.value ?? null; if (code === null || state === null || storedState === null) { return new Response(null, { status: 400 @@ -153,7 +155,7 @@ export async function GET(request: Request): Promise { if (existingUser !== null) { const sessionToken = generateSessionToken(); const session = await createSession(sessionToken, existingUser.id); - setSessionTokenCookie(sessionToken, session.expiresAt); + await setSessionTokenCookie(sessionToken, session.expiresAt); return new Response(null, { status: 302, headers: { @@ -167,7 +169,7 @@ export async function GET(request: Request): Promise { const sessionToken = generateSessionToken(); const session = await createSession(sessionToken, user.id); - setSessionTokenCookie(sessionToken, session.expiresAt); + await setSessionTokenCookie(sessionToken, session.expiresAt); return new Response(null, { status: 302, headers: { @@ -221,7 +223,7 @@ async function logout(): Promise { } await invalidateSession(session.id); - deleteSessionTokenCookie(); + await deleteSessionTokenCookie(); return redirect("/login"); } diff --git a/pages/tutorials/google-oauth/nextjs.md b/pages/tutorials/google-oauth/nextjs.md index 0907263fc..0ed6cdcb6 100644 --- a/pages/tutorials/google-oauth/nextjs.md +++ b/pages/tutorials/google-oauth/nextjs.md @@ -83,14 +83,15 @@ export async function GET(): Promise { const codeVerifier = generateCodeVerifier(); const url = google.createAuthorizationURL(state, codeVerifier, ["openid", "profile"]); - cookies().set("google_oauth_state", state, { + const cookieStore = await cookies(); + cookieStore.set("google_oauth_state", state, { path: "/", httpOnly: true, secure: process.env.NODE_ENV === "production", maxAge: 60 * 10, // 10 minutes sameSite: "lax" }); - cookies().set("google_code_verifier", codeVerifier, { + cookieStore.set("google_code_verifier", codeVerifier, { path: "/", httpOnly: true, secure: process.env.NODE_ENV === "production", @@ -123,8 +124,9 @@ export async function GET(request: Request): Promise { const url = new URL(request.url); const code = url.searchParams.get("code"); const state = url.searchParams.get("state"); - const storedState = cookies().get("google_oauth_state")?.value ?? null; - const codeVerifier = cookies().get("google_code_verifier")?.value ?? null; + const cookieStore = await cookies(); + const storedState = cookieStore.get("google_oauth_state")?.value ?? null; + const codeVerifier = cookieStore.get("google_code_verifier")?.value ?? null; if (code === null || state === null || storedState === null || codeVerifier === null) { return new Response(null, { status: 400 @@ -155,7 +157,7 @@ export async function GET(request: Request): Promise { if (existingUser !== null) { const sessionToken = generateSessionToken(); const session = await createSession(sessionToken, existingUser.id); - setSessionTokenCookie(sessionToken, session.expiresAt); + await setSessionTokenCookie(sessionToken, session.expiresAt); return new Response(null, { status: 302, headers: { @@ -169,7 +171,7 @@ export async function GET(request: Request): Promise { const sessionToken = generateSessionToken(); const session = await createSession(sessionToken, user.id); - setSessionTokenCookie(sessionToken, session.expiresAt); + await setSessionTokenCookie(sessionToken, session.expiresAt); return new Response(null, { status: 302, headers: { @@ -223,7 +225,7 @@ async function logout(): Promise { } await invalidateSession(session.id); - deleteSessionTokenCookie(); + await deleteSessionTokenCookie(); return redirect("/login"); } From 86da6b9c6d5e49a09a4af623d59864e73b3a36ea Mon Sep 17 00:00:00 2001 From: Wasit Uzayer Faraizi <54891391+wasituf@users.noreply.github.com> Date: Wed, 30 Oct 2024 05:52:40 +0600 Subject: [PATCH 4/4] docs: typo in astro csrf protection codeblock (#1725) --- pages/sessions/cookies/astro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/sessions/cookies/astro.md b/pages/sessions/cookies/astro.md index ff9d9865b..5bbf8a5dd 100644 --- a/pages/sessions/cookies/astro.md +++ b/pages/sessions/cookies/astro.md @@ -15,7 +15,7 @@ CSRF protection is a must when using cookies. From Astro v5.0, basic CSRF protec export default defineConfig({ output: "server", security: { - checkOrigin: false + checkOrigin: true } }); ```