fix(ancpi): GET login page before POST to establish form tokens

OpenAM requires an initial GET to set session cookies before the
credentials POST. Without it, POST returns 500 and only sets
AMAuthCookie (intermediate) instead of iPlanetDirectoryPro (final SSO).
Then navigate to ePay goto URL to establish JSESSIONID.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
AI Assistant
2026-03-23 01:06:42 +02:00
parent 887e3f423e
commit 0447908007
+39 -77
View File
@@ -128,11 +128,19 @@ export class EpayClient {
private async login(): Promise<void> {
// Full login URL with module + goto params (required by OpenAM)
const loginUrlFull = `${LOGIN_URL}?module=SelfRegistration&goto=${encodeURIComponent("http://epay.ancpi.ro:80/epay/LogIn.action")}`;
const gotoUrl = "http://epay.ancpi.ro:80/epay/LogIn.action";
const loginUrlFull = `${LOGIN_URL}?module=SelfRegistration&goto=${encodeURIComponent(gotoUrl)}`;
// Step 1: GET the login page first (sets initial cookies + form tokens)
await this.client.get(loginUrlFull, {
timeout: DEFAULT_TIMEOUT_MS,
maxRedirects: 5,
validateStatus: () => true,
});
// Step 2: POST credentials
const body = `IDToken1=${encodeURIComponent(this.username)}&IDToken2=${encodeURIComponent(this.password)}`;
// POST login — follow all redirects, let cookie jar capture everything
const response = await this.client.post(loginUrlFull, body, {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
@@ -146,112 +154,66 @@ export class EpayClient {
const finalUrl =
response.request?.res?.responseUrl ??
response.request?.responseURL ??
response.config?.url ??
"";
const html = typeof response.data === "string" ? response.data : "";
console.log(
`[epay] Login: status=${response.status}, finalUrl=${finalUrl.slice(0, 100)}`,
`[epay] Login POST: status=${response.status}, finalUrl=${finalUrl.slice(0, 120)}`,
);
// Auth failure: OpenAM returns login form again
// Auth failure check
if (
html.includes("Authentication Failed") ||
html.includes("Autentificare esuata") ||
(finalUrl.includes("/openam/UI/Login") &&
html.includes("IDToken1") &&
html.includes("IDToken2"))
html.includes("Autentificare esuata")
) {
throw new Error("ePay login failed (invalid credentials)");
}
// After redirect chain, navigate to ePay explicitly to ensure JSESSIONID
if (!finalUrl.includes("epay.ancpi.ro")) {
await this.client.get(`${BASE_URL}/LogIn.action`, {
timeout: DEFAULT_TIMEOUT_MS,
maxRedirects: 5,
validateStatus: () => true,
});
}
// Step 3: Navigate to ePay to establish JSESSIONID
// Try the goto URL (HTTP) and HTTPS variants
const epayUrls = [gotoUrl, `${BASE_URL}/LogIn.action`];
let loggedIn = false;
// Log all cookies for debugging
const domains = [
"https://epay.ancpi.ro",
"https://oassl.ancpi.ro",
"http://epay.ancpi.ro",
BASE_URL,
];
const allCookies: Array<{ domain: string; key: string; value: string }> = [];
for (const domain of domains) {
try {
const cookies = await this.jar.getCookies(domain);
for (const c of cookies) {
allCookies.push({
domain,
key: c.key,
value: c.value.slice(0, 15) + "...",
});
}
} catch {
// domain not applicable
}
}
console.log(
`[epay] Cookies after login (${allCookies.length}):`,
JSON.stringify(allCookies),
);
// OpenAM at ANCPI uses AMAuthCookie (not iPlanetDirectoryPro)
const SESSION_COOKIE_NAMES = [
"AMAuthCookie",
"iPlanetDirectoryPro",
"JSESSIONID",
];
const hasSession = allCookies.some((c) =>
SESSION_COOKIE_NAMES.includes(c.key),
);
if (!hasSession) {
throw new Error("ePay login failed (no session cookie)");
}
// Navigate to ePay to establish JSESSIONID
// CRITICAL: ePay's goto URL is HTTP (http://epay.ancpi.ro:80), not HTTPS.
// The AMAuthCookie must be sent to this exact URL for ePay to create a session.
const epayUrls = [
"http://epay.ancpi.ro:80/epay/LogIn.action",
"http://epay.ancpi.ro/epay/LogIn.action",
`${BASE_URL}/LogIn.action`,
];
let jsessionEstablished = false;
for (const epayUrl of epayUrls) {
try {
const epayResponse = await this.client.get(epayUrl, {
timeout: DEFAULT_TIMEOUT_MS,
maxRedirects: 5,
maxRedirects: 10,
validateStatus: () => true,
});
const epayHtml = String(epayResponse.data ?? "");
// Check if we got the logged-in page (has credit info or user menu)
if (
epayHtml.includes("credit") ||
epayHtml.includes("puncte de credit") ||
epayHtml.includes("LogOut") ||
epayHtml.includes("Istoric")
epayHtml.includes("Istoric Comenzi")
) {
jsessionEstablished = true;
loggedIn = true;
console.log(`[epay] Session established via ${epayUrl}`);
break;
}
} catch {
// Try next URL
// Try next
}
}
if (!jsessionEstablished) {
console.warn("[epay] Could not establish ePay session, but AMAuthCookie is set.");
if (!loggedIn) {
// Log cookies for debugging
const cookieKeys: string[] = [];
for (const domain of [
"https://epay.ancpi.ro",
"https://oassl.ancpi.ro",
"http://epay.ancpi.ro",
]) {
try {
const cookies = await this.jar.getCookies(domain);
for (const c of cookies) cookieKeys.push(`${c.key}@${domain}`);
} catch {
/* skip */
}
}
console.error(`[epay] Login failed. Cookies: ${cookieKeys.join(", ")}`);
throw new Error("ePay login failed (could not establish session)");
}
console.log("[epay] Login successful.");