From 8cb01193e508825435d2946df57920402913143c Mon Sep 17 00:00:00 2001 From: Mart Aarma Date: Mon, 14 Sep 2026 13:19:35 +0300 Subject: [PATCH 1/5] Add guide for adding Web eID for Mobile support to an existing integration Add a short section to the library and example READMEs that lists only what an application with a working regular Web eID integration has to change or add for the mobile authentication flow, with links to the existing sections and example classes that implement each step. Signed-off-by: Mart Aarma Co-authored-by: Sander Kondratjev --- README.md | 16 ++++++++++++++-- example/README.md | 9 +++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9d16ea9f..0df96294 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ More information about the Web eID project is available on the project [website] # Quickstart -Complete the steps below to add support for secure authentication with eID cards to your Java web application back end. Instructions for the front end are available [here](https://github.com/web-eid/web-eid.js). +Complete the steps below to add support for secure authentication with eID cards to your Java web application back end. Instructions for the front end are available [here](https://github.com/web-eid/web-eid.js). If your application already uses Web eID and you only need to add the mobile flow, see [Adding Web eID for Mobile support to an existing integration](#adding-web-eid-for-mobile-support-to-an-existing-integration). A Java web application that uses Maven or Gradle to manage packages is needed for running this quickstart. Examples are for Maven, but they are straightforward to translate to Gradle. @@ -183,7 +183,7 @@ public final class WebEidChallengeNonceFilter extends OncePerRequestFilter { } ``` -The `WebEidMobileAuthInitFilter` handles `/auth/mobile/init` requests for authentication flows using **Web eID token format v1.1**. It generates a challenge nonce and returns a deep link URI that embeds both the challenge nonce and the authentication endpoint required for initiating the v1.1 flow. +The `WebEidMobileAuthInitFilter` handles `/auth/mobile/init` requests for mobile authentication. It generates a challenge nonce and returns a deep link URI containing the challenge nonce, the login page URL and an optional request for a signing certificate. The response uses token format `web-eid:1.1` when a signing certificate is requested, or `web-eid:1.0` for authentication only. See the full implementation [here](example/src/main/java/eu/webeid/example/security/WebEidMobileAuthInitFilter.java). ```java @@ -252,9 +252,21 @@ try { ... ``` +## Adding Web eID for Mobile support to an existing integration + +Reuse your existing challenge nonce generator and store, trusted CA configuration, token validation and authorization logic. The mobile flow adds an App Link/Universal Link that opens the RIA DigiDoc app and a login page that receives the response in its URL fragment and posts the token to your back end. Authentication-only responses use `web-eid:1.0`; requesting a signing certificate with `getSigningCertificate=true` requires a validator that supports `web-eid:1.1`. The validator configuration stays the same. + +1. Add `POST /auth/mobile/init` to generate and store a challenge nonce and return `authUri`: `https://mopp.ria.ee/auth#`, where the payload is Base64-encoded JSON containing `challenge`, `loginUri` and optionally `getSigningCertificate` ([init filter](example/src/main/java/eu/webeid/example/security/WebEidMobileAuthInitFilter.java)). Set `web-eid-mobile.base-request-uri` to `https://mopp.ria.ee` for the RIA DigiDoc app; the example defaults to the development scheme `web-eid-mobile://`. Configure `web-eid-mobile.request-signing-cert` as needed and enable `web-eid-mobile.enabled` if previously disabled. +2. Serve `GET /auth/mobile/login` at an HTTPS `loginUri` on the validator's configured `web-eid-auth-token.validation.local-origin`. Its script must decode the response, handle errors and post `authToken` to the existing `POST /auth/login` endpoint ([login page](example/src/main/resources/templates/webeid-login.html), [payload parser](example/src/main/resources/static/js/payload.js)). Retrieve and consume the session's unexpired challenge nonce, validate the token, apply your authorization checks and establish the authenticated session as before. +3. Use `Secure`, `HttpOnly`, `SameSite=Lax` for the pre-authentication session cookie so it accompanies the return from the app. Keep CSRF protection on POST endpoints and protect the login page against XSS. Callback fragments are untrusted input; the CSRF token alone does not authenticate them. See the architecture document's [security assumptions](https://github.com/web-eid/web-eid-for-mobile-architecture-doc/tree/web-eid-mobile#security-assumptions). +4. Add a mobile login button that calls the init endpoint and opens the returned `authUri` ([front end](example/src/main/resources/templates/index.html)). Keep the existing desktop login control. + +Optional mobile signing is demonstrated by [SigningController](example/src/main/java/eu/webeid/example/web/rest/SigningController.java) and [MobileSigningService](example/src/main/java/eu/webeid/example/service/MobileSigningService.java). A validated signing certificate returned during authentication can be stored with its supported algorithms to skip the separate certificate request. Check that the signing certificate belongs to the authenticated user before preparing the data to sign. + # Table of contents - [Quickstart](#quickstart) + - [Adding Web eID for Mobile support to an existing integration](#adding-web-eid-for-mobile-support-to-an-existing-integration) - [Introduction](#introduction) - [Authentication token format](#authentication-token-format) - [Authentication token validation](#authentication-token-validation) diff --git a/example/README.md b/example/README.md index c78bba33..abeaddb8 100644 --- a/example/README.md +++ b/example/README.md @@ -80,6 +80,7 @@ When the application has started, open the _ngrok_ HTTPS URL in your preferred w + [Overview of the source code](#overview-of-the-source-code) + [Configuration](#configuration) + [Integration with Web eID components](#integration-with-web-eid-components) + + [Adding Web eID for Mobile support to an existing integration](#adding-web-eid-for-mobile-support-to-an-existing-integration) + [Integration with DigiDoc4j components](#integration-with-digidoc4j-components) - [Using the Certificates' *Authority Information Access* (AIA) extension in DigiDoc4j](#using-the-certificates-authority-information-access-aia-extension-in-digidoc4j) - [Using DigiDoc4j in test mode with the `dev` profile](#using-digidoc4j-in-test-mode-with-the-dev-profile) @@ -158,6 +159,14 @@ Detailed overview of Java code changes required for integrating Web eID authenti A similar overview of JavaScript and HTML code changes required for authentication and digital signing with Web eID is available in the [web-eid.js library README](https://github.com/web-eid/web-eid.js/blob/main/README.md). The corresponding JavaScript and HTML code is in the `src/resources/{static,templates}` directories. +### Adding Web eID for Mobile support to an existing integration + +To add mobile authentication to an existing integration, follow the [library migration guide](../README.md#adding-web-eid-for-mobile-support-to-an-existing-integration). This example adds `POST /auth/mobile/init` through `WebEidMobileAuthInitFilter` and `GET /auth/mobile/login` through `WebEidLoginPageGeneratingFilter`, using `templates/webeid-login.html` and `static/js/payload.js`. Both login flows submit to `POST /auth/login` and share `WebEidAuthenticationProvider`. + +Configure `web-eid-mobile.base-request-uri`, `web-eid-mobile.request-signing-cert` and `web-eid-auth-token.validation.local-origin` in `application-{dev,prod}.yaml`. Set the base request URI to `https://mopp.ria.ee` for the RIA DigiDoc app; both profiles currently use `web-eid-mobile://` for development. `web-eid-mobile.enabled` selects the mobile-enabled security filter chain in `ApplicationConfiguration`. The mobile button is in `templates/index.html`; `SameSiteCookieConfiguration` sets `SameSite=Lax`, and `application.yaml` enables forwarded HTTPS detection behind a reverse proxy. Spring Security CSRF protection remains enabled. + +For optional mobile signing, see `SigningController`, `MobileSigningService` and `templates/webeid-callback.html`. `WebEidAuthentication` stores a signing certificate from the validated token when requested; `SigningService.prepareContainer()` checks its subject ID against the authenticated user's ID. + ### Integration with DigiDoc4j components Java code examples that show how to create and sign data containers that hold signed file objects and digital signatures is available in the [DigiDoc4j wiki](https://github.com/open-eid/digidoc4j/wiki/Examples-of-using-it). Further information and links to the API documentation is available in the project [README](https://github.com/open-eid/digidoc4j/blob/master/README.md). The corresponding Java code is in the `src/main/java/eu/webeid/example/{service,web/rest}` directories. From e7a40b2764a22c16e487a4c821e78968e3b334aa Mon Sep 17 00:00:00 2001 From: Mart Aarma Date: Tue, 22 Sep 2026 10:25:04 +0300 Subject: [PATCH 2/5] Fix deleted AuthTokenValidatorImpl reference Signed-off-by: Mart Aarma Co-authored-by: Sander Kondratjev --- .../eu/webeid/security/validator/AuthTokenValidatorImpl.java | 0 .../validator/certvalidators/SubjectCertificateValidator.java | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 src/main/java/eu/webeid/security/validator/AuthTokenValidatorImpl.java diff --git a/src/main/java/eu/webeid/security/validator/AuthTokenValidatorImpl.java b/src/main/java/eu/webeid/security/validator/AuthTokenValidatorImpl.java deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/eu/webeid/security/validator/certvalidators/SubjectCertificateValidator.java b/src/main/java/eu/webeid/security/validator/certvalidators/SubjectCertificateValidator.java index 08eccdf8..aed25aec 100644 --- a/src/main/java/eu/webeid/security/validator/certvalidators/SubjectCertificateValidator.java +++ b/src/main/java/eu/webeid/security/validator/certvalidators/SubjectCertificateValidator.java @@ -29,7 +29,7 @@ /** * Validators perform the actual user certificate validation actions. *

- * They are used by AuthTokenValidatorImpl and are not part of the public API. + * They are used by AuthTokenVersionValidatorFactory and are not part of the public API. */ @FunctionalInterface public interface SubjectCertificateValidator { From 4c20f0478d4d56a99a0cf7e4f5d2e106414c99e0 Mon Sep 17 00:00:00 2001 From: Mart Aarma Date: Tue, 22 Sep 2026 11:44:06 +0300 Subject: [PATCH 3/5] Fix base URL Signed-off-by: Mart Aarma Co-authored-by: Sander Kondratjev --- README.md | 2 +- example/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0df96294..2988d340 100644 --- a/README.md +++ b/README.md @@ -256,7 +256,7 @@ try { Reuse your existing challenge nonce generator and store, trusted CA configuration, token validation and authorization logic. The mobile flow adds an App Link/Universal Link that opens the RIA DigiDoc app and a login page that receives the response in its URL fragment and posts the token to your back end. Authentication-only responses use `web-eid:1.0`; requesting a signing certificate with `getSigningCertificate=true` requires a validator that supports `web-eid:1.1`. The validator configuration stays the same. -1. Add `POST /auth/mobile/init` to generate and store a challenge nonce and return `authUri`: `https://mopp.ria.ee/auth#`, where the payload is Base64-encoded JSON containing `challenge`, `loginUri` and optionally `getSigningCertificate` ([init filter](example/src/main/java/eu/webeid/example/security/WebEidMobileAuthInitFilter.java)). Set `web-eid-mobile.base-request-uri` to `https://mopp.ria.ee` for the RIA DigiDoc app; the example defaults to the development scheme `web-eid-mobile://`. Configure `web-eid-mobile.request-signing-cert` as needed and enable `web-eid-mobile.enabled` if previously disabled. +1. Add `POST /auth/mobile/init` to generate and store a challenge nonce and return `authUri`: `https://id.eesti.ee/auth#`, where the payload is Base64-encoded JSON containing `challenge`, `loginUri` and optionally `getSigningCertificate` ([init filter](example/src/main/java/eu/webeid/example/security/WebEidMobileAuthInitFilter.java)). Set `web-eid-mobile.base-request-uri` to `https://id.eesti.ee` for the RIA DigiDoc app; the example defaults to the development scheme `web-eid-mobile://`. Configure `web-eid-mobile.request-signing-cert` as needed and enable `web-eid-mobile.enabled` if previously disabled. 2. Serve `GET /auth/mobile/login` at an HTTPS `loginUri` on the validator's configured `web-eid-auth-token.validation.local-origin`. Its script must decode the response, handle errors and post `authToken` to the existing `POST /auth/login` endpoint ([login page](example/src/main/resources/templates/webeid-login.html), [payload parser](example/src/main/resources/static/js/payload.js)). Retrieve and consume the session's unexpired challenge nonce, validate the token, apply your authorization checks and establish the authenticated session as before. 3. Use `Secure`, `HttpOnly`, `SameSite=Lax` for the pre-authentication session cookie so it accompanies the return from the app. Keep CSRF protection on POST endpoints and protect the login page against XSS. Callback fragments are untrusted input; the CSRF token alone does not authenticate them. See the architecture document's [security assumptions](https://github.com/web-eid/web-eid-for-mobile-architecture-doc/tree/web-eid-mobile#security-assumptions). 4. Add a mobile login button that calls the init endpoint and opens the returned `authUri` ([front end](example/src/main/resources/templates/index.html)). Keep the existing desktop login control. diff --git a/example/README.md b/example/README.md index abeaddb8..865970be 100644 --- a/example/README.md +++ b/example/README.md @@ -163,7 +163,7 @@ A similar overview of JavaScript and HTML code changes required for authenticati To add mobile authentication to an existing integration, follow the [library migration guide](../README.md#adding-web-eid-for-mobile-support-to-an-existing-integration). This example adds `POST /auth/mobile/init` through `WebEidMobileAuthInitFilter` and `GET /auth/mobile/login` through `WebEidLoginPageGeneratingFilter`, using `templates/webeid-login.html` and `static/js/payload.js`. Both login flows submit to `POST /auth/login` and share `WebEidAuthenticationProvider`. -Configure `web-eid-mobile.base-request-uri`, `web-eid-mobile.request-signing-cert` and `web-eid-auth-token.validation.local-origin` in `application-{dev,prod}.yaml`. Set the base request URI to `https://mopp.ria.ee` for the RIA DigiDoc app; both profiles currently use `web-eid-mobile://` for development. `web-eid-mobile.enabled` selects the mobile-enabled security filter chain in `ApplicationConfiguration`. The mobile button is in `templates/index.html`; `SameSiteCookieConfiguration` sets `SameSite=Lax`, and `application.yaml` enables forwarded HTTPS detection behind a reverse proxy. Spring Security CSRF protection remains enabled. +Configure `web-eid-mobile.base-request-uri`, `web-eid-mobile.request-signing-cert` and `web-eid-auth-token.validation.local-origin` in `application-{dev,prod}.yaml`. Set the base request URI to `https://id.eesti.ee` for the RIA DigiDoc app; both profiles currently use `web-eid-mobile://` for development. `web-eid-mobile.enabled` selects the mobile-enabled security filter chain in `ApplicationConfiguration`. The mobile button is in `templates/index.html`; `SameSiteCookieConfiguration` sets `SameSite=Lax`, and `application.yaml` enables forwarded HTTPS detection behind a reverse proxy. Spring Security CSRF protection remains enabled. For optional mobile signing, see `SigningController`, `MobileSigningService` and `templates/webeid-callback.html`. `WebEidAuthentication` stores a signing certificate from the validated token when requested; `SigningService.prepareContainer()` checks its subject ID against the authenticated user's ID. From bf5eed91a96102dd298dd270f0c5a8544a6bc92f Mon Sep 17 00:00:00 2001 From: Mart Aarma Date: Tue, 22 Sep 2026 11:44:16 +0300 Subject: [PATCH 4/5] Fix test Signed-off-by: Mart Aarma Co-authored-by: Sander Kondratjev --- .../java/eu/webeid/example/WebApplicationTest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/example/src/test/java/eu/webeid/example/WebApplicationTest.java b/example/src/test/java/eu/webeid/example/WebApplicationTest.java index 8b2ed8ec..901aa3cf 100644 --- a/example/src/test/java/eu/webeid/example/WebApplicationTest.java +++ b/example/src/test/java/eu/webeid/example/WebApplicationTest.java @@ -29,6 +29,7 @@ import eu.webeid.security.authtoken.WebEidAuthToken; import eu.webeid.security.challenge.ChallengeNonce; import eu.webeid.security.util.DateAndTime; +import eu.webeid.security.validator.certvalidators.SubjectCertificateNotRevokedValidator; import mockit.Mock; import mockit.MockUp; import org.digidoc4j.impl.asic.AsicSignatureFinalizer; @@ -49,6 +50,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; +import java.security.cert.X509Certificate; import java.util.stream.Stream; import static eu.webeid.example.testutil.ObjectMother.VALID_AUTH_TOKEN; @@ -101,6 +103,15 @@ public void validateOcspResponse(XadesSignature xadesSignature) { } }; + new MockUp() { + @Mock + public void validateCertificateNotRevoked(X509Certificate subjectCertificate) { + // Do not call real OCSP service in tests. The test certificates have expired, so the + // validation clock is mocked to a date in the past, but OCSP responses are always + // current, which makes the OCSP response time and responder certificate checks fail. + } + }; + MockHttpSession session = new MockHttpSession(); session.setAttribute("challenge-nonce", new ChallengeNonce(ObjectMother.VALID_CHALLENGE_NONCE, DateAndTime.utcNow().plusMinutes(1))); From 64dfa3860846a7aeb4eacb643f8a0842d0bab10c Mon Sep 17 00:00:00 2001 From: Mart Aarma Date: Tue, 22 Sep 2026 11:47:37 +0300 Subject: [PATCH 5/5] Fix link Signed-off-by: Mart Aarma Co-authored-by: Sander Kondratjev --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2988d340..5d747c1f 100644 --- a/README.md +++ b/README.md @@ -258,7 +258,7 @@ Reuse your existing challenge nonce generator and store, trusted CA configuratio 1. Add `POST /auth/mobile/init` to generate and store a challenge nonce and return `authUri`: `https://id.eesti.ee/auth#`, where the payload is Base64-encoded JSON containing `challenge`, `loginUri` and optionally `getSigningCertificate` ([init filter](example/src/main/java/eu/webeid/example/security/WebEidMobileAuthInitFilter.java)). Set `web-eid-mobile.base-request-uri` to `https://id.eesti.ee` for the RIA DigiDoc app; the example defaults to the development scheme `web-eid-mobile://`. Configure `web-eid-mobile.request-signing-cert` as needed and enable `web-eid-mobile.enabled` if previously disabled. 2. Serve `GET /auth/mobile/login` at an HTTPS `loginUri` on the validator's configured `web-eid-auth-token.validation.local-origin`. Its script must decode the response, handle errors and post `authToken` to the existing `POST /auth/login` endpoint ([login page](example/src/main/resources/templates/webeid-login.html), [payload parser](example/src/main/resources/static/js/payload.js)). Retrieve and consume the session's unexpired challenge nonce, validate the token, apply your authorization checks and establish the authenticated session as before. -3. Use `Secure`, `HttpOnly`, `SameSite=Lax` for the pre-authentication session cookie so it accompanies the return from the app. Keep CSRF protection on POST endpoints and protect the login page against XSS. Callback fragments are untrusted input; the CSRF token alone does not authenticate them. See the architecture document's [security assumptions](https://github.com/web-eid/web-eid-for-mobile-architecture-doc/tree/web-eid-mobile#security-assumptions). +3. Use `Secure`, `HttpOnly`, `SameSite=Lax` for the pre-authentication session cookie so it accompanies the return from the app. Keep CSRF protection on POST endpoints and protect the login page against XSS. Callback fragments are untrusted input; the CSRF token alone does not authenticate them. See the architecture document's [security assumptions](https://github.com/web-eid/web-eid-for-mobile-architecture-doc#security-assumptions). 4. Add a mobile login button that calls the init endpoint and opens the returned `authUri` ([front end](example/src/main/resources/templates/index.html)). Keep the existing desktop login control. Optional mobile signing is demonstrated by [SigningController](example/src/main/java/eu/webeid/example/web/rest/SigningController.java) and [MobileSigningService](example/src/main/java/eu/webeid/example/service/MobileSigningService.java). A validated signing certificate returned during authentication can be stored with its supported algorithms to skip the separate certificate request. Check that the signing certificate belongs to the authenticated user before preparing the data to sign.