Skip to content

Commit

Permalink
update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
pilcrowonpaper committed Jan 23, 2024
1 parent f474864 commit 324fe75
Show file tree
Hide file tree
Showing 15 changed files with 71 additions and 8 deletions.
11 changes: 10 additions & 1 deletion docs/pages/guides/email-and-password/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,16 @@ app.post("/login", async (request: Request) => {
const user = await db.table("user").where("email", "=", email).get();

if (!user) {
// invalid email
// NOTE:
// Returning immediately allows malicious actors to figure out valid emails from response times,
// allowing them to only focus on guessing passwords in brute-force attacks.
// As a preventive measure, you may want to hash passwords even for invalid emails.
// However, valid emails can be already be revealed with the signup page
// and a similar timing issue can likely be found in password reset implementation.
// It will also be much more resource intensive.
// Since protecting against this is none-trivial,
// it is crucial your implementation is protected against brute-force attacks with login throttling etc.
// If emails/usernames are public, you may outright tell the user that the username is invalid.
return new Response("Invalid email or password", {
status: 400
});
Expand Down
3 changes: 2 additions & 1 deletion docs/pages/guides/validate-session-cookies/astro.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import { defineMiddleware } from "astro:middleware";
export const onRequest = defineMiddleware(async (context, next) => {
if (context.request.method !== "GET") {
const originHeader = request.headers.get("Origin");
const hostHeader = request.headers.get("Header");
// NOTE: You may need to use `X-Forwarded-Host` instead
const hostHeader = request.headers.get("Host");
if (!originHeader || !hostHeader || !verifyRequestOrigin(originHeader, [hostHeader])) {
return new Response(null, {
status: 403
Expand Down
1 change: 1 addition & 0 deletions docs/pages/guides/validate-session-cookies/elysia.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const app = new Elysia().derive(
// CSRF check
if (context.request.method !== "GET") {
const originHeader = context.request.headers.get("Origin");
// NOTE: You may need to use `X-Forwarded-Host` instead
const hostHeader = context.request.headers.get("Host");
if (!originHeader || !hostHeader || !verifyRequestOrigin(originHeader, [hostHeader])) {
return {
Expand Down
1 change: 1 addition & 0 deletions docs/pages/guides/validate-session-cookies/express.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ app.use((req, res, next) => {
return next();
}
const originHeader = req.headers.origin ?? null;
// NOTE: You may need to use `X-Forwarded-Host` instead
const hostHeader = req.headers.host ?? null;
if (!originHeader || !hostHeader || !verifyRequestOrigin(originHeader, [hostHeader])) {
return res.status(403).end();
Expand Down
1 change: 1 addition & 0 deletions docs/pages/guides/validate-session-cookies/hono.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ app.use("*", (c, next) => {
return next();
}
const originHeader = c.req.headers.get("Origin");
// NOTE: You may need to use `X-Forwarded-Host` instead
const hostHeader = c.req.headers.get("Host");
if (!originHeader || !hostHeader || !verifyRequestOrigin(originHeader, [hostHeader])) {
return c.body(null, 403);
Expand Down
5 changes: 3 additions & 2 deletions docs/pages/guides/validate-session-cookies/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,16 @@ This guide is also available for:
- [SolidStart](/guides/validate-session-cookies/solidstart)
- [SvelteKit](/guides/validate-session-cookies/sveltekit)

**CSRF protection must be implemented when using cookies and forms.** This can be easily done by comparing the `Origin` and `Host` header.
**CSRF protection must be implemented when using cookies and forms.** This can be easily done by comparing the `Origin` and `Host` header.

For non-GET requests, check the request origin. You can use `readSessionCookie()` to get the session cookie from a HTTP `Cookie` header, and validate it with `Lucia.validateSession()`. Make sure to delete the session cookie if it's invalid and create a new session cookie when the expiration gets extended, which is indicated by `Session.fresh`.

```ts
import { verifyRequestOrigin } from "lucia";

// only required in non-GET requests (POST, PUT, DELETE, PATCH, etc)
// Only required in non-GET requests (POST, PUT, DELETE, PATCH, etc)
const originHeader = request.headers.get("Origin");
// NOTE: You may need to use `X-Forwarded-Host` instead
const hostHeader = request.headers.get("Host");
if (!originHeader || !hostHeader || !verifyRequestOrigin(originHeader, [hostHeader])) {
return new Response(null, {
Expand Down
1 change: 1 addition & 0 deletions docs/pages/guides/validate-session-cookies/nextjs-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export async function middleware(request: NextRequest): Promise<NextResponse> {
return NextResponse.next();
}
const originHeader = request.headers.get("Origin");
// NOTE: You may need to use `X-Forwarded-Host` instead
const hostHeader = request.headers.get("Host");
if (!originHeader || !hostHeader || !verifyRequestOrigin(originHeader, [hostHeader])) {
return new NextResponse(null, {
Expand Down
1 change: 1 addition & 0 deletions docs/pages/guides/validate-session-cookies/nextjs-pages.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export async function middleware(request: NextRequest): Promise<NextResponse> {
return NextResponse.next();
}
const originHeader = request.headers.get("Origin");
// NOTE: You may need to use `X-Forwarded-Host` instead
const hostHeader = request.headers.get("Host");
if (!originHeader || !hostHeader || !verifyRequestOrigin(originHeader, [hostHeader])) {
return new NextResponse(null, {
Expand Down
1 change: 1 addition & 0 deletions docs/pages/guides/validate-session-cookies/nuxt.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type { Session, User } from "lucia";
export default defineEventHandler(async (event) => {
if (event.method !== "GET") {
const originHeader = getHeader(event, "Origin") ?? null;
// NOTE: You may need to use `X-Forwarded-Host` instead
const hostHeader = getHeader(event, "Host") ?? null;
if (!originHeader || !hostHeader || !verifyRequestOrigin(originHeader, [hostHeader])) {
return event.node.res.writeHead(403).end();
Expand Down
1 change: 1 addition & 0 deletions docs/pages/guides/validate-session-cookies/solidstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { lucia } from "./lib/auth";
export default defineEventHandler((event) => {
if (context.request.method !== "GET") {
const originHeader = getHeader(event, "Origin") ?? null;
// NOTE: You may need to use `X-Forwarded-Host` instead
const hostHeader = getHeader(event, "Host") ?? null;
if (!originHeader || !hostHeader || !verifyRequestOrigin(originHeader, [hostHeader])) {
return event.node.res.writeHead(403).end();
Expand Down
9 changes: 9 additions & 0 deletions docs/pages/tutorials/username-and-password/astro.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,15 @@ export async function POST(context: APIContext): Promise<Response> {
.where("username", "=", username.toLowerCase())
.get();
if (!existingUser) {
// NOTE:
// Returning immediately allows malicious actors to figure out valid usernames from response times,
// allowing them to only focus on guessing passwords in brute-force attacks.
// As a preventive measure, you may want to hash passwords even for invalid usernames.
// However, valid usernames can be already be revealed with the signup page among other methods.
// It will also be much more resource intensive.
// Since protecting against this is none-trivial,
// it is crucial your implementation is protected against brute-force attacks with login throttling etc.
// If usernames are public, you may outright tell the user that the username is invalid.
return new Response("Incorrect username or password", {
status: 400
});
Expand Down
9 changes: 9 additions & 0 deletions docs/pages/tutorials/username-and-password/nextjs-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,15 @@ async function login(_: any, formData: FormData): Promise<ActionResult> {
.where("username", "=", username.toLowerCase())
.get();
if (!existingUser) {
// NOTE:
// Returning immediately allows malicious actors to figure out valid usernames from response times,
// allowing them to only focus on guessing passwords in brute-force attacks.
// As a preventive measure, you may want to hash passwords even for invalid usernames.
// However, valid usernames can be already be revealed with the signup page among other methods.
// It will also be much more resource intensive.
// Since protecting against this is none-trivial,
// it is crucial your implementation is protected against brute-force attacks with login throttling etc.
// If usernames are public, you may outright tell the user that the username is invalid.
return {
error: "Incorrect username or password"
};
Expand Down
9 changes: 9 additions & 0 deletions docs/pages/tutorials/username-and-password/nextjs-pages.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
.where("username", "=", username.toLowerCase())
.get();
if (!existingUser) {
// NOTE:
// Returning immediately allows malicious actors to figure out valid usernames from response times,
// allowing them to only focus on guessing passwords in brute-force attacks.
// As a preventive measure, you may want to hash passwords even for invalid usernames.
// However, valid usernames can be already be revealed with the signup page among other methods.
// It will also be much more resource intensive.
// Since protecting against this is none-trivial,
// it is crucial your implementation is protected against brute-force attacks with login throttling etc.
// If usernames are public, you may outright tell the user that the username is invalid.
res.status(400).json({
error: "Incorrect username or password"
});
Expand Down
9 changes: 9 additions & 0 deletions docs/pages/tutorials/username-and-password/nuxt.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,15 @@ export default eventHandler(async (event) => {
.where("username", "=", username.toLowerCase())
.get();
if (!existingUser) {
// NOTE:
// Returning immediately allows malicious actors to figure out valid usernames from response times,
// allowing them to only focus on guessing passwords in brute-force attacks.
// As a preventive measure, you may want to hash passwords even for invalid usernames.
// However, valid usernames can be already be revealed with the signup page among other methods.
// It will also be much more resource intensive.
// Since protecting against this is none-trivial,
// it is crucial your implementation is protected against brute-force attacks with login throttling etc.
// If usernames are public, you may outright tell the user that the username is invalid.
throw createError({
message: "Incorrect username or password",
statusCode: 400
Expand Down
17 changes: 13 additions & 4 deletions docs/pages/tutorials/username-and-password/sveltekit.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export const actions: Actions = {
...sessionCookie.attributes
});

redirect(302, "/");
redirect(302, "/");
}
};
```
Expand Down Expand Up @@ -198,6 +198,15 @@ export const actions: Actions = {
.where("username", "=", username.toLowerCase())
.get();
if (!existingUser) {
// NOTE:
// Returning immediately allows malicious actors to figure out valid usernames from response times,
// allowing them to only focus on guessing passwords in brute-force attacks.
// As a preventive measure, you may want to hash passwords even for invalid usernames.
// However, valid usernames can be already be revealed with the signup page among other methods.
// It will also be much more resource intensive.
// Since protecting against this is none-trivial,
// it is crucial your implementation is protected against brute-force attacks with login throttling etc.
// If usernames are public, you may outright tell the user that the username is invalid.
return fail(400, {
message: "Incorrect username or password"
});
Expand All @@ -217,7 +226,7 @@ export const actions: Actions = {
...sessionCookie.attributes
});

redirect(302, "/");
redirect(302, "/");
}
};
```
Expand All @@ -231,7 +240,7 @@ You can validate requests by checking `locals.user`. The field `user.username` i
import type { PageServerLoad, Actions } from "./$types";

export const load: PageServerLoad = async (event) => {
if (!event.locals.user) redirect(302, "/login");
if (!event.locals.user) redirect(302, "/login");
return {
username: event.locals.user.username
};
Expand Down Expand Up @@ -264,7 +273,7 @@ export const actions: Actions = {
path: ".",
...sessionCookie.attributes
});
redirect(302, "/login");
redirect(302, "/login");
}
};
```
Expand Down

0 comments on commit 324fe75

Please sign in to comment.