Skip to content

fix(oidc): fail sign-in when register self-identified provisioning fails#2004

Open
TheTechArch wants to merge 1 commit into
mainfrom
feature/exception_handling
Open

fix(oidc): fail sign-in when register self-identified provisioning fails#2004
TheTechArch wants to merge 1 commit into
mainfrom
feature/exception_handling

Conversation

@TheTechArch

@TheTechArch TheTechArch commented May 28, 2026

Copy link
Copy Markdown
Member

Summary

  • A failed register self-identified provisioning call (non-200, network error, or exception — all swallowed into null by RegisterUserProvisioningClient) caused IdentifyOrCreateAltinnUser to return an unpopulated UserAuthenticationModel and continue building an Altinn session/cookie for UserID 0 / PartyID 0. The real failure was masked by a downstream session-creation error, making the actual cause hard to diagnose (the original CodeRabbit concern on feat(oidc): provision self-identified users via register (feature-fla… #1982).
  • GetOrCreateSelfIdentifiedUserViaRegister now returns a non-nullable SelfIdentifiedUser and throws a new RegisterUserProvisioningException when provisioning fails, instead of returning the empty model.
  • HandleUpstreamCallback catches that exception and returns a 502 Bad Gateway LocalError (register is a failed downstream dependency), so the cause is logged and surfaced rather than hidden.
  • Removed the two dead if (provisioned is null) return userAuthenticationModel; blocks now that the helper can't return null.

Notes

  • Only the OIDC upstream-callback path exercises the register-provisioning branches. The two Altinn2-ticket paths also call IdentifyOrCreateAltinnUser but require UserID > 0 and normally route to the existing-user branch; in the rare case a ticket carries an ExternalIdentity with the flag on and register is down, the exception propagates as an unhandled 500 — still surfacing the real cause in logs rather than masking it. Can add explicit handling there if desired.

Test plan

  • Build passes (verified locally: 0 errors)
  • With flag RegisterSelfIdentifiedUserProvisioning on and register returning a non-200 / unreachable, OIDC sign-in returns 502 with the register failure logged (no session/cookie issued)
  • With flag on and register healthy, new self-identified user (Educational and idporten-email) signs in successfully
  • With flag off, behavior unchanged (SBL Bridge GetUser + CreateUser)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved error handling during user registration via OIDC authentication with clearer user-facing error messages.
    • Enhanced reliability of the authentication provisioning process to gracefully handle registration failures.

Review Change Stack

When register provisioning returned null (non-200, network error, or
exception swallowed by the client), the OIDC sign-in path returned an
unpopulated UserAuthenticationModel and continued building a session for
UserID 0 / PartyID 0. The real failure was masked by a downstream
session-creation error.

Provisioning failure now throws RegisterUserProvisioningException, which
HandleUpstreamCallback converts into a 502 LocalError so the actual cause
is logged and surfaced instead of hidden.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR introduces exception-based error handling for OIDC self-identified user provisioning. A new RegisterUserProvisioningException is defined, the provisioning helper is refactored to throw this exception instead of returning null, callers are simplified by removing null checks, and the callback entry point catches and converts the exception to an HTTP 502 error response.

Changes

OIDC Provisioning Exception Handling

Layer / File(s) Summary
RegisterUserProvisioningException definition
src/Authentication/Exceptions/RegisterUserProvisioningException.cs
New custom exception with three constructors (parameterless, message-only, message+inner exception) for signaling provisioning failures during self-identified user registration.
Provisioning helper throws on failure
src/Authentication/Services/OidcServerService.cs
GetOrCreateSelfIdentifiedUserViaRegister return type changed to non-nullable; when downstream registration returns no result, the method now throws RegisterUserProvisioningException instead of logging and returning null. Namespace import added for the exception type.
Provisioning paths simplified
src/Authentication/Services/OidcServerService.cs
Null checks for provisioning results removed from both SSN/external identity and selfregistered-email registration branches; control flow now relies on exception propagation.
Callback error handling
src/Authentication/Services/OidcServerService.cs
HandleUpstreamCallback wraps IdentifyOrCreateAltinnUser in try/catch; RegisterUserProvisioningException is logged and converted to an UpstreamCallbackResult with HTTP 502 and user-facing "try again later" message.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • Altinn/altinn-authentication#1982: Related modification to the same provisioning path in OidcServerService, shifting from null-return handling to exception-based control flow.

Suggested reviewers

  • Alxandr
  • acn-dgopa
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding error handling to fail sign-in when self-identified user provisioning fails, which matches the core objective of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/exception_handling

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/Authentication/Services/OidcServerService.cs (2)

1652-1657: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Sanitize exception text and normalize client failures to the same exception type.

Line 1657 includes raw externalIdentity in the exception message, which is logged at Line 323 and can leak identifiers. Also, exceptions thrown by _registerUserProvisioningClient.GetOrCreateUser(...) currently bypass this mapping path. Use a generic message and wrap non-cancellation exceptions as RegisterUserProvisioningException.

Proposed fix
-            var response = await _registerUserProvisioningClient.GetOrCreateUser(request, cancellationToken);
+            SelfIdentifiedUser? response;
+            try
+            {
+                response = await _registerUserProvisioningClient.GetOrCreateUser(request, cancellationToken);
+            }
+            catch (OperationCanceledException)
+            {
+                throw;
+            }
+            catch (Exception ex)
+            {
+                throw new RegisterUserProvisioningException(
+                    "Register self-identified provisioning failed; sign-in cannot complete.",
+                    ex);
+            }
 
             if (response is null)
             {
                 throw new RegisterUserProvisioningException(
-                    $"Register self-identified provisioning returned no result for externalIdentity '{externalIdentity}'; sign-in cannot complete.");
+                    "Register self-identified provisioning returned no result; sign-in cannot complete.");
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Authentication/Services/OidcServerService.cs` around lines 1652 - 1657,
Replace the raw externalIdentity leak and normalize client failures by changing
the call to _registerUserProvisioningClient.GetOrCreateUser(request,
cancellationToken) so that any non-cancellation exception is caught and rethrown
as a RegisterUserProvisioningException with a generic message (do not include
externalIdentity); also when the returned response is null throw a
RegisterUserProvisioningException with a generic message (no sensitive data).
Preserve and rethrow OperationCanceledException/TaskCanceledException unchanged
so cancellation semantics remain intact.

1637-1643: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle the new provisioning exception in all entry-point flows.

After this contract change, failures now throw instead of returning null. HandleUpstreamCallback handles that, but the calls at Line 152 and Line 781 do not, which can still surface as unhandled 500s on affected ticket paths. Add consistent catch/mapping in those entry points as well.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Authentication/Services/OidcServerService.cs` around lines 1637 - 1643,
Get the new provisioning exception handled consistently: surround every call
site of GetOrCreateSelfIdentifiedUserViaRegister (including the two entry-point
flows outside HandleUpstreamCallback that currently call it without protection)
with a try/catch that mirrors HandleUpstreamCallback’s behavior—catch the
provisioning-specific exception type thrown by
GetOrCreateSelfIdentifiedUserViaRegister and map it to the same
response/status/logging used in HandleUpstreamCallback so those flows no longer
surface unhandled 500s. Ensure you update both entry-point handlers that invoke
GetOrCreateSelfIdentifiedUserViaRegister (the other two call sites in this file)
to use the same catch-and-map logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/Authentication/Services/OidcServerService.cs`:
- Around line 1652-1657: Replace the raw externalIdentity leak and normalize
client failures by changing the call to
_registerUserProvisioningClient.GetOrCreateUser(request, cancellationToken) so
that any non-cancellation exception is caught and rethrown as a
RegisterUserProvisioningException with a generic message (do not include
externalIdentity); also when the returned response is null throw a
RegisterUserProvisioningException with a generic message (no sensitive data).
Preserve and rethrow OperationCanceledException/TaskCanceledException unchanged
so cancellation semantics remain intact.
- Around line 1637-1643: Get the new provisioning exception handled
consistently: surround every call site of
GetOrCreateSelfIdentifiedUserViaRegister (including the two entry-point flows
outside HandleUpstreamCallback that currently call it without protection) with a
try/catch that mirrors HandleUpstreamCallback’s behavior—catch the
provisioning-specific exception type thrown by
GetOrCreateSelfIdentifiedUserViaRegister and map it to the same
response/status/logging used in HandleUpstreamCallback so those flows no longer
surface unhandled 500s. Ensure you update both entry-point handlers that invoke
GetOrCreateSelfIdentifiedUserViaRegister (the other two call sites in this file)
to use the same catch-and-map logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d545f848-fe39-4f5c-8e2c-8bad2dc1b543

📥 Commits

Reviewing files that changed from the base of the PR and between cd7d2b3 and a093357.

📒 Files selected for processing (2)
  • src/Authentication/Exceptions/RegisterUserProvisioningException.cs
  • src/Authentication/Services/OidcServerService.cs

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
12.5% Coverage on New Code (required ≥ 65%)

See analysis details on SonarQube Cloud

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.

1 participant