173 lines
5.3 KiB
TypeScript
173 lines
5.3 KiB
TypeScript
// __tests__/auth.test.ts
|
|
import * as client from "openid-client";
|
|
import {
|
|
clientConfig,
|
|
getClientConfig,
|
|
Auth,
|
|
Logout,
|
|
Callback,
|
|
} from "../../../src/features/User/oidcAuth";
|
|
import * as apiUtils from "../../../src/utils/apiUtils";
|
|
|
|
clientConfig.url = "https://example.com";
|
|
const localAdress = "https://local.exemple.com";
|
|
|
|
describe("OpenID Client Auth Module", () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
jest.spyOn(apiUtils, "getLocation").mockImplementation(() => localAdress);
|
|
});
|
|
|
|
describe("getClientConfig", () => {
|
|
it("should call discovery with clientConfig.url", async () => {
|
|
const discoveryMock = client.discovery as jest.Mock;
|
|
discoveryMock.mockResolvedValue("discoveredClient");
|
|
|
|
const result = await getClientConfig();
|
|
|
|
expect(discoveryMock).toHaveBeenCalledWith(
|
|
new URL(clientConfig.url),
|
|
clientConfig.client_id
|
|
);
|
|
expect(result).toBe("discoveredClient");
|
|
});
|
|
});
|
|
|
|
describe("Auth", () => {
|
|
it("should generate PKCE and build authorization URL with PKCE", async () => {
|
|
(client.randomPKCECodeVerifier as jest.Mock).mockReturnValue(
|
|
"verifier123"
|
|
);
|
|
(client.calculatePKCECodeChallenge as jest.Mock).mockResolvedValue(
|
|
"challenge123"
|
|
);
|
|
|
|
// Mock discovery returning an object with serverMetadata()
|
|
const discoveredClient = {
|
|
serverMetadata: jest.fn(() => ({
|
|
supportsPKCE: () => true,
|
|
})),
|
|
};
|
|
(client.discovery as jest.Mock).mockResolvedValue(discoveredClient);
|
|
|
|
(client.buildAuthorizationUrl as jest.Mock).mockReturnValue(
|
|
"https://auth.url"
|
|
);
|
|
|
|
const result = await Auth();
|
|
|
|
expect(client.randomPKCECodeVerifier).toHaveBeenCalled();
|
|
expect(client.calculatePKCECodeChallenge).toHaveBeenCalledWith(
|
|
"verifier123"
|
|
);
|
|
expect(client.buildAuthorizationUrl).toHaveBeenCalledWith(
|
|
discoveredClient,
|
|
expect.objectContaining({
|
|
code_challenge: "challenge123",
|
|
code_challenge_method: clientConfig.code_challenge_method,
|
|
redirect_uri: clientConfig.redirect_uri,
|
|
scope: clientConfig.scope,
|
|
})
|
|
);
|
|
expect(result).toEqual({
|
|
redirectTo: "https://auth.url",
|
|
code_verifier: "verifier123",
|
|
state: undefined,
|
|
});
|
|
});
|
|
|
|
it("should generate state when PKCE not supported", async () => {
|
|
(client.randomPKCECodeVerifier as jest.Mock).mockReturnValue(
|
|
"verifier123"
|
|
);
|
|
(client.calculatePKCECodeChallenge as jest.Mock).mockResolvedValue(
|
|
"challenge123"
|
|
);
|
|
|
|
const discoveredClient = {
|
|
serverMetadata: jest.fn(() => ({
|
|
supportsPKCE: () => false,
|
|
})),
|
|
};
|
|
(client.discovery as jest.Mock).mockResolvedValue(discoveredClient);
|
|
(client.randomState as jest.Mock).mockReturnValue("state123");
|
|
(client.buildAuthorizationUrl as jest.Mock).mockReturnValue(
|
|
"https://auth.url"
|
|
);
|
|
|
|
const result = await Auth();
|
|
|
|
expect(client.randomState).toHaveBeenCalled();
|
|
expect(result.state).toBe("state123");
|
|
expect(result.redirectTo).toBe("https://auth.url");
|
|
});
|
|
});
|
|
|
|
describe("Logout", () => {
|
|
it("should build end session URL", async () => {
|
|
const discoveredClient = {};
|
|
(client.discovery as jest.Mock).mockResolvedValue(discoveredClient);
|
|
(client.buildEndSessionUrl as jest.Mock).mockReturnValue(
|
|
"https://logout.url"
|
|
);
|
|
|
|
const result = await Logout();
|
|
|
|
expect(client.buildEndSessionUrl).toHaveBeenCalledWith(discoveredClient, {
|
|
post_logout_redirect_uri: clientConfig.post_logout_redirect_uri,
|
|
});
|
|
expect(result).toBe("https://logout.url");
|
|
});
|
|
});
|
|
|
|
describe("Callback", () => {
|
|
it("should perform authorization code grant and fetch user info", async () => {
|
|
const discoveredClient = {};
|
|
(client.discovery as jest.Mock).mockResolvedValue(discoveredClient);
|
|
|
|
const mockTokenSet = {
|
|
access_token: "access123",
|
|
claims: jest.fn(() => ({ sub: "user123" })),
|
|
};
|
|
(client.authorizationCodeGrant as jest.Mock).mockResolvedValue(
|
|
mockTokenSet
|
|
);
|
|
(client.fetchUserInfo as jest.Mock).mockResolvedValue({ name: "User" });
|
|
const result = await Callback("verifier123", "state123");
|
|
|
|
expect(client.authorizationCodeGrant).toHaveBeenCalledWith(
|
|
discoveredClient,
|
|
new URL(localAdress),
|
|
{ pkceCodeVerifier: "verifier123", expectedState: "state123" }
|
|
);
|
|
|
|
expect(client.fetchUserInfo).toHaveBeenCalledWith(
|
|
discoveredClient,
|
|
"access123",
|
|
"user123"
|
|
);
|
|
|
|
expect(result).toEqual({
|
|
tokenSet: mockTokenSet,
|
|
userinfo: { name: "User" },
|
|
});
|
|
});
|
|
|
|
it("should catch and log errors", async () => {
|
|
const error = new Error("fail");
|
|
(client.discovery as jest.Mock).mockResolvedValue({});
|
|
(client.authorizationCodeGrant as jest.Mock).mockRejectedValue(error);
|
|
const consoleErrorSpy = jest
|
|
.spyOn(console, "error")
|
|
.mockImplementation(() => {});
|
|
|
|
const result = await Callback("verifier", "state");
|
|
|
|
expect(consoleErrorSpy).toHaveBeenCalledWith("Token grant error:", error);
|
|
expect(result).toBeUndefined();
|
|
|
|
consoleErrorSpy.mockRestore();
|
|
});
|
|
});
|
|
});
|