elsa-workflows/elsa-core · error · OpenIdConnectAuthenticationException
The identity provider response did not contain a subject.
Error message
The identity provider response did not contain a subject.
What it means
After all cryptographic checks pass, AuthenticateCallbackAsync reads the 'sub' claim from the validated principal to identify the authenticated user. If the sub claim is missing or whitespace, the provider response does not identify a subject and this exception is thrown.
Solutions
- Inspect the decoded id_token (jwt.io) and confirm a non-empty 'sub' claim is present.
- Fix the provider's claim mapping or add the required scopes/claims so sub is included in the id_token.
- If the provider uses a different subject claim (e.g. Azure AD 'oid'), adjust the connection's ClaimProjection and note this specific check requires sub.
Defensive patterns
Strategy: validation
Validate before calling
var token = DecodeJwt(idToken); if (string.IsNullOrWhiteSpace(token.Payload.Subject)) throw new InvalidOperationException("id_token has no sub claim."); Try / catch
try { await adapter.AuthenticateCallbackAsync(context); } catch (OpenIdConnectAuthenticationException ex) when (ex.Message.Contains("subject")) { logger.LogError(ex, "Provider id_token missing sub claim"); throw; } Prevention
- Decode a sample id_token from the provider during setup and confirm 'sub' exists.
- Review provider claim-mapping policies that rename or drop the sub claim.
- Request the openid scope so the subject claim is guaranteed by the spec.
When it happens
Trigger: AuthenticateCallbackAsync where principal.FindFirst("sub") is null or empty after ValidateIdTokenAsync succeeds.
Common situations: Misconfigured claim mapping at the provider that strips or renames the sub claim (e.g. mapping to 'oid' in Azure AD); using an access token style token that lacks sub; provider scopes/requested claims omit the subject.
Related errors
- The identity provider rejected the authentication request.
- The identity provider callback could not be correlated.
- The identity provider callback issuer did not match the…
- The identity provider nonce did not match the initiated…
- The identity provider callback did not contain an…
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/27eeb23a2dea4bb7.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.ExternalAuthentication.OpenIdConnect/Services/OpenIdConnectExternalAuthenticationAdapter.cs:103
throw new OpenIdConnectAuthenticationException("The identity provider callback could not be correlated.");
var settings = await GetSettingsAsync(context.Connection.Connection.AdapterSettings, cancellationToken);
var metadata = await ResolveMetadataAsync(settings, cancellationToken);
var adapterState = ReadAdapterState(context.Transaction.ProtectedPayload);
if (adapterState is not null && !string.Equals(adapterState.Issuer, metadata.Issuer, StringComparison.Ordinal))
throw new OpenIdConnectAuthenticationException("The identity provider callback issuer did not match the initiated request.");
var idToken = await ExchangeCodeAsync(settings, metadata, context, adapterState?.CodeVerifier, cancellationToken);
var principal = await ValidateIdTokenAsync(idToken, settings, metadata, cancellationToken);
var nonce = principal.FindFirst("nonce")?.Value;
var expectedNonce = context.Transaction.ProviderNonce ?? adapterState?.Nonce;
if (string.IsNullOrWhiteSpace(expectedNonce) || !FixedTimeEquals(nonce, expectedNonce))
throw new OpenIdConnectAuthenticationException("The identity provider nonce did not match the initiated request.");
var issuer = principal.FindFirst("iss")?.Value ?? metadata.Issuer;
var subject = principal.FindFirst("sub")?.Value;
if (string.IsNullOrWhiteSpace(subject))
throw new OpenIdConnectAuthenticationException("The identity provider response did not contain a subject.");
var projectedClaims = ProjectClaims(principal, context.Connection.Connection.ClaimProjection);
return new(new(issuer, subject, projectedClaims), projectedClaims, [], new(idToken));
}
public async ValueTask<ConnectionTestResult> TestAsync(ConnectionTestContext context, CancellationToken cancellationToken = default)
{
var settings = await GetSettingsAsync(context.Connection.Connection.AdapterSettings, cancellationToken);
_ = await ResolveMetadataAsync(settings, cancellationToken);
return new(ConnectionObservationStatus.Succeeded, "reachable", "Provider metadata was resolved.", []);
}
public async ValueTask<ExternalLogoutRequest?> CreateLogoutRequestAsync(ExternalLogoutContext context, CancellationToken cancellationToken = default)
{
var settings = await GetSettingsAsync(context.Connection.Connection.AdapterSettings, cancellationToken);
var metadata = await ResolveMetadataAsync(settings, cancellationToken);
if (metadata.EndSessionEndpoint is null)
return null;View on GitHub (pinned to fe9217bdfa)