@auth0/nextjs-auth0
Version:
Auth0 Next.js SDK
190 lines (189 loc) • 8.66 kB
JavaScript
import { IncomingMessage, ServerResponse } from "http";
import { Socket } from "net";
import { cookies } from "next/headers.js";
import { NextRequest, NextResponse } from "next/server.js";
import { afterEach, describe, expect, it, vi } from "vitest";
import { generateSecret } from "../../test/utils.js";
import { Auth0Client } from "../client.js";
import { encrypt, RequestCookies } from "../cookies.js";
import { appRouteHandlerFactory, pageRouteHandlerFactory } from "./with-api-auth-required.js";
describe("with-api-auth-required", () => {
describe("app router", () => {
vi.mock("next/headers.js", async (importActual) => {
const actual = await importActual();
return {
...actual,
cookies: vi.fn().mockImplementation(async () => {
return new RequestCookies(new Headers());
})
};
});
afterEach(() => {
vi.clearAllMocks();
});
it("should protect an API route", async () => {
const withApiAuthRequired = appRouteHandlerFactory(new Auth0Client({
domain: constants.domain,
clientId: constants.clientId,
clientSecret: constants.clientSecret,
appBaseUrl: constants.appBaseUrl,
secret: constants.secret
}));
const request = new NextRequest(new URL("/custom-profile", constants.appBaseUrl), {
method: "GET"
});
const handler = withApiAuthRequired(async () => NextResponse.json({ protected: true }));
const response = await handler(request, {});
expect(response.status).toBe(401);
});
it("allow access to an API route with a valid session", async () => {
const auth0Client = new Auth0Client({
domain: constants.domain,
clientId: constants.clientId,
clientSecret: constants.clientSecret,
appBaseUrl: constants.appBaseUrl,
secret: constants.secret
});
const withApiAuthRequired = appRouteHandlerFactory(auth0Client);
vi.mocked(cookies).mockImplementation(async () => {
const session = {
user: { sub: "user_123" },
tokenSet: {
idToken: "idt_123",
accessToken: "at_123",
refreshToken: "rt_123",
expiresAt: 123456
},
internal: {
sid: "auth0-sid",
createdAt: Math.floor(Date.now() / 1000)
}
};
const maxAge = 60 * 60; // 1 hour
const expiration = Math.floor(Date.now() / 1000 + maxAge);
const sessionCookie = await encrypt(session, constants.secret, expiration);
const headers = new Headers();
headers.append("cookie", `__session=${sessionCookie}`);
return new RequestCookies(headers);
});
const request = new NextRequest(new URL("/custom-profile", constants.appBaseUrl), {
method: "GET"
});
const handler = withApiAuthRequired(async () => NextResponse.json({ foo: "bar" }));
const response = await handler(request, {});
expect(response.status).toBe(200);
await expect(response.json()).resolves.toEqual({ foo: "bar" });
});
it("allow access to an API route that updates the cookies", async () => {
const auth0Client = new Auth0Client({
domain: constants.domain,
clientId: constants.clientId,
clientSecret: constants.clientSecret,
appBaseUrl: constants.appBaseUrl,
secret: constants.secret
});
const withApiAuthRequired = appRouteHandlerFactory(auth0Client);
vi.mocked(cookies).mockImplementation(async () => {
const session = {
user: { sub: "user_123" },
tokenSet: {
idToken: "idt_123",
accessToken: "at_123",
refreshToken: "rt_123",
expiresAt: 123456
},
internal: {
sid: "auth0-sid",
createdAt: Math.floor(Date.now() / 1000)
}
};
const maxAge = 60 * 60; // 1 hour
const expiration = Math.floor(Date.now() / 1000 + maxAge);
const sessionCookie = await encrypt(session, constants.secret, expiration);
const headers = new Headers();
headers.append("cookie", `__session=${sessionCookie}`);
return new RequestCookies(headers);
});
const request = new NextRequest(new URL("/custom-profile", constants.appBaseUrl), {
method: "GET"
});
const handler = withApiAuthRequired(async () => {
const res = NextResponse.json({ foo: "bar" });
res.cookies.set("foo", "bar");
res.headers.set("baz", "bar");
return res;
});
const response = (await handler(request, {}));
expect(response.status).toBe(200);
await expect(response.json()).resolves.toEqual({ foo: "bar" });
expect(response.cookies.get("foo")?.value).toBe("bar");
expect(response.headers.get("baz")).toBe("bar");
});
});
describe("pages router", () => {
it("should protect an API route", async () => {
const withApiAuthRequired = pageRouteHandlerFactory(new Auth0Client({
domain: constants.domain,
clientId: constants.clientId,
clientSecret: constants.clientSecret,
appBaseUrl: constants.appBaseUrl,
secret: constants.secret
}));
const request = Object.assign(new IncomingMessage(new Socket()), {});
const response = new ServerResponse(request);
response.status = vi.fn().mockReturnValue(response);
response.json = vi.fn();
const handler = withApiAuthRequired(async () => NextResponse.json({ protected: true }));
await handler(request, response);
expect(response.status).toHaveBeenCalledWith(401);
});
it("allow access to an API route with a valid session", async () => {
const withApiAuthRequired = pageRouteHandlerFactory(new Auth0Client({
domain: constants.domain,
clientId: constants.clientId,
clientSecret: constants.clientSecret,
appBaseUrl: constants.appBaseUrl,
secret: constants.secret
}));
const session = {
user: { sub: "user_123" },
tokenSet: {
idToken: "idt_123",
accessToken: "at_123",
refreshToken: "rt_123",
expiresAt: 123456
},
internal: {
sid: "auth0-sid",
createdAt: Math.floor(Date.now() / 1000)
}
};
const maxAge = 60 * 60; // 1 hour
const expiration = Math.floor(Date.now() / 1000 + maxAge);
const sessionCookie = await encrypt(session, constants.secret, expiration);
const request = Object.assign(new IncomingMessage(new Socket()), {
cookies: {
__session: sessionCookie
},
headers: {
cookie: `__session=${sessionCookie}`
}
});
const response = new ServerResponse(request);
response.status = vi.fn().mockReturnValue(response);
response.json = vi.fn();
const handler = withApiAuthRequired(async (_, res) => res.status(200).json({ foo: "bar" }));
await handler(request, response);
expect(response.status).toHaveBeenCalledWith(200);
expect(response.json).toHaveBeenCalledWith({ foo: "bar" });
});
});
});
const constants = {
domain: "example.us.auth0.com",
clientId: "client_123",
clientSecret: "client-secret",
appBaseUrl: "https://example.com",
sub: "user_123",
secret: await generateSecret(32)
};