Skip to content

Commit 8d766cd

Browse files
committed
1 parent 728f500 commit 8d766cd

File tree

11 files changed

+93
-112
lines changed

11 files changed

+93
-112
lines changed

.dockerignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
*
2+
!app
3+
!assets
4+
!components
5+
!config
6+
!environment.d.ts
7+
!lib
8+
!middleware.ts
9+
!next-env.d.ts
10+
!package*.json
11+
!public
12+
!styles
13+
!tsconfig.json

Dockerfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FROM node:24-bookworm
2+
3+
WORKDIR /app
4+
5+
EXPOSE 3000
6+
7+
COPY package*.json ./
8+
RUN npm ci
9+
10+
COPY . .
11+
RUN npm run build
12+
13+
CMD ["npm", "run", "start"]

app/api/auth/[...nextauth]/route.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// This is Auth.js 5, the successor to NextAuth 4
22

3-
import { isDevelopment } from "@arcjet/env";
4-
import ip from "@arcjet/ip";
5-
import { NextRequest, NextResponse } from "next/server";
3+
import { type NextRequest, NextResponse } from "next/server";
64
import arcjet, { detectBot, shield, slidingWindow } from "@/lib/arcjet";
75
import { handlers } from "@/lib/auth";
86

@@ -31,11 +29,7 @@ const aj = arcjet
3129

3230
// Protect the sensitive actions e.g. login, signup, etc with Arcjet
3331
const ajProtectedPOST = async (req: NextRequest) => {
34-
// Next.js 15 doesn't provide the IP address in the request object so we use
35-
// the Arcjet utility package to parse the headers and find it. If we're
36-
// running in development mode, we'll use a local IP address.
37-
const userIp = !isDevelopment(process.env) ? "127.0.0.1" : ip(req);
38-
const decision = await aj.protect(req, { fingerprint: userIp });
32+
const decision = await aj.protect(req);
3933

4034
if (decision.isDenied()) {
4135
if (decision.reason.isRateLimit()) {

app/attack/test/route.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { isDevelopment } from "@arcjet/env";
2-
import ip from "@arcjet/ip";
31
import { type NextRequest, NextResponse } from "next/server";
42
import arcjet, { shield } from "@/lib/arcjet";
53

@@ -16,13 +14,9 @@ const aj = arcjet.withRule(
1614
);
1715

1816
export async function GET(req: NextRequest) {
19-
// Next.js 15 doesn't provide the IP address in the request object so we use
20-
// the Arcjet utility package to parse the headers and find it. If we're
21-
// running in development mode, we'll use a local IP address.
22-
const userIp = isDevelopment(process.env) ? "127.0.0.1" : ip(req);
2317
// The protect method returns a decision object that contains information
2418
// about the request.
25-
const decision = await aj.protect(req, { fingerprint: userIp });
19+
const decision = await aj.protect(req);
2620

2721
console.log("Arcjet decision: ", decision);
2822

app/bots/test/route.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { isDevelopment } from "@arcjet/env";
2-
import ip from "@arcjet/ip";
31
import { type NextRequest, NextResponse } from "next/server";
42
import arcjet, { detectBot, fixedWindow } from "@/lib/arcjet";
53

@@ -26,13 +24,9 @@ const aj = arcjet
2624
);
2725

2826
export async function GET(req: NextRequest) {
29-
// Next.js 15 doesn't provide the IP address in the request object so we use
30-
// the Arcjet utility package to parse the headers and find it. If we're
31-
// running in development mode, we'll use a local IP address.
32-
const userIp = isDevelopment(process.env) ? "127.0.0.1" : ip(req);
3327
// The protect method returns a decision object that contains information
3428
// about the request.
35-
const decision = await aj.protect(req, { fingerprint: userIp });
29+
const decision = await aj.protect(req);
3630

3731
console.log("Arcjet decision: ", decision);
3832

app/rate-limiting/test/route.ts

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { setRateLimitHeaders } from "@arcjet/decorate";
2-
import { isDevelopment } from "@arcjet/env";
3-
import ip from "@arcjet/ip";
2+
import type { ArcjetDecision } from "@arcjet/next";
43
import { type NextRequest, NextResponse } from "next/server";
5-
import type { Session } from "next-auth";
64
import arcjet, { fixedWindow, shield } from "@/lib/arcjet";
75
import { auth } from "@/lib/auth";
86

@@ -18,47 +16,42 @@ const aj = arcjet.withRule(
1816
}),
1917
);
2018

21-
// Returns ad-hoc rules depending on whether the session is present. You could
22-
// inspect more details about the session to dynamically adjust the rate limit.
23-
function getClient(session: Session | null) {
24-
if (session?.user) {
25-
return aj.withRule(
26-
fixedWindow({
27-
mode: "LIVE",
28-
max: 5,
29-
window: "60s",
30-
}),
31-
);
32-
} else {
33-
return aj.withRule(
34-
fixedWindow({
35-
mode: "LIVE",
36-
max: 2,
37-
window: "60s",
38-
}),
39-
);
40-
}
41-
}
19+
// Define an augmented client for rate limiting users
20+
const ajForUser = aj.withRule(
21+
fixedWindow({
22+
// fingerprint requests by user ID
23+
characteristics: ["userId"],
24+
mode: "LIVE",
25+
max: 5,
26+
window: "60s",
27+
}),
28+
);
29+
30+
// Define an augmented client for rate limiting guests
31+
const ajForGuest = aj.withRule(
32+
fixedWindow({
33+
// fingerprint requests by ip address (default unless set globally)
34+
characteristics: ["ip.src"],
35+
mode: "LIVE",
36+
max: 2,
37+
window: "60s",
38+
}),
39+
);
4240

4341
export async function POST(req: NextRequest) {
4442
// Get the session
4543
const session = await auth();
4644

4745
console.log("Session: ", session);
4846

49-
// Next.js 15 doesn't provide the IP address in the request object so we use
50-
// the Arcjet utility package to parse the headers and find it. If we're
51-
// running in development mode, we'll use a local IP address.
52-
const userIp = isDevelopment(process.env) ? "127.0.0.1" : ip(req);
47+
let decision: ArcjetDecision;
5348

5449
// Use the user ID if the user is logged in, otherwise use the IP address
55-
const fingerprint = session?.user?.id ?? userIp;
56-
57-
// The protect method returns a decision object that contains information
58-
// about the request.
59-
const decision = await getClient(session).protect(req, {
60-
fingerprint,
61-
});
50+
if (session?.user?.id) {
51+
decision = await ajForUser.protect(req, { userId: session.user.id });
52+
} else {
53+
decision = await ajForGuest.protect(req);
54+
}
6255

6356
console.log("Arcjet decision: ", decision);
6457

app/sensitive-info/test/route.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { isDevelopment } from "@arcjet/env";
2-
import ip from "@arcjet/ip";
31
import { type NextRequest, NextResponse } from "next/server";
42
import { formSchema } from "@/app/sensitive-info/schema";
53
import arcjet, { sensitiveInfo, shield } from "@/lib/arcjet";
@@ -26,13 +24,9 @@ const aj = arcjet
2624
);
2725

2826
export async function POST(req: NextRequest) {
29-
// Next.js 15 doesn't provide the IP address in the request object so we use
30-
// the Arcjet utility package to parse the headers and find it. If we're
31-
// running in development mode, we'll use a local IP address.
32-
const userIp = isDevelopment(process.env) ? "127.0.0.1" : ip(req);
3327
// The protect method returns a decision object that contains information
3428
// about the request.
35-
const decision = await aj.protect(req, { fingerprint: userIp });
29+
const decision = await aj.protect(req);
3630

3731
console.log("Arcjet decision: ", decision);
3832

app/signup/test/route.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import { isDevelopment } from "@arcjet/env";
2-
import ip from "@arcjet/ip";
3-
import { NextRequest, NextResponse } from "next/server";
1+
import { type NextRequest, NextResponse } from "next/server";
42
import { formSchema } from "@/app/signup/schema";
53
import arcjet, { protectSignup, shield } from "@/lib/arcjet";
64

@@ -57,13 +55,9 @@ export async function POST(req: NextRequest) {
5755

5856
const { email } = data.data;
5957

60-
// Next.js 15 doesn't provide the IP address in the request object so we use
61-
// the Arcjet utility package to parse the headers and find it. If we're
62-
// running in development mode, we'll use a local IP address.
63-
const userIp = isDevelopment(process.env) ? "127.0.0.1" : ip(req);
6458
// The protect method returns a decision object that contains information
6559
// about the request.
66-
const decision = await aj.protect(req, { fingerprint: userIp, email });
60+
const decision = await aj.protect(req, { email });
6761

6862
console.log("Arcjet decision: ", decision);
6963

@@ -133,7 +127,7 @@ export async function POST(req: NextRequest) {
133127
}
134128
} else if (decision.isErrored()) {
135129
console.error("Arcjet error:", decision.reason);
136-
if (decision.reason.message == "[unauthenticated] invalid key") {
130+
if (decision.reason.message === "[unauthenticated] invalid key") {
137131
return NextResponse.json(
138132
{
139133
message:
@@ -143,7 +137,7 @@ export async function POST(req: NextRequest) {
143137
);
144138
} else {
145139
return NextResponse.json(
146-
{ message: "internal server error: " + decision.reason.message },
140+
{ message: `internal server error: ${decision.reason.message}` },
147141
{ status: 500 },
148142
);
149143
}

compose.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
services:
2+
nextjs:
3+
build: .
4+
command: npm run dev
5+
labels:
6+
- dev.orbstack.domains=nextjs.arcjet-examples.orb.local
7+
env_file:
8+
- .env.local
9+
ports:
10+
- 3000
11+
volumes:
12+
- .:/app
13+
- nextjs_node_modules:/app/node_modules
14+
15+
volumes:
16+
nextjs_node_modules:

lib/arcjet.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ export default arcjet({
2323
// and set it as an environment variable rather than hard coding.
2424
// See: https://nextjs.org/docs/app/building-your-application/configuring/environment-variables
2525
key: process.env.ARCJET_KEY,
26-
// We specify a custom fingerprint so we can dynamically build it within each
27-
// demo route.
28-
characteristics: ["fingerprint"],
2926
rules: [
3027
// You can include one or more rules base rules. We don't include any here
3128
// so they can be set on each sub-page for the demo.

0 commit comments

Comments
 (0)