{"record":{"id":"e023a1836b1bc63e","repo":"OrchardCMS/OrchardCore","slug":"the-token-issuer-is-not-valid","errorCode":null,"errorMessage":"The token issuer is not valid.","messagePattern":"The token issuer is not valid\\.","errorType":"exception","errorClass":"SecurityTokenInvalidIssuerException","httpStatus":null,"severity":"error","filePath":"src/OrchardCore.Modules/OrchardCore.OpenId/Configuration/OpenIdValidationConfiguration.cs","lineNumber":183,"sourceCode":"            // to validate/introspect tokens meant to be used with another tenant.\n            options.Audiences.Add(OpenIdConstants.Prefixes.Tenant + _shellSettings.Name);\n\n            // Note: token entry validation must be enabled to be able to validate reference tokens.\n            options.EnableTokenEntryValidation = configuration.UseReferenceAccessTokens;\n\n            // If an authority was explicitly set in the OpenID server options,\n            // prefer it to the dynamic tenant comparison as it's more efficient.\n            if (configuration.Authority != null)\n            {\n                options.TokenValidationParameters.ValidIssuer = configuration.Authority.AbsoluteUri;\n            }\n            else\n            {\n                options.TokenValidationParameters.IssuerValidator = (issuer, token, parameters) =>\n                {\n                    if (!Uri.TryCreate(issuer, UriKind.Absolute, out var uri))\n                    {\n                        throw new SecurityTokenInvalidIssuerException(\"The token issuer is not valid.\");\n                    }\n\n                    var tenant = _runningShellTable.Match(HostString.FromUriComponent(uri), uri.AbsolutePath);\n                    if (tenant == null || !string.Equals(tenant.Name, settings.Tenant, StringComparison.Ordinal))\n                    {\n                        throw new SecurityTokenInvalidIssuerException(\"The token issuer is not valid.\");\n                    }\n\n                    return issuer;\n                };\n            }\n        }).GetAwaiter().GetResult();\n    }\n\n    public void Configure(OpenIddictValidationDataProtectionOptions options)\n    {\n        var settings = GetValidationSettingsAsync().GetAwaiter().GetResult();\n        if (settings == null)","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/OrchardCMS/OrchardCore/blob/4306c0717fe573f6fca1b4955909ddab6a192807/src/OrchardCore.Modules/OrchardCore.OpenId/Configuration/OpenIdValidationConfiguration.cs#L165-L201","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Align the token issuer URL (iss) with the tenant configured in the OpenIdConnect validation settings (Tenant field).","Fix the issuing tenant's Authority/base URL so tokens carry the expected issuer URI.","Check reverse-proxy forwarding (X-Forwarded-Host/Proto, UseForwardedHeaders) so issuer URLs match the public host.","Update API clients to request tokens from the correct tenant endpoint.","If you maintain this code, log the received issuer to ease diagnosis."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Validate the expected issuer before sending the token\nvar expectedIssuer = new Uri(settings.Authority);\nif (!Uri.TryCreate(tokenIssuer, UriKind.Absolute, out var iss) ||\n    !string.Equals(iss.GetLeftPart(UriPartial.Authority), expectedIssuer.GetLeftPart(UriPartial.Authority), StringComparison.Ordinal))\n    throw new SecurityTokenInvalidIssuerException(\"Token issuer does not match the configured tenant.\");","typeGuard":null,"tryCatchPattern":"try { await ValidateTokenAsync(token); }\ncatch (SecurityTokenInvalidIssuerException ex)\n{ /* log ex.Message and the token iss claim; compare with the tenant's Authority URL */ }","preventionTips":["Keep the Authority/base URL of the issuing tenant identical to the issuer expected by the validating tenant.","Configure forwarded headers so issuer URIs match public endpoints behind proxies.","Regenerate client credentials after domain or tenant renames.","Log the received iss claim (safely) during validation failures to speed diagnosis."],"tags":["openid","jwt","security","multi-tenancy"],"backgroundTag":"jwt-token-expired","analyzedSha":"4306c0717fe573f6fca1b4955909ddab6a192807","analyzedAt":"2026-09-13T17:41:05.024Z","contentChangedAt":"2026-09-13T17:41:05.024Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}