{"record":{"id":"fda752e158f59137","repo":"hashicorp/nomad","slug":"invalid-or-missing-issuer-parameter-in-callback","errorCode":null,"errorMessage":"invalid or missing issuer parameter in callback","messagePattern":"invalid or missing issuer parameter in callback","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/acl_endpoint.go","lineNumber":2780,"sourceCode":"\n\t// Use the cache to provide us with an OIDC provider for the auth method\n\t// that was resolved from state.\n\toidcProvider, err := a.oidcProviderCache.Get(authMethod)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to generate OIDC provider: %v\", err)\n\t}\n\n\t// Check if the OIDC provider requires the `iss` parameter to be\n\t// validated\n\tproviderMetadata := struct {\n\t\tAuthorizationResponseIssParameterSupported bool `json:\"authorization_response_iss_parameter_supported\"`\n\t}{}\n\tif err := oidcProvider.Claims(&providerMetadata); err != nil {\n\t\treturn fmt.Errorf(\"failed to retrieve OIDC provider metadata: %w\", err)\n\t}\n\tif providerMetadata.AuthorizationResponseIssParameterSupported {\n\t\tif args.Iss == \"\" || args.Iss != authMethod.Config.OIDCDiscoveryURL {\n\t\t\treturn errors.New(\"invalid or missing issuer parameter in callback\")\n\t\t}\n\t}\n\n\t// Retrieve the request generated in OIDCAuthURL()\n\toidcReq := a.oidcRequestCache.LoadAndDelete(args.ClientNonce) // I am so done with this NONCENSE\n\tif oidcReq == nil {\n\t\t// note: this may happen if there is a leader election between getting\n\t\t// the auth url and completing the login flow here.\n\t\treturn errors.New(\"no OIDC request found for client nonce\")\n\t}\n\n\t// Generate a context with a deadline. This is passed to the OIDC provider\n\t// and used when making remote HTTP requests.\n\tctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(aclOIDCCallbackRequestExpiryTime))\n\tdefer cancel()\n\n\t// Exchange the state and code for an OIDC provider token.\n\toidcToken, err := oidcProvider.Exchange(ctx, oidcReq, args.State, args.Code)","sourceCodeStart":2762,"sourceCodeEnd":2798,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/acl_endpoint.go#L2762-L2798","documentation":"When the OIDC provider signals AuthorizationResponseIssParameterSupported, the callback must carry an iss parameter that matches the configured OIDCDiscoveryURL. OIDCCompleteAuth rejects the callback when iss is absent or differs, guarding against issuer-confusion / mix-up attacks between multiple providers.","triggerScenarios":"OIDCCompleteAuth is called (ACL auth callback) with args.Iss empty, or args.Iss not exactly equal to authMethod.Config.OIDCDiscoveryURL, while provider metadata declares iss-parameter support.","commonSituations":"OIDC provider omits or misconfigures the iss response parameter; auth method's OIDCDiscoveryURL has a trailing slash or scheme mismatch (https vs http) versus the iss the provider sends; discovery URL changed in config after the auth flow started; registering the same callback with multiple providers.","solutions":["Compare the iss value in the callback URL against authMethod.Config.OIDCDiscoveryURL byte-for-byte and fix the discovery URL config (watch trailing slashes and scheme) to match the issuer the provider actually sends.","Check the OIDC provider's configuration/docs so it sends the iss authorization-response parameter when it advertises iss support.","Re-run the login flow after any config change — an auth started before a discovery-URL change will mismatch.","Verify no proxy/redirect step is stripping the iss query parameter from the callback."],"exampleFix":"// before: discovery URL with trailing slash mismatches iss\nauthMethod.Config.OIDCDiscoveryURL = \"https://accounts.example.com/\"\n// after: match the provider's iss exactly\nauthMethod.Config.OIDCDiscoveryURL = \"https://accounts.example.com\"","handlingStrategy":"validation","validationCode":"if providerMetadata.AuthorizationResponseIssParameterSupported {\n    iss := callbackQuery.Get(\"iss\")\n    if iss == \"\" || iss != strings.TrimRight(authMethod.Config.OIDCDiscoveryURL, \"/\") {\n        return errors.New(\"callback issuer does not match configured discovery URL\")\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := a.OIDCCompleteAuth(...); err != nil && strings.Contains(err.Error(), \"invalid or missing issuer\") {\n    // log callback iss vs discovery URL and re-initiate the login flow\n    return restartOIDCLogin()\n}","preventionTips":["Keep OIDCDiscoveryURL byte-exact with the provider's issuer (no trailing slash drift)","Re-initiate login after any auth-method config change","Verify providers/send iss parameter before enabling iss validation","Check reverse proxies are not stripping query parameters from callbacks"],"tags":["oidc","issuer","callback","csrf-mixup"],"backgroundTag":"oidc-issuer-mismatch","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}