{"record":{"id":"cd356003e8af7776","repo":"karatelabs/karate","slug":"missing-url-token-endpoint-in-oauth-config","errorCode":null,"errorMessage":"Missing 'url' (token endpoint) in OAuth config","messagePattern":"Missing 'url' \\(token endpoint\\) in OAuth config","errorType":"validation","errorClass":"OAuth2Exception","httpStatus":null,"severity":"error","filePath":"karate-core/src/main/java/io/karatelabs/http/AuthorizationCodeAuthHandler.java","lineNumber":174,"sourceCode":"        // Add state for CSRF protection\n        String state = generateState();\n        url.append(\"&state=\").append(urlEncode(state));\n\n        return url.toString();\n    }\n\n    /**\n     * Exchange authorization code for access token\n     */\n    private OAuth2Token exchangeCodeForToken(\n        HttpRequestBuilder builder,\n        String code,\n        String codeVerifier,\n        String redirectUri\n    ) {\n        String tokenUrl = (String) config.get(\"url\");\n        if (tokenUrl == null) {\n            throw new OAuth2Exception(\"Missing 'url' (token endpoint) in OAuth config\");\n        }\n\n        logger.debug(\"Exchanging authorization code for token...\");\n\n        builder.url(tokenUrl);\n        builder.formField(\"grant_type\", \"authorization_code\");\n        builder.formField(\"code\", code);\n        builder.formField(\"redirect_uri\", redirectUri);\n        builder.formField(\"client_id\", config.get(\"client_id\"));\n        builder.formField(\"code_verifier\", codeVerifier);\n\n        // Optional client_secret (for confidential clients)\n        if (config.containsKey(\"client_secret\")) {\n            builder.formField(\"client_secret\", config.get(\"client_secret\"));\n        }\n\n        builder.header(\"Accept\", \"application/json\");\n","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-core/src/main/java/io/karatelabs/http/AuthorizationCodeAuthHandler.java#L156-L192","documentation":"exchangeCodeForToken() reads the token endpoint URL from the config key 'url'. Without it the authorization code cannot be exchanged for tokens, so an OAuth2Exception is thrown before any HTTP request is made. Note the key is the generic 'url', not something token-specific like 'tokenUrl'.","triggerScenarios":"token() -> exchangeCodeForToken() called with a config that has authorizationUrl and client_id but no 'url' key pointing at the token endpoint — common when the config was built for the authorize step only, or the key was named 'tokenUrl'.","commonSituations":"Splitting OAuth config across files where the token endpoint was dropped, renaming keys when migrating between auth handler implementations, or provider docs using 'token_endpoint' terminology.","solutions":["Add the provider's token endpoint under the exact key 'url' in the OAuth config map","Check for near-miss keys like 'tokenUrl' or 'token_url' and rename to 'url'","Validate the full required key set (authorizationUrl, client_id, url) before running the flow"],"exampleFix":"// before\nconfig.put(\"tokenUrl\", \"https://idp/token\");\n// after\nconfig.put(\"url\", \"https://idp/token\");","handlingStrategy":"validation","validationCode":"Object tokenUrl = config.get(\"url\");\nif (tokenUrl == null || tokenUrl.toString().isBlank()) {\n    throw new IllegalArgumentException(\"config.url (token endpoint) is required\");\n}","typeGuard":"static String requireConfigKey(Map<String, Object> config, String key) {\n    Object v = config.get(key);\n    if (!(v instanceof String s) || s.isBlank()) {\n        throw new IllegalArgumentException(\"Missing '\" + key + \"' in OAuth config\");\n    }\n    return s;\n}","tryCatchPattern":"try {\n    Token t = handler.token(code, verifier, redirectUri);\n} catch (OAuth2Exception e) {\n    if (e.getMessage().contains(\"token endpoint\")) {\n        throw new ConfigurationException(\"Add 'url' (token endpoint) to OAuth config\", e);\n    }\n    throw e;\n}","preventionTips":["Set the token endpoint under the exact key 'url' (not tokenUrl/token_url)","Validate the full key set (authorizationUrl, client_id, url) before any flow step","Reuse a shared config-builder that always emits all required keys","Verify endpoint URLs against provider OIDC discovery metadata"],"tags":["oauth2","configuration","token-exchange","missing-config"],"backgroundTag":"missing-required-config-field","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}