{"record":{"id":"94dd4d6174f72e7a","repo":"apache/pulsar","slug":"issuer-url-does-not-use-https-but-must","errorCode":null,"errorMessage":"Issuer URL does not use https, but must: ","messagePattern":"Issuer URL does not use https, but must: ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"critical","filePath":"pulsar-broker-auth-oidc/src/main/java/org/apache/pulsar/broker/authentication/oidc/AuthenticationProviderOpenID.java","lineNumber":500,"sourceCode":"     * the plugin to authenticate any token. Thus, it fails initialization if the configuration is\n     * missing. Each issuer URL should use the HTTPS scheme. The plugin fails initialization if any\n     * issuer url is insecure, unless requireHttps is false.\n     * @param allowedIssuers - issuers to validate\n     * @param requireHttps - whether to require https for issuers.\n     * @param allowEmptyIssuers - whether to allow empty issuers. This setting only makes sense when kubernetes is used\n     *                   as a fallback issuer.\n     * @return the validated issuers\n     * @throws IllegalArgumentException if the allowedIssuers is empty, or contains insecure issuers when required\n     */\n    private Set<String> validateIssuers(Set<String> allowedIssuers, boolean requireHttps, boolean allowEmptyIssuers) {\n        if (allowedIssuers == null || (allowedIssuers.isEmpty() && !allowEmptyIssuers)) {\n            throw new IllegalArgumentException(\"Missing configured value for: \" + ALLOWED_TOKEN_ISSUERS);\n        }\n        for (String issuer : allowedIssuers) {\n            if (!issuer.toLowerCase().startsWith(\"https://\")) {\n                log.warn().attr(\"issuer\", issuer).log(\"Allowed issuer is not using https scheme\");\n                if (requireHttps) {\n                    throw new IllegalArgumentException(\"Issuer URL does not use https, but must: \" + issuer);\n                }\n            }\n        }\n        return allowedIssuers;\n    }\n\n    /**\n     * Validate the configured allow list of allowedAudiences. The allowedAudiences must be set because\n     * JWT must have an audience claim.\n     * See https://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation.\n     * @param allowedAudiences\n     * @return the validated audiences\n     */\n    String[] validateAllowedAudiences(Set<String> allowedAudiences) {\n        if (allowedAudiences == null || allowedAudiences.isEmpty()) {\n            throw new IllegalArgumentException(\"Missing configured value for: \" + ALLOWED_AUDIENCES);\n        }\n        return allowedAudiences.toArray(new String[0]);","sourceCodeStart":482,"sourceCodeEnd":518,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker-auth-oidc/src/main/java/org/apache/pulsar/broker/authentication/oidc/AuthenticationProviderOpenID.java#L482-L518","documentation":"An IllegalArgumentException thrown by validateIssuers during initialize when an entry in allowedTokenIssuers does not start with https:// and requireHttps is true (the default). The provider refuses to start rather than trusting tokens over insecure issuer URLs, which could allow token forgery via plaintext-protected metadata endpoints.","triggerScenarios":"allowedTokenIssuers contains an issuer like http://idp.example.com or a bare hostname such as idp.example.com while the requireHttps setting is enabled (default).","commonSituations":"Local development IdPs (Keycloak, Dex) configured with http:// URLs deployed to production config; issuer copied from documentation without the scheme; values like localhost:8081 lacking https://; older IdP deployments on plain HTTP.","solutions":["Change the issuer URL to use the https:// scheme in authenticationProviderOpenID.allowedTokenIssuers","Serve the identity provider behind TLS (reverse proxy or ingress with a certificate)","For local testing only, explicitly disable the https requirement in the provider configuration","Remove the http:// entry if it is stale and keep only valid HTTPS issuers"],"exampleFix":"// before\nauthenticationProviderOpenID.allowedTokenIssuers=http://keycloak.internal:8080/realms/pulsar\n// after\nauthenticationProviderOpenID.allowedTokenIssuers=https://keycloak.internal/realms/pulsar","handlingStrategy":"validation","validationCode":"// Check every issuer uses https before applying config\nSet<String> issuers = parseCsv(config.getString(\"authenticationProviderOpenID.allowedTokenIssuers\"));\nfor (String issuer : issuers) {\n    if (!issuer.toLowerCase().startsWith(\"https://\")) {\n        throw new IllegalStateException(\"Issuer must use https: \" + issuer);\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    provider.initialize(config);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"Issuer URL does not use https\")) {\n        throw new IllegalStateException(\"Fix issuer URLs to https:// in allowedTokenIssuers: \" + e.getMessage(), e);\n    }\n    throw e;\n}","preventionTips":["Standardize issuer URLs as https:// in all environments, including dev/staging","Terminate TLS at a reverse proxy/ingress in front of internal IdPs like Keycloak","Run a startup config check that rejects http:// issuer entries before broker deployment","Only disable the https requirement for throwaway local testing, never in shared environments"],"tags":["configuration","oidc","tls","security"],"backgroundTag":"insecure-http-endpoint","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}