Skip to content

Commit

Permalink
fix(bankid): support new bankid implementation @ polisen.se
Browse files Browse the repository at this point in the history
  • Loading branch information
kalkih committed Mar 19, 2024
1 parent 10dd6e9 commit 2b82dff
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 63 deletions.
181 changes: 120 additions & 61 deletions src/services/bankIdService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,8 @@ import nodeFetch from "node-fetch";
import qrCode from "qrcode-terminal";

import makeFetchCookie from "fetch-cookie";
import cheerio from "cheerio";

enum BankIdStatus {
COMPLETED = 1,
SIGNING = 2,
NOT_STARTED = 3,
FINISHED = 4,
EXPIRED = 5,
EXCEPTION = 16,
CANCELED = 17,
}

interface BankIdState {
code: BankIdStatus;
description: string;
result: number;
}

interface BankIdAttempt {
code: number;
description: string;
qr_text: string;
result: number;
}

const BASE_URL = "https://legitimera.polisen.se";
const BASE_URL = "https://etjanster.polisen.se/login";

class BankIdService {
private sessionId: string;
Expand All @@ -39,59 +15,60 @@ class BankIdService {
}

private get loginUrl(): string {
return `${BASE_URL}/api/login?sessionid=${this.sessionId}`;
return `${BASE_URL}/request?tid=external-api&lid=${this.sessionId}`;
}

private get pollStatusUrl(): string {
return `${BASE_URL}/ajax-wpki.polling?sessionId=${this.sessionId}`;
private get bankIdUrl(): string {
return `${BASE_URL}/bankid?tid=external-api&lid=${this.sessionId}`;
}

private get newAttemptUrl(): string {
return `${BASE_URL}/ajax-wpki.new-attempt?sessionId=${this.sessionId}`;
private get pollStatusUrl(): string {
return `${BASE_URL}/bankid/api/v1/status`;
}

private get continueUrl(): string {
return `${BASE_URL}/continue.request?sessionId=${this.sessionId}`;
private get newAttemptUrl(): string {
return `${BASE_URL}/bankid/api/v1/init`;
}

public async identify(): Promise<string> {
const res = await this.fetch(this.loginUrl);
const $ = cheerio.load(await res.text());
const url = $("#a_wpki2").attr("href");
// fetch login/id page
await this.fetch(this.loginUrl);

await this.fetch(BASE_URL + url);
// select bank id option
await this.fetch(this.bankIdUrl);

await this.newAttempt();
return await this.pollStatus();
}

private async pollStatus(): Promise<string> {
let isSigningLogged = false;
const statusPromise: Promise<void> = new Promise((resolve) => {
const statusPromise: Promise<string> = new Promise((resolve) => {
const interval = setInterval(async () => {
const res = await this.fetch(this.pollStatusUrl);
const state: BankIdState = await res.json();
logger.debug(state);
const res = await this.fetch(this.pollStatusUrl, {
method: "POST",
});
const response: BankIdResponse = await res.json();
logger.debug(response);

if (state.code === BankIdStatus.SIGNING) {
if (response.userMessageShortCode === BankIdHintCode.RFA21) {
if (!isSigningLogged) {
logger.verbose("BankID signing in progress");
logger.verbose(HintCodeDescription[response.userMessageShortCode]);
isSigningLogged = true;
}
return;
} else {
isSigningLogged = false;
}

if (
[
BankIdStatus.EXPIRED,
BankIdStatus.EXCEPTION,
BankIdStatus.CANCELED,
].includes(state.code)
) {
if (state.code === BankIdStatus.CANCELED) {
logger.error("BankID identification canceled");
if (response.status === BankIdStatus.FAILED) {
if (response.userMessageShortCode) {
logger.error(
`BankID signing failed: ${
HintCodeDescription[response.userMessageShortCode] ??
"Timed out"
}`
);
} else {
logger.warn(
"BankID identification attempt expired, generating new attempt..."
Expand All @@ -100,26 +77,26 @@ class BankIdService {
return await this.newAttempt();
}

if (
[BankIdStatus.COMPLETED, BankIdStatus.FINISHED].includes(state.code)
) {
if (response.status === BankIdStatus.COMPLETE && response.completeUrl) {
logger.success("BankID identification completed");
clearInterval(interval);
return resolve();
return resolve(response.completeUrl);
}
}, 1000);
}, 2500);
});

await statusPromise;
const returnUrl = await statusPromise;

const res = await this.fetch(this.continueUrl);
const res = await this.fetch(returnUrl);
return res.url;
}

private async newAttempt(): Promise<void> {
const res = await this.fetch(this.newAttemptUrl);
const attempt: BankIdAttempt = await res.json();
const qr = attempt.qr_text;
const res = await this.fetch(this.newAttemptUrl, {
method: "POST",
});
const attempt: BankIdInitResponse = await res.json();
const qr = attempt.qrData;
logger.info("Requires BankID identification to proceed");
logger.info(
"Use the link or scan the QR code in the BankID app (valid for 30 seconds)"
Expand All @@ -130,4 +107,86 @@ class BankIdService {
}
}

interface BankIdResponse {
status: BankIdStatus;
qrData: string;
userMessageShortCode: BankIdHintCode | "";

completeUrl: string | null;
errorCode: string | null;
}

interface BankIdInitResponse
extends Pick<
BankIdResponse,
"errorCode" | "qrData" | "userMessageShortCode"
> {
autoStartToken: string;
}

enum BankIdStatus {
PENDING = "pending",
COMPLETE = "complete",
FAILED = "failed",
}

enum BankIdHintCode {
RFA1 = "RFA1",
RFA2 = "RFA2",
RFA3 = "RFA3",
RFA4 = "RFA4",
RFA5 = "RFA5",
RFA6 = "RFA6",
RFA8 = "RFA8",
RFA9 = "RFA9",
RFA13 = "RFA13",
RFA14A = "RFA14A",
RFA14B = "RFA14B",
RFA15A = "RFA15A",
RFA15B = "RFA15B",
RFA16 = "RFA16",
RFA17A = "RFA17A",
RPA17B = "RPA17B",
RFA18 = "RFA18",
RFA19 = "RFA19",
RFA20 = "RFA20",
RFA21 = "RFA21",
RFA22 = "RFA22",
RFA23 = "RFA23",
}

const HintCodeDescription: Record<BankIdHintCode, string> = {
RFA1: "Start your BankID app.",
RFA2: "The BankID app is not installed. Please contact your bank.",
RFA3: "Action cancelled. Please try again.",
RFA4: "An identification or signing for this personal number is already started. Please try again.",
RFA5: "Internal error. Please try again.",
RFA6: "Action cancelled.",
RFA8: "The BankID app is not responding. Please check that it’s started and that you have internet access. If you don’t have a valid BankID you can get one from your bank. Try again.",
RFA9: "Enter your security code in the BankID app and select Identify or Sign.",
RFA13: "Trying to start your BankID app.",
RFA14A:
"Searching for BankID, it may take a little while … If a few seconds have passed and still no BankID has been found, you probably don’t have a BankID which can be used for this identification/signing on this computer. If you have a BankID card, please insert it into your card reader. If you don’t have a BankID you can get one from your bank. If you have a BankID on another device you can start the BankID app on that device.",
RFA14B:
"Searching for BankID, it may take a little while … If a few seconds have passed and still no BankID has been found, you probably don’t have a BankID which can be used for this identification/signing on this device. If you don’t have a BankID you can get one from your bank. If you have a BankID on another device you can start the BankID app on that device.",
RFA15A:
"Searching for BankID:s, it may take a little while … If a few seconds have passed and still no BankID has been found, you probably don’t have a BankID which can be used for this identification/signing on this computer. If you have a BankID card, please insert it into your card reader. If you don’t have a BankID you can get one from your bank.",
RFA15B:
"Searching for BankID, it may take a little while … If a few seconds have passed and still no BankID has been found, you probably don’t have a BankID which can be used for this identification/signing on this device. If you don’t have a BankID you can get one from your bank.",
RFA16:
"The BankID you are trying to use is blocked or too old. Please use another BankID or get a new one from your bank.",
RFA17A:
"The BankID app couldn’t be found on your computer or mobile device. Please install it and get a BankID from your bank. Install the app from your app store or https://install.bankid.com.",
RPA17B:
"Failed to scan the QR code. Start the BankID app and scan the QR code. Check that the BankID app is up to date. If you don't have the BankID app, you need to install it and get a BankID from your bank. Install the app from your app store or https://install.bankid.com.",
RFA18: "Start the BankID app.",
RFA19:
"Would you like to identify yourself or sign with a BankID on this computer, or with a Mobile BankID?",
RFA20:
"Would you like to identify yourself or sign with a BankID on this device, or with a BankID on another device?",
RFA21: "Identification or signing in progress.",
RFA22: "Unknown error. Please try again.",
RFA23: "Process your machine-readable travel document using the BankID app.",
};

export { BankIdService };
2 changes: 1 addition & 1 deletion src/services/bookingService/existingBookingService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class ExistingBookingService extends BookingService {
NextButtonID6: "Omboka/Avboka",
});

const sessionId = initBookingResponse.url.split("?sessionid=")[1];
const sessionId = initBookingResponse.url.split("&lid=")[1];
const bankIdService = new BankIdService(sessionId);
const sessionUrl = await bankIdService.identify();

Expand Down
2 changes: 1 addition & 1 deletion src/services/bookingService/newBookingService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class NewBookingService extends BookingService {
StartNextButton: "Boka ny tid",
});

const sessionId = res.url.split("?sessionid=")[1];
const sessionId = res.url.split("&lid=")[1];
const bankIdService = new BankIdService(sessionId);
const sessionUrl = await bankIdService.identify();

Expand Down

0 comments on commit 2b82dff

Please sign in to comment.