I have an API client written in TypeScript (Next.js) that handles user login by sending credentials to a server endpoint (/Auth/login). The server is responsible for generating a JWT token (securityToken) and a refresh token (refreshToken) upon successful authentication. Here's the code I'm using:
import { NextResponse } from "next/server";
import type { LoginDto, LoginResponse } from "@/types/auth";
export async function POST(request: Request) {
try {
const body: LoginDto = await request.json();
console.log("Incoming request to /api/auth/login", body);
const response = await fetch("Swagger Url", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
const data: LoginResponse = await response.json();
console.log("Full login API response:", data);
if (!response.ok) {
return NextResponse.json(data, { status: response.status });
}
// We'll handle token storage on the client side
return NextResponse.json(data);
} catch (error) {
console.error("Error in POST /api/auth/login:", error);
return NextResponse.json(
{
status: false,
message: "An unexpected error occurred",
code: 500,
},
{ status: 500 },
);
}
}
The server's Swagger schema defines the LoginDto and LoginResponse as follows:
"LoginDto": {
"required": ["password", "username"],
"type": "object",
"properties": {
"username": {
"maxLength": 255,
"minLength": 1,
"type": "string",
"format": "email"
},
"password": {
"maxLength": 255,
"minLength": 1,
"type": "string"
}
}
},
"LoginResponse": {
"type": "object",
"properties": {
"status": { "type": "boolean" },
"message": { "type": "string", "nullable": true },
"code": { "type": "integer", "format": "int32" },
"securityToken": { "$ref": "#/components/schemas/Token" },
"signInResult": { "$ref": "#/components/schemas/SignInResultType" },
"isEmailConfirmed": { "type": "boolean" },
"refreshToken": { "$ref": "#/components/schemas/RefreshToken" }
}
}```
The token I receive from the website after the initial login works perfectly for other authenticated requests (e.g., GET requests), but it fails when used for the 2FA (Two-Factor Authentication) authorization. Specifically, when I make a 2FA request (/api/auth/two-factor-login) using the token obtained from the login response, I get a 401 Unauthorized error. However, the exact same process (login followed by 2FA) works flawlessly in Swagger and Postman
What I Tried: I successfully logged in via my website's /api/auth/login endpoint, receiving a valid securityToken that works for other authenticated requests (e.g., GET endpoints). However, when I use the same token for the 2FA request (/api/auth/two-factor-login), it fails with a 401 Unauthorized error. The exact same flow (login → 2FA) works perfectly in Swagger and Postman, confirming the backend is functioning correctly. I inspected the headers and cookies in browser developer tools and noticed that cookies like .AspNetCore.Identity.Application and Identity.TwoFactorUserId are handled automatically in Swagger/Postman but might not be set or passed correctly on the website.
What I Expected: I expected the securityToken to work seamlessly for the 2FA request, just like it does in Swagger and Postman, returning a success response with a new securityToken. Additionally, I expected cookies to be handled automatically, as they are in Swagger/Postman, and no cross-origin issues since the backend works fine with Swagger.