fix(#410): align BetterAuth basePath and auth client with NestJS routing
BetterAuth defaulted basePath to /api/auth but NestJS controller routes
to /auth/* (no global prefix). The auth client also pointed at the web
frontend origin instead of the API server, and LoginButton used a
nonexistent GET /auth/signin/authentik endpoint.
- Set basePath: "/auth" in BetterAuth server config
- Point auth client baseURL to API_BASE_URL with matching basePath
- Add genericOAuthClient plugin to auth client
- Use signIn.oauth2({ providerId: "authentik" }) in LoginButton
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,20 +3,19 @@ import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { LoginButton } from "./LoginButton";
|
||||
|
||||
// Mock window.location
|
||||
const mockLocation = {
|
||||
href: "",
|
||||
assign: vi.fn(),
|
||||
};
|
||||
Object.defineProperty(window, "location", {
|
||||
value: mockLocation,
|
||||
writable: true,
|
||||
});
|
||||
const { mockOAuth2 } = vi.hoisted(() => ({
|
||||
mockOAuth2: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/auth-client", () => ({
|
||||
signIn: {
|
||||
oauth2: mockOAuth2,
|
||||
},
|
||||
}));
|
||||
|
||||
describe("LoginButton", (): void => {
|
||||
beforeEach((): void => {
|
||||
mockLocation.href = "";
|
||||
mockLocation.assign.mockClear();
|
||||
mockOAuth2.mockClear();
|
||||
});
|
||||
|
||||
it("should render sign in button", (): void => {
|
||||
@@ -25,14 +24,17 @@ describe("LoginButton", (): void => {
|
||||
expect(button).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should redirect to OIDC endpoint on click", async (): Promise<void> => {
|
||||
it("should initiate OAuth2 sign-in on click", async (): Promise<void> => {
|
||||
const user = userEvent.setup();
|
||||
render(<LoginButton />);
|
||||
|
||||
const button = screen.getByRole("button", { name: /sign in/i });
|
||||
await user.click(button);
|
||||
|
||||
expect(mockLocation.assign).toHaveBeenCalledWith("http://localhost:3001/auth/signin/authentik");
|
||||
expect(mockOAuth2).toHaveBeenCalledWith({
|
||||
providerId: "authentik",
|
||||
callbackURL: "/",
|
||||
});
|
||||
});
|
||||
|
||||
it("should have proper styling", (): void => {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@mosaic/ui";
|
||||
import { API_BASE_URL } from "@/lib/config";
|
||||
import { signIn } from "@/lib/auth-client";
|
||||
|
||||
export function LoginButton(): React.JSX.Element {
|
||||
const handleLogin = (): void => {
|
||||
// Redirect to the backend OIDC authentication endpoint
|
||||
// BetterAuth will handle the OIDC flow and redirect back to the callback
|
||||
window.location.assign(`${API_BASE_URL}/auth/signin/authentik`);
|
||||
// Use BetterAuth's genericOAuth client to initiate the OIDC flow.
|
||||
// This POSTs to /auth/sign-in/oauth2 and follows the returned redirect URL.
|
||||
void signIn.oauth2({ providerId: "authentik", callbackURL: "/" });
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -7,20 +7,16 @@
|
||||
* - Automatic token refresh
|
||||
*/
|
||||
import { createAuthClient } from "better-auth/react";
|
||||
// Note: Credentials plugin import removed - better-auth has built-in credentials support
|
||||
import { genericOAuthClient } from "better-auth/client/plugins";
|
||||
import { API_BASE_URL } from "./config";
|
||||
|
||||
/**
|
||||
* Auth client instance configured for Jarvis.
|
||||
* Auth client instance configured for Mosaic Stack.
|
||||
*/
|
||||
export const authClient = createAuthClient({
|
||||
// Base URL for auth API
|
||||
baseURL:
|
||||
typeof window !== "undefined"
|
||||
? window.location.origin
|
||||
: (process.env.BETTER_AUTH_URL ?? "http://localhost:3042"),
|
||||
|
||||
// Plugins can be added here when needed
|
||||
plugins: [],
|
||||
baseURL: API_BASE_URL,
|
||||
basePath: "/auth",
|
||||
plugins: [genericOAuthClient()],
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -36,12 +32,7 @@ export const { signIn, signOut, useSession, getSession } = authClient;
|
||||
* and the default BetterAuth client expects email.
|
||||
*/
|
||||
export async function signInWithCredentials(username: string, password: string): Promise<unknown> {
|
||||
const baseURL =
|
||||
typeof window !== "undefined"
|
||||
? window.location.origin
|
||||
: (process.env.BETTER_AUTH_URL ?? "http://localhost:3042");
|
||||
|
||||
const response = await fetch(`${baseURL}/api/auth/sign-in/credentials`, {
|
||||
const response = await fetch(`${API_BASE_URL}/auth/sign-in/credentials`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
|
||||
Reference in New Issue
Block a user