OrchardCMS/OrchardCore · error · SecurityTokenInvalidIssuerException

The token issuer is not valid.

Error message

The token issuer is not valid.

What it means

OpenIdConnect token validation is configured with a custom IssuerValidator that expects the token's iss claim to be an absolute URI whose host/path match an Orchard tenant (matched via _runningShellTable and compared to settings.Tenant). If the issuer is not a parseable absolute URI, or resolves to a different tenant, a SecurityTokenInvalidIssuerException is thrown and the token is rejected.

Solutions

  1. Align the token issuer URL (iss) with the tenant configured in the OpenIdConnect validation settings (Tenant field).
  2. Fix the issuing tenant's Authority/base URL so tokens carry the expected issuer URI.
  3. Check reverse-proxy forwarding (X-Forwarded-Host/Proto, UseForwardedHeaders) so issuer URLs match the public host.
  4. Update API clients to request tokens from the correct tenant endpoint.
  5. If you maintain this code, log the received issuer to ease diagnosis.
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate the expected issuer before sending the token
var expectedIssuer = new Uri(settings.Authority);
if (!Uri.TryCreate(tokenIssuer, UriKind.Absolute, out var iss) ||
    !string.Equals(iss.GetLeftPart(UriPartial.Authority), expectedIssuer.GetLeftPart(UriPartial.Authority), StringComparison.Ordinal))
    throw new SecurityTokenInvalidIssuerException("Token issuer does not match the configured tenant.");

Try / catch

try { await ValidateTokenAsync(token); }
catch (SecurityTokenInvalidIssuerException ex)
{ /* log ex.Message and the token iss claim; compare with the tenant's Authority URL */ }

Prevention

When it happens

Trigger: Presenting an OIDC/JWT token whose issuer URL does not exactly match the tenant configured in the OpenId validation settings — e.g. issuer with a different hostname, port, or path segment; malformed iss claim; tokens minted by another tenant.

Common situations: Misconfigured Authority/issuer in the token-issuing tenant; behind a reverse proxy where the public URL differs from the configured one; API clients hardcoding an issuer URL that predates a domain change; multi-tenant setups where the token was issued by tenant A but validated for tenant B.

Related errors


AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13). Data as JSON: /api/errors/e023a1836b1bc63e. Report an issue: GitHub.

Appendix: source

Thrown at src/OrchardCore.Modules/OrchardCore.OpenId/Configuration/OpenIdValidationConfiguration.cs:183

            // to validate/introspect tokens meant to be used with another tenant.
            options.Audiences.Add(OpenIdConstants.Prefixes.Tenant + _shellSettings.Name);

            // Note: token entry validation must be enabled to be able to validate reference tokens.
            options.EnableTokenEntryValidation = configuration.UseReferenceAccessTokens;

            // If an authority was explicitly set in the OpenID server options,
            // prefer it to the dynamic tenant comparison as it's more efficient.
            if (configuration.Authority != null)
            {
                options.TokenValidationParameters.ValidIssuer = configuration.Authority.AbsoluteUri;
            }
            else
            {
                options.TokenValidationParameters.IssuerValidator = (issuer, token, parameters) =>
                {
                    if (!Uri.TryCreate(issuer, UriKind.Absolute, out var uri))
                    {
                        throw new SecurityTokenInvalidIssuerException("The token issuer is not valid.");
                    }

                    var tenant = _runningShellTable.Match(HostString.FromUriComponent(uri), uri.AbsolutePath);
                    if (tenant == null || !string.Equals(tenant.Name, settings.Tenant, StringComparison.Ordinal))
                    {
                        throw new SecurityTokenInvalidIssuerException("The token issuer is not valid.");
                    }

                    return issuer;
                };
            }
        }).GetAwaiter().GetResult();
    }

    public void Configure(OpenIddictValidationDataProtectionOptions options)
    {
        var settings = GetValidationSettingsAsync().GetAwaiter().GetResult();
        if (settings == null)

View on GitHub (pinned to 4306c0717f)