Skip to content

feat: support email user instantiation#1657

Open
HauklandJ wants to merge 11 commits intomainfrom
feat/email-user
Open

feat: support email user instantiation#1657
HauklandJ wants to merge 11 commits intomainfrom
feat/email-user

Conversation

@HauklandJ
Copy link
Contributor

@HauklandJ HauklandJ commented Feb 6, 2026

Description

Add email to InstanceOwner when the party is self-identified and contains the email urn for external identifier

Related Issue(s)

  • #{issue number}

Verification

  • Your code builds clean without any errors or warnings
  • Manual testing done (required)
  • Relevant automated test added (if you find this hard, leave it and we'll help out)
  • All tests run green

Documentation

  • User documentation is updated with a separate linked PR in altinn-studio-docs. (if applicable)

Summary by CodeRabbit

  • New Features

    • API responses now include an external identifier for instance owners; instance creation can include self-identified external IDs.
  • Bug Fixes

    • Instance owner resolution during creation now considers authentication context (async) to preserve external identity for self‑identified parties.
  • Tests

    • Added and updated unit, integration, and fixture tests covering party-to-instance-owner resolution and externalIdentifier handling.
  • Documentation

    • OpenAPI schema updated to expose externalIdentifier.
  • Chores

    • Storage platform dependency bumped to 4.4.0.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 6, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaces synchronous party-to-instance-owner resolution with an async overload that accepts an authentication context, adds ExternalIdentifier to InstanceOwner responses and OpenAPI, introduces new URN constants, refactors instance-owner party-type logic, adds unit and layout tests, and bumps a storage-interface package version.

Changes

Cohort / File(s) Summary
Controllers & API models
src/Altinn.App.Api/Controllers/InstancesController.cs, src/Altinn.App.Api/Models/InstanceResponse.cs
POST endpoints updated to await InstantiationHelper.PartyToInstanceOwner(party, authenticationContext); InstanceOwnerResponse gains ExternalIdentifier and is set from instance.InstanceOwner.ExternalIdentifier.
Core helpers & constants
src/Altinn.App.Core/Helpers/InstantiationHelper.cs, src/Altinn.App.Core/Constants/AltinnUrns.cs
Added async overload PartyToInstanceOwner(Party, IAuthenticationContext) returning Task<InstanceOwner> and helper GetExternalIdentityForSelfIdentifiedParty; added URN constants SelfIdentifiedEmail and PartyUuid.
Expression state refactor
src/Altinn.App.Core/Internal/Expressions/LayoutEvaluatorState.cs
Extracted instance-owner party-type logic into private GetInstanceOwnerPartyType(...) and updated GetInstanceContext to use it.
Tests & fixtures
test/Altinn.App.Core.Tests/Helpers/InstantiationHelperTests.cs, test/Altinn.App.Core.Tests/LayoutExpressions/.../partyTypeSelfIdentifiedEmail.json, test/Altinn.App.Core.Tests/LayoutExpressions/.../partyTypeSelfIdentifiedUsername.json, test/Altinn.App.Api.Tests/Controllers/InstancesController_GetTests.ReturnsOkResult_Raw.verified.txt
Added unit tests for new async helper and external-identity behavior; added layout test for self-identified email; updated verified fixture to include externalIdentifier.
OpenAPI & public API snapshots
test/Altinn.App.Api.Tests/OpenApi/OpenApiSpecChangeDetection.SaveJsonSwagger.verified.json, test/Altinn.App.Api.Tests/OpenApi/OpenApiSpecChangeDetection.SaveCustomOpenApiSpec.verified.json, test/Altinn.App.Api.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt, test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
Replaced InstanceOwner.email with InstanceOwner.externalIdentifier in OpenAPI schemas; updated public API snapshots to include new ExternalIdentifier and the async overload in core.
Integration snapshots
test/.../_snapshots/*Instantiation.verified.txt
Multiple test snapshots updated where recorded request Content-Length values changed (mostly +13 bytes) — test fixtures adjusted.
Dependency update
Directory.Packages.props
Bumped Altinn.Platform.Storage.Interface from 4.3.0 to 4.4.0.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

kind/chore

Suggested reviewers

  • bjorntore
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.32% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: support email user instantiation' directly describes the main feature being added, which is supporting email-based instantiation for self-identified parties as an external identifier.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/email-user

No actionable comments were generated in the recent review. 🎉

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@HauklandJ
Copy link
Contributor Author

/publish

@github-actions
Copy link

github-actions bot commented Feb 6, 2026

PR release:

⚙️ Building...
✅ Done!

@HauklandJ HauklandJ added backport-ignore This PR is a new feature and should not be cherry-picked onto release branches squad/data Issues that belongs to the named squad. feature Label Pull requests with new features. Used when generation releasenotes labels Feb 6, 2026
@HauklandJ
Copy link
Contributor Author

/publish

@github-actions
Copy link

github-actions bot commented Feb 6, 2026

PR release:

⚙️ Building...
✅ Done!

@HauklandJ
Copy link
Contributor Author

/publish

@github-actions
Copy link

github-actions bot commented Feb 8, 2026

PR release:

⚙️ Building...
✅ Done!

@HauklandJ
Copy link
Contributor Author

/publish

@github-actions
Copy link

github-actions bot commented Feb 9, 2026

PR release:

⚙️ Building...
✅ Done!

@HauklandJ
Copy link
Contributor Author

/publish

@github-actions
Copy link

github-actions bot commented Feb 9, 2026

PR release:

⚙️ Building...
✅ Done!

@HauklandJ
Copy link
Contributor Author

tested in tt02:

image

@HauklandJ HauklandJ marked this pull request as ready for review February 9, 2026 11:09
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/Altinn.App.Api/Controllers/InstancesController.cs`:
- Around line 308-309: In PostSimplified, replace the synchronous
PartyToInstanceOwner call with the async overload that accepts the
authentication context: await InstantiationHelper.PartyToInstanceOwner(party,
_authenticationContext). Locate the PostSimplified method (around where
LookupParty is awaited) and update the InstanceOwner assignment to use the async
method on InstantiationHelper so email-identified users get their email
populated, matching the behavior used in Post.

In `@src/Altinn.App.Core/Internal/Expressions/LayoutEvaluatorState.cs`:
- Around line 227-239: The code calls
GetInstanceOwnerPartyType(Instance.InstanceOwner) without guarding for a null
Instance.InstanceOwner while other branches use Instance.InstanceOwner?; update
the call or signature to handle nulls: either check Instance.InstanceOwner for
null before calling (return "unknown" when null) or change
GetInstanceOwnerPartyType to accept a nullable InstanceOwner? and return
"unknown" when the parameter is null; update references to
GetInstanceOwnerPartyType accordingly so no NullReferenceException can occur
when key == "instanceOwnerPartyType".
🧹 Nitpick comments (4)
src/Altinn.App.Core/Helpers/InstantiationHelper.cs (2)

222-267: Code duplication between sync and async PartyToInstanceOwner overloads.

The SSN, OrgNumber, and default branches (lines 231–242, 262–267) are duplicated from the sync overload (lines 200–221). Consider having the async overload delegate to the sync one for non-SelfIdentified cases to reduce drift risk:

♻️ Suggested refactor
 internal static async Task<InstanceOwner> PartyToInstanceOwner(
     Party party,
     IAuthenticationContext authenticationContext
 )
 {
-    if (!string.IsNullOrEmpty(party.SSN))
-    {
-        return new() { PartyId = party.PartyId.ToString(CultureInfo.InvariantCulture), PersonNumber = party.SSN };
-    }
-    else if (!string.IsNullOrEmpty(party.OrgNumber))
-    {
-        return new()
-        {
-            PartyId = party.PartyId.ToString(CultureInfo.InvariantCulture),
-            OrganisationNumber = party.OrgNumber,
-        };
-    }
-    else if (party.PartyTypeName.Equals(PartyType.SelfIdentified))
+    if (party.PartyTypeName.Equals(PartyType.SelfIdentified))
     {
         string? externalIdentifier = await GetExternalIdentityForSelfIdentifiedParty(party, authenticationContext);
-        // external identifier is expected to be in the format "urn:altinn:person:idporten-email:<email>".
         const string emailPrefix = AltinnUrns.SelfIdentifiedEmail + ":";
         if (
             externalIdentifier is not null
             && externalIdentifier.StartsWith(emailPrefix, StringComparison.OrdinalIgnoreCase)
         )
         {
             return new()
             {
                 PartyId = party.PartyId.ToString(CultureInfo.InvariantCulture),
                 Email = externalIdentifier[emailPrefix.Length..],
             };
         }
-        // Support legacy self identification with username and password.
         return new() { PartyId = party.PartyId.ToString(CultureInfo.InvariantCulture), Username = party.Name };
     }
-    return new()
-    {
-        PartyId = party.PartyId.ToString(CultureInfo.InvariantCulture),
-        // instanceOwnerPartyType == "unknown"
-    };
+
+    // SSN, OrgNumber, and unknown party types don't need auth context
+    return PartyToInstanceOwner(party);
 }

269-282: Consider adding telemetry for external identity resolution.

This new code path is a key part of the email user instantiation feature. There's no OpenTelemetry instrumentation to track when external identity resolution is attempted, succeeds, or falls back to legacy username flow. Adding an activity/span or at minimum a metric counter would help with observability in production.

As per coding guidelines, "Comprehensive telemetry instrumentation should be included in feature implementations using OpenTelemetry".

test/Altinn.App.Core.Tests/Helpers/InstantiationHelperTests.cs (2)

11-11: Consider sealing the test class.

Minor nit: the class could be sealed to follow the project convention. As per coding guidelines, "Use sealed for classes unless inheritance is considered a valid use-case".


13-48: Good coverage of the email extraction path.

The test correctly validates that the email is extracted from the URN and that Username remains null. Consider adding an edge-case test where ExternalIdentity equals exactly the prefix ("urn:altinn:person:idporten-email:") with no email portion — in that scenario the helper (line 256 of InstantiationHelper.cs) would set Email to an empty string, which may warrant either a guard in the helper or documented behavior via a test.

@HauklandJ HauklandJ requested a review from ivarne February 9, 2026 12:23
@HauklandJ HauklandJ self-assigned this Feb 9, 2026
@HauklandJ HauklandJ moved this to 🔎 In review in Team Altinn Studio Feb 9, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/Altinn.App.Api/Models/InstanceResponse.cs`:
- Around line 163-166: The ExternalIdentifier property is documented as nullable
but the file has "#nullable disable"; re-enable nullable reference types for the
file (replace "#nullable disable" with "#nullable enable" or add "#nullable
enable" at the top) and update the property signature to reflect nullability:
change ExternalIdentifier to "public string? ExternalIdentifier { get; init; }"
(referencing the ExternalIdentifier property in InstanceResponse) so the type
matches the XML docs and project nullable guidelines.

In `@src/Altinn.App.Core/Helpers/InstantiationHelper.cs`:
- Around line 264-276: GetExternalIdentityForSelfIdentifiedParty dereferences
profile without guarding against a null return from user.LookupProfile(); change
the method to check the awaited LookupProfile() result (from
user.LookupProfile()) for null and return null if it's null, or return the
ExternalIdentity only when profile is non-null (e.g., use a null-check or the
null-conditional on profile before returning profile.ExternalIdentity) so no
NullReferenceException occurs.

@sonarqubecloud
Copy link

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

Labels

backport-ignore This PR is a new feature and should not be cherry-picked onto release branches feature Label Pull requests with new features. Used when generation releasenotes squad/data Issues that belongs to the named squad.

Projects

Status: 🔎 In review

Development

Successfully merging this pull request may close these issues.

2 participants