From 2436586ee3067f7f697ff25c668a7b659dab4c4c Mon Sep 17 00:00:00 2001 From: Elin Fokine Date: Tue, 18 Nov 2025 16:14:05 +0100 Subject: [PATCH 1/9] Changed retry logic for http calls in typescript code, so that: 1. Duplicate retires is not fired for the same failed request. 2. Calls to checkstatus is not retried if status failed is returned from the BankID API, since the retry will just result in another error in that case. --- .../Client/activelogin-main.ts | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/ActiveLogin.Authentication.BankId.AspNetCore/Client/activelogin-main.ts b/src/ActiveLogin.Authentication.BankId.AspNetCore/Client/activelogin-main.ts index 9353f876..47102f6c 100644 --- a/src/ActiveLogin.Authentication.BankId.AspNetCore/Client/activelogin-main.ts +++ b/src/ActiveLogin.Authentication.BankId.AspNetCore/Client/activelogin-main.ts @@ -123,7 +123,7 @@ function activeloginInit(configuration: IBankIdUiScriptConfiguration, initState: requestVerificationToken, { "returnUrl": returnUrl - }, fetchRetryCountDefault) + }, fetchRetryCountDefault, true) .then(data => { if (data.isAutoLaunch) { if (!data.checkStatus) { @@ -177,7 +177,7 @@ function activeloginInit(configuration: IBankIdUiScriptConfiguration, initState: "orderRef": orderRef, "returnUrl": returnUrl, "autoStartAttempts": autoStartAttempts - }, fetchRetryCountDefault) + }, fetchRetryCountDefault, false) .then(data => { if (data.retryLogin) { autoStartAttempts++; @@ -227,7 +227,7 @@ function activeloginInit(configuration: IBankIdUiScriptConfiguration, initState: requestVerificationToken, { "qrStartState": qrStartState - }, fetchRetryCountDefault) + }, fetchRetryCountDefault, false) .then(data => { if (!!data.qrCodeAsBase64) { qrLastRefreshTimestamp = new Date(); @@ -278,7 +278,13 @@ function activeloginInit(configuration: IBankIdUiScriptConfiguration, initState: // Helpers - function postJson(url: string, requestVerificationToken: string, data: any, retryCount: number = 0): Promise { + function postJson(url: string, requestVerificationToken: string, data: any, retryCount: number = 0, retryOnHttpError: boolean = true): Promise { + + const retry = () => { + return delay(fetchRetryDelayMs) + .then(() => postJson(url, requestVerificationToken, data, retryCount - 1, retryOnHttpError)); + }; + return fetch(url, { method: "POST", @@ -290,22 +296,10 @@ function activeloginInit(configuration: IBankIdUiScriptConfiguration, initState: credentials: 'include', body: JSON.stringify(data) }) - .catch(error => { - if (retryCount > 0) { - return delay(fetchRetryDelayMs).then(() => { - return postJson(url, requestVerificationToken, data, retryCount - 1); - }); - } - - throw error; - }) .then(response => { - if (!response.ok && retryCount > 0) { - return delay(fetchRetryDelayMs).then(() => { - return postJson(url, requestVerificationToken, data, retryCount - 1) - }); + if (!response.ok && retryOnHttpError && retryCount > 0) { + return retry(); } - return response; }) .then(response => { @@ -321,6 +315,15 @@ function activeloginInit(configuration: IBankIdUiScriptConfiguration, initState: throw Error(data.errorMessage); } return data; + }) + .catch(error => { + const isNetworkError = error instanceof TypeError; + + if (isNetworkError && retryCount > 0) { + return retry(); + } + + throw error; }); } From b4f8ff76bac884454a6701739c4983515400944d Mon Sep 17 00:00:00 2001 From: Elin Fokine Date: Tue, 18 Nov 2025 17:08:48 +0100 Subject: [PATCH 2/9] Change vmimage for mac-os build. --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 83d69be0..fa2e5133 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -23,7 +23,7 @@ stages: artifactName: 'nuget-windows' macOS: vmImage: 'macOS-latest' - artifactName: 'nuget-macos' + artifactName: 'macOS-13' Linux: vmImage: 'ubuntu-latest' artifactName: 'nuget-linux' @@ -100,7 +100,7 @@ stages: vmImage: 'windows-latest' artifactName: 'samples-windows' macOS: - vmImage: 'macOS-latest' + artifactName: 'macOS-13' artifactName: 'samples-macos' Linux: vmImage: 'ubuntu-latest' From 9cade765fdd2798aa4f01e6de2ebe3d542c9e57d Mon Sep 17 00:00:00 2001 From: Elin Fokine Date: Tue, 18 Nov 2025 17:24:48 +0100 Subject: [PATCH 3/9] Fix mac-os build. --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index fa2e5133..a51a5504 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -22,8 +22,8 @@ stages: vmImage: 'windows-latest' artifactName: 'nuget-windows' macOS: - vmImage: 'macOS-latest' - artifactName: 'macOS-13' + vmImage: 'macOS-13' + artifactName: 'nuget-macos' Linux: vmImage: 'ubuntu-latest' artifactName: 'nuget-linux' From ed5ab55f8a862e1ed4a8451d247ccc2c1600705e Mon Sep 17 00:00:00 2001 From: Elin Fokine Date: Tue, 18 Nov 2025 17:31:11 +0100 Subject: [PATCH 4/9] fix mac-os build --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a51a5504..086868ae 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -100,7 +100,7 @@ stages: vmImage: 'windows-latest' artifactName: 'samples-windows' macOS: - artifactName: 'macOS-13' + vmImage: 'macOS-13' artifactName: 'samples-macos' Linux: vmImage: 'ubuntu-latest' From bfebe47d10ef18c4af7562cd2c43e565c75472d2 Mon Sep 17 00:00:00 2001 From: Elin Fokine Date: Tue, 18 Nov 2025 17:41:08 +0100 Subject: [PATCH 5/9] change macos from latest to 13. --- .github/workflows/build.yml | 4 ++-- .github/workflows/test.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 527a2ca1..9588fc73 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: include: - os: windows-latest artifactName: activelogin-authentication-nuget-windows - - os: macos-latest + - os: macos-13 artifactName: activelogin-authentication-nuget-macos - os: ubuntu-latest artifactName: activelogin-authentication-nuget-ubuntu @@ -87,7 +87,7 @@ jobs: include: - os: windows-latest artifactName: activelogin-authentication-samples-windows - - os: macos-latest + - os: macos-13 artifactName: activelogin-authentication-samples-macos - os: ubuntu-latest artifactName: activelogin-authentication-samples-ubuntu diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 79c182f1..8c90b413 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,7 @@ jobs: matrix: include: - os: windows-latest - - os: macos-latest + - os: macos-13 - os: ubuntu-latest steps: From 7977bef4fbd07b47f050914ce99109d74c46f433 Mon Sep 17 00:00:00 2001 From: Elin Fokine Date: Wed, 19 Nov 2025 09:11:07 +0100 Subject: [PATCH 6/9] replace macos-13 with macos-15-intel --- .github/workflows/build.yml | 4 ++-- .github/workflows/test.yml | 2 +- azure-pipelines.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9588fc73..8faa3fcc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: include: - os: windows-latest artifactName: activelogin-authentication-nuget-windows - - os: macos-13 + - os: macos-15-intel artifactName: activelogin-authentication-nuget-macos - os: ubuntu-latest artifactName: activelogin-authentication-nuget-ubuntu @@ -87,7 +87,7 @@ jobs: include: - os: windows-latest artifactName: activelogin-authentication-samples-windows - - os: macos-13 + - os: macos-15-intel artifactName: activelogin-authentication-samples-macos - os: ubuntu-latest artifactName: activelogin-authentication-samples-ubuntu diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8c90b413..7e607b80 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,7 @@ jobs: matrix: include: - os: windows-latest - - os: macos-13 + - os: macos-15-intel - os: ubuntu-latest steps: diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 086868ae..a496255f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -22,7 +22,7 @@ stages: vmImage: 'windows-latest' artifactName: 'nuget-windows' macOS: - vmImage: 'macOS-13' + vmImage: 'macos-15-intel' artifactName: 'nuget-macos' Linux: vmImage: 'ubuntu-latest' @@ -100,7 +100,7 @@ stages: vmImage: 'windows-latest' artifactName: 'samples-windows' macOS: - vmImage: 'macOS-13' + vmImage: 'macos-15-intel' artifactName: 'samples-macos' Linux: vmImage: 'ubuntu-latest' From 5d456a25b359898208ee453e913374590637334a Mon Sep 17 00:00:00 2001 From: Elin Fokine Date: Wed, 19 Nov 2025 09:43:12 +0100 Subject: [PATCH 7/9] Remove macos build for Azure DevOps. Keep it on GitHub. --- azure-pipelines.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a496255f..e0123a26 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -21,9 +21,6 @@ stages: Windows: vmImage: 'windows-latest' artifactName: 'nuget-windows' - macOS: - vmImage: 'macos-15-intel' - artifactName: 'nuget-macos' Linux: vmImage: 'ubuntu-latest' artifactName: 'nuget-linux' @@ -99,9 +96,6 @@ stages: Windows: vmImage: 'windows-latest' artifactName: 'samples-windows' - macOS: - vmImage: 'macos-15-intel' - artifactName: 'samples-macos' Linux: vmImage: 'ubuntu-latest' artifactName: 'samples-linux' From 4b0c327498a214e04f12977a30d2988c615a9798 Mon Sep 17 00:00:00 2001 From: Elin Fokine Date: Wed, 19 Nov 2025 11:46:29 +0100 Subject: [PATCH 8/9] Fixed review comments. --- .../Client/activelogin-main.ts | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/ActiveLogin.Authentication.BankId.AspNetCore/Client/activelogin-main.ts b/src/ActiveLogin.Authentication.BankId.AspNetCore/Client/activelogin-main.ts index 47102f6c..0233ecb5 100644 --- a/src/ActiveLogin.Authentication.BankId.AspNetCore/Client/activelogin-main.ts +++ b/src/ActiveLogin.Authentication.BankId.AspNetCore/Client/activelogin-main.ts @@ -23,6 +23,13 @@ function activeloginInit(configuration: IBankIdUiScriptConfiguration, initState: const fetchRetryCountDefault: number = 3; const fetchRetryDelayMs: number = 1000; + const retryOnHttpErrors = { + initialize: true, + status: false, // A second call to the BankID collect endpoint is not supported after a status complete or failed is returned. + qr: false, // No Http calls made by this endpoint + cancel: true + }; + // Pre check const requiredFeatures = [window.fetch, window.sessionStorage]; @@ -123,7 +130,7 @@ function activeloginInit(configuration: IBankIdUiScriptConfiguration, initState: requestVerificationToken, { "returnUrl": returnUrl - }, fetchRetryCountDefault, true) + }, fetchRetryCountDefault, retryOnHttpErrors.initialize) .then(data => { if (data.isAutoLaunch) { if (!data.checkStatus) { @@ -177,7 +184,9 @@ function activeloginInit(configuration: IBankIdUiScriptConfiguration, initState: "orderRef": orderRef, "returnUrl": returnUrl, "autoStartAttempts": autoStartAttempts - }, fetchRetryCountDefault, false) + }, + fetchRetryCountDefault, + retryOnHttpErrors.status) .then(data => { if (data.retryLogin) { autoStartAttempts++; @@ -227,7 +236,7 @@ function activeloginInit(configuration: IBankIdUiScriptConfiguration, initState: requestVerificationToken, { "qrStartState": qrStartState - }, fetchRetryCountDefault, false) + }, fetchRetryCountDefault, retryOnHttpErrors.qr) .then(data => { if (!!data.qrCodeAsBase64) { qrLastRefreshTimestamp = new Date(); @@ -278,11 +287,11 @@ function activeloginInit(configuration: IBankIdUiScriptConfiguration, initState: // Helpers - function postJson(url: string, requestVerificationToken: string, data: any, retryCount: number = 0, retryOnHttpError: boolean = true): Promise { - + function postJson(url: string, requestVerificationToken: string, data: any, remaingRetryCount: number = 0, retryOnHttpError: boolean = true): Promise { + const retry = () => { return delay(fetchRetryDelayMs) - .then(() => postJson(url, requestVerificationToken, data, retryCount - 1, retryOnHttpError)); + .then(() => postJson(url, requestVerificationToken, data, remaingRetryCount - 1, retryOnHttpError)); }; return fetch(url, @@ -297,7 +306,7 @@ function activeloginInit(configuration: IBankIdUiScriptConfiguration, initState: body: JSON.stringify(data) }) .then(response => { - if (!response.ok && retryOnHttpError && retryCount > 0) { + if (!response.ok && retryOnHttpError && remaingRetryCount > 0) { return retry(); } return response; @@ -317,9 +326,19 @@ function activeloginInit(configuration: IBankIdUiScriptConfiguration, initState: return data; }) .catch(error => { + // A TypeError here means fetch failed before receiving any HTTP response + // (network down, DNS error, CORS block, connection aborted, etc.). + // These failures are transient and safe to retry. const isNetworkError = error instanceof TypeError; - if (isNetworkError && retryCount > 0) { + // Other error types should NOT be retried here: + // - HTTP-level failures (e.g., non-2xx status codes) are already handled in the + // previous `.then` block, where retry behavior is controlled by `retryOnHttpError`. + // - Any error thrown after that stage represents a *valid server response*, + // meaning the request reached the API and should not be repeated. + // Retrying such cases here could cause duplicated API calls or break BankID flows + // (e.g.collect/status), which do not allow repeated calls after a completed/failed state. + if (isNetworkError && remaingRetryCount > 0) { return retry(); } From ce0c8aedd708eaf760bb5879279241067a846b19 Mon Sep 17 00:00:00 2001 From: Elin Fokine Date: Wed, 19 Nov 2025 13:46:31 +0100 Subject: [PATCH 9/9] Allow retrires for qr code refresh. --- .../Client/activelogin-main.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ActiveLogin.Authentication.BankId.AspNetCore/Client/activelogin-main.ts b/src/ActiveLogin.Authentication.BankId.AspNetCore/Client/activelogin-main.ts index 0233ecb5..1439fd20 100644 --- a/src/ActiveLogin.Authentication.BankId.AspNetCore/Client/activelogin-main.ts +++ b/src/ActiveLogin.Authentication.BankId.AspNetCore/Client/activelogin-main.ts @@ -25,8 +25,8 @@ function activeloginInit(configuration: IBankIdUiScriptConfiguration, initState: const retryOnHttpErrors = { initialize: true, - status: false, // A second call to the BankID collect endpoint is not supported after a status complete or failed is returned. - qr: false, // No Http calls made by this endpoint + status: false, // A second call to the BankID collect endpoint is not supported after a status complete or failed is returned. + qr: true, cancel: true };