refactor: replace signIn callback with signIn event, adjust getUserByEmail in adapter to check provider (#1223)

* refactor: replace signIn callback with signIn event, adjust getUserByEmail in adapter to check provider

* test: adjusting tests for adapter and events

* docs: add comments for unknown auth provider

* fix: missing dayjs import
This commit is contained in:
Meier Lukas
2024-10-07 21:13:15 +02:00
committed by GitHub
parent 4d51e3b344
commit eb21628ee4
19 changed files with 521 additions and 423 deletions

View File

@@ -1,7 +1,5 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { cookies } from "next/headers";
import type { Adapter, AdapterUser } from "@auth/core/adapters";
import type { Account } from "next-auth";
import type { AdapterUser } from "@auth/core/adapters";
import type { JWT } from "next-auth/jwt";
import { describe, expect, test, vi } from "vitest";
@@ -9,7 +7,7 @@ import { groupMembers, groupPermissions, groups, users } from "@homarr/db/schema
import { createDb } from "@homarr/db/test";
import * as definitions from "@homarr/definitions";
import { createSessionCallback, createSignInCallback, getCurrentUserPermissionsAsync } from "../callbacks";
import { createSessionCallback, getCurrentUserPermissionsAsync } from "../callbacks";
// This one is placed here because it's used in multiple tests and needs to be the same reference
const setCookies = vi.fn();
@@ -141,151 +139,3 @@ describe("session callback", () => {
expect(result.user!.name).toEqual(user.name);
});
});
type AdapterSessionInput = Parameters<Exclude<Adapter["createSession"], undefined>>[0];
const createAdapter = () => {
const result = {
createSession: (input: AdapterSessionInput) => input,
};
vi.spyOn(result, "createSession");
return result;
};
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
type SessionExport = typeof import("../session");
const mockSessionToken = "e9ef3010-6981-4a81-b9d6-8495d09cf3b5";
const mockSessionExpiry = new Date("2023-07-01");
vi.mock("../env.mjs", () => {
return {
env: {
AUTH_SESSION_EXPIRY_TIME: 60 * 60 * 24 * 7,
},
};
});
vi.mock("../session", async (importOriginal) => {
const mod = await importOriginal<SessionExport>();
const generateSessionToken = (): typeof mockSessionToken => mockSessionToken;
const expireDateAfter = (_seconds: number) => mockSessionExpiry;
return {
...mod,
generateSessionToken,
expireDateAfter,
} satisfies SessionExport;
});
describe("createSignInCallback", () => {
test("should return true if not credentials request and set colorScheme & sessionToken cookie", async () => {
// Arrange
const isCredentialsRequest = false;
const db = await prepareDbForSigninAsync("1");
const signInCallback = createSignInCallback(createAdapter(), db, isCredentialsRequest);
// Act
const result = await signInCallback({
user: { id: "1", emailVerified: new Date("2023-01-13") },
account: {} as Account,
});
// Assert
expect(result).toBe(true);
});
test("should return false if no adapter.createSession", async () => {
// Arrange
const isCredentialsRequest = true;
const db = await prepareDbForSigninAsync("1");
const signInCallback = createSignInCallback(
// https://github.com/nextauthjs/next-auth/issues/6106
{ createSession: undefined } as unknown as Adapter,
db,
isCredentialsRequest,
);
// Act
const result = await signInCallback({
user: { id: "1", emailVerified: new Date("2023-01-13") },
account: {} as Account,
});
// Assert
expect(result).toBe(false);
});
test("should call adapter.createSession with correct input", async () => {
// Arrange
const adapter = createAdapter();
const isCredentialsRequest = true;
const db = await prepareDbForSigninAsync("1");
const signInCallback = createSignInCallback(adapter, db, isCredentialsRequest);
const user = { id: "1", emailVerified: new Date("2023-01-13") };
const account = {} as Account;
// Act
await signInCallback({ user, account });
// Assert
expect(adapter.createSession).toHaveBeenCalledWith({
sessionToken: mockSessionToken,
userId: user.id,
expires: mockSessionExpiry,
});
expect(cookies().set).toHaveBeenCalledWith("next-auth.session-token", mockSessionToken, {
path: "/",
expires: mockSessionExpiry,
httpOnly: true,
sameSite: "lax",
secure: true,
});
});
test("should set colorScheme from db as cookie", async () => {
// Arrange
const isCredentialsRequest = true;
const db = await prepareDbForSigninAsync("1");
const signInCallback = createSignInCallback(createAdapter(), db, isCredentialsRequest);
// Act
const result = await signInCallback({
user: { id: "1", emailVerified: new Date("2023-01-13") },
account: {} as Account,
});
// Assert
expect(result).toBe(true);
expect(cookies().set).toHaveBeenCalledWith(
"homarr-color-scheme",
"dark",
expect.objectContaining({
path: "/",
}),
);
});
test("should return false if user not found in db", async () => {
// Arrange
const isCredentialsRequest = true;
const db = await prepareDbForSigninAsync("other-id");
const signInCallback = createSignInCallback(createAdapter(), db, isCredentialsRequest);
// Act
const result = await signInCallback({
user: { id: "1", emailVerified: new Date("2023-01-13") },
account: {} as Account,
});
// Assert
expect(result).toBe(false);
});
});
const prepareDbForSigninAsync = async (userId: string) => {
const db = createDb();
await db.insert(users).values({
id: userId,
colorScheme: "dark",
});
return db;
};