Skip to content

NFC-99 Web eID for mobile support#64

Open
SanderKondratjevNortal wants to merge 5 commits intoweb-eid-mobilefrom
NFC-99
Open

NFC-99 Web eID for mobile support#64
SanderKondratjevNortal wants to merge 5 commits intoweb-eid-mobilefrom
NFC-99

Conversation

@SanderKondratjevNortal
Copy link
Copy Markdown

Signed-off-by: Sander Kondratjev sander.kondratjev@nortal.com

@SanderKondratjevNortal SanderKondratjevNortal changed the base branch from main to web-eid-mobile November 27, 2025 11:05
@SanderKondratjevNortal SanderKondratjevNortal force-pushed the NFC-99 branch 2 times, most recently from 0315bc1 to 076734c Compare November 27, 2025 12:55
@SanderKondratjevNortal SanderKondratjevNortal force-pushed the NFC-99 branch 2 times, most recently from 46c9709 to 025a58c Compare January 27, 2026 09:07

const path = window.location.pathname;
let endpoint;
if (path === "/sign/mobile/certificate") {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed during Java code review, you can just validate the path and then reuse it directly:

const path = window.location.pathname;
const allowedEndpoints = ["/sign/mobile/certificate", "/sign/mobile/signature"];
if (!allowedEndpoints.includes(path)) {
    const error = new Error("Unexpected callback path: " + path);
    error.code = "INVALID_CALLBACK_PATH";
    throw error;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This remains yet to be updated.

AddNewClaimIfCertificateHasData(claims, ClaimTypes.Name, certificate.GetSubjectCn);

var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
if (!string.IsNullOrEmpty(authToken.UnverifiedSigningCertificate))
Copy link
Copy Markdown
Member

@mrts mrts Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need a v1.1 format check here to avoid the following attack (also in Java):

  1. Attacker gets a valid web-eid:1 auth token from client-side flow to log in normally
  2. Before posting to /auth/login, attacker edits JSON and adds:
    • unverifiedSigningCertificate: any base64 cert they want
    • supportedSignatureAlgorithms: any list
  3. Server validates token as v1, v1 validator ignores these two fields and authentication succeeds
  4. AuthController stores those untrusted values into auth cookie claims (signingCertificate, supportedSignatureAlgorithms)
  5. Later attacker calls /sign/mobile/init; MobileSigningService sees those claims, skips certificate initialization and builds mobile sign request directly from injected claim values
  6. Signing flow now runs with attacker-chosen signing certificate metadata, not server-validated metadata

This is not immediately dangerous, but it bypasses v1.1 safety checks and may introduce some subtle attacks against real systems that we cannot even foresee at the moment.

A simple

var isV11 = authToken.Format?.StartsWith("web-eid:1.1", StringComparison.OrdinalIgnoreCase) == true;

check and using isV11 in the if block would guard against this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not implemented yet.


bool hasInvalid =
algorithms.Any(a =>
!SupportedSigningCryptoAlgorithms.Contains(a.CryptoAlgorithm) ||
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a can be null here, then a.CryptoAlgorithm will fail with NullReferenceException.

…eid example

Signed-off-by: Sander Kondratjev <sander.kondratjev@nortal.com>
Signed-off-by: Sander Kondratjev <sander.kondratjev@nortal.com>
/// Validates the purpose of the user certificate from the authentication token.
/// </summary>
public class SubjectCertificatePurposeValidator : ISubjectCertificateValidator
internal class SubjectCertificatePurposeValidator : ISubjectCertificateValidator
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be sealed as others?

Suggested change
internal class SubjectCertificatePurposeValidator : ISubjectCertificateValidator
internal sealed class SubjectCertificatePurposeValidator : ISubjectCertificateValidator


const path = window.location.pathname;
let endpoint;
if (path === "/sign/mobile/certificate") {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This remains yet to be updated.

}
});

function isMobileDevice() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should extract this function to a separate module and reuse it to make code more DRY.

AddNewClaimIfCertificateHasData(claims, ClaimTypes.Name, certificate.GetSubjectCn);

var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
if (!string.IsNullOrEmpty(authToken.UnverifiedSigningCertificate))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not implemented yet.

var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var signingCertificate = authToken.UnverifiedSigningCertificates != null &&
authToken.UnverifiedSigningCertificates.Count > 0
? authToken.UnverifiedSigningCertificates[0]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noting that currently a user authenticating with multiple signing certificates cannot choose a non-first certificate during signing, but I guess this is intended.

<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="10.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.3" />
<PackageReference Include="Microsoft.Identity.Web" Version="3.8.4" />
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we update other packages as well?

using Exceptions;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use System.Text.Json instead of Newtonsoft.Json and remove Newtonsoft.Json from project. Also update project dependencies and .NET version to 10 like with example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants