{"record":{"id":"27eeb23a2dea4bb7","repo":"elsa-workflows/elsa-core","slug":"the-identity-provider-response-did-not-contain-a-subject","errorCode":null,"errorMessage":"The identity provider response did not contain a subject.","messagePattern":"The identity provider response did not contain a subject\\.","errorType":"exception","errorClass":"OpenIdConnectAuthenticationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.ExternalAuthentication.OpenIdConnect/Services/OpenIdConnectExternalAuthenticationAdapter.cs","lineNumber":103,"sourceCode":"            throw new OpenIdConnectAuthenticationException(\"The identity provider callback could not be correlated.\");\n\n        var settings = await GetSettingsAsync(context.Connection.Connection.AdapterSettings, cancellationToken);\n        var metadata = await ResolveMetadataAsync(settings, cancellationToken);\n        var adapterState = ReadAdapterState(context.Transaction.ProtectedPayload);\n        if (adapterState is not null && !string.Equals(adapterState.Issuer, metadata.Issuer, StringComparison.Ordinal))\n            throw new OpenIdConnectAuthenticationException(\"The identity provider callback issuer did not match the initiated request.\");\n\n        var idToken = await ExchangeCodeAsync(settings, metadata, context, adapterState?.CodeVerifier, cancellationToken);\n        var principal = await ValidateIdTokenAsync(idToken, settings, metadata, cancellationToken);\n        var nonce = principal.FindFirst(\"nonce\")?.Value;\n        var expectedNonce = context.Transaction.ProviderNonce ?? adapterState?.Nonce;\n        if (string.IsNullOrWhiteSpace(expectedNonce) || !FixedTimeEquals(nonce, expectedNonce))\n            throw new OpenIdConnectAuthenticationException(\"The identity provider nonce did not match the initiated request.\");\n\n        var issuer = principal.FindFirst(\"iss\")?.Value ?? metadata.Issuer;\n        var subject = principal.FindFirst(\"sub\")?.Value;\n        if (string.IsNullOrWhiteSpace(subject))\n            throw new OpenIdConnectAuthenticationException(\"The identity provider response did not contain a subject.\");\n\n        var projectedClaims = ProjectClaims(principal, context.Connection.Connection.ClaimProjection);\n        return new(new(issuer, subject, projectedClaims), projectedClaims, [], new(idToken));\n    }\n\n    public async ValueTask<ConnectionTestResult> TestAsync(ConnectionTestContext context, CancellationToken cancellationToken = default)\n    {\n        var settings = await GetSettingsAsync(context.Connection.Connection.AdapterSettings, cancellationToken);\n        _ = await ResolveMetadataAsync(settings, cancellationToken);\n        return new(ConnectionObservationStatus.Succeeded, \"reachable\", \"Provider metadata was resolved.\", []);\n    }\n\n    public async ValueTask<ExternalLogoutRequest?> CreateLogoutRequestAsync(ExternalLogoutContext context, CancellationToken cancellationToken = default)\n    {\n        var settings = await GetSettingsAsync(context.Connection.Connection.AdapterSettings, cancellationToken);\n        var metadata = await ResolveMetadataAsync(settings, cancellationToken);\n        if (metadata.EndSessionEndpoint is null)\n            return null;","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.ExternalAuthentication.OpenIdConnect/Services/OpenIdConnectExternalAuthenticationAdapter.cs#L85-L121","documentation":"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.","triggerScenarios":"AuthenticateCallbackAsync where principal.FindFirst(\"sub\") is null or empty after ValidateIdTokenAsync succeeds.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"var token = DecodeJwt(idToken); if (string.IsNullOrWhiteSpace(token.Payload.Subject)) throw new InvalidOperationException(\"id_token has no sub claim.\");","typeGuard":null,"tryCatchPattern":"try { await adapter.AuthenticateCallbackAsync(context); } catch (OpenIdConnectAuthenticationException ex) when (ex.Message.Contains(\"subject\")) { logger.LogError(ex, \"Provider id_token missing sub claim\"); throw; }","preventionTips":["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."],"tags":["openid-connect","authentication","missing-claim"],"backgroundTag":"unexpected-response-shape","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}