Skip to content

Conversation

@zibet27
Copy link
Collaborator

@zibet27 zibet27 commented Jan 30, 2026

Subsystem
Server Auth

Motivation
KTOR-8596 OpenID Connect (OAuth2) auto-discover & configuration

Also see KTOR-8595 Auth JWK Support (auto-discover)
#5339

Solution
New DSL

val httpClient = HttpClient()
val config = httpClient.fetchOpenIdConfiguration("https://accounts.google.com")
install(Authentication) {
   oauth("test-openid", openIdConfig) {
      client = httpClient
      clientId = "test-client-id"
      clientSecret = "test-client-secret"
      urlProvider = { "http://localhost/callback" }
   }
}

@zibet27
Copy link
Collaborator Author

zibet27 commented Jan 30, 2026

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 30, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 30, 2026

Walkthrough

The PR extends OpenID Connect support in Ktor's authentication system by adding new properties to OpenIdConfiguration, introducing a new OpenIdOAuthConfig class, and providing new OAuth configuration overloads that accept OpenID configuration for streamlined OpenID-based OAuth flows.

Changes

Cohort / File(s) Summary
OpenIdConfiguration Data Class Expansion
ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OpenIdConfiguration.kt, ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OAuthProcedure.kt
Added seven new public properties (authorizationEndpoint, tokenEndpoint, userInfoEndpoint, scopesSupported, responseTypesSupported, tokenEndpointAuthMethodsSupported, claimsSupported) with JSON serialization mappings. Enhanced fetchOpenIdConfiguration with validation for authorizationEndpoint and tokenEndpoint non-blank requirements. Added two new oauth overloads accepting OpenIdConfiguration for automatic URL and settings population.
New OpenIdOAuthConfig Class
ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OpenIdOAuthConfig.kt
Introduced new public class wrapping OpenIdConfiguration with customizable OAuth fields including client, credentials, scopes, request method, extra parameters, state creation hook, error fallback, and nonce manager. Provides internal toServerSettings() helper to build OAuthServerSettings from configuration.
API Surface & ABI Changes
ktor-server/ktor-server-plugins/ktor-server-auth/api/ktor-server-auth.api, ktor-server/ktor-server-plugins/ktor-server-auth/api/ktor-server-auth.klib.api
Expanded OpenIdConfiguration constructor signature from two to nine parameters. Added new public accessors for all new properties. Removed clientId, clientSecret, and urlProvider fields from OpenIdOAuthConfig public surface. Added new oauth overload signatures to support OpenIdConfiguration-based configuration paths.
OpenID OAuth Test Suite
ktor-server/ktor-server-plugins/ktor-server-auth/common/test/io/ktor/tests/auth/OpenIdOAuthTest.kt, ktor-server/ktor-server-plugins/ktor-server-auth/jvm/test/io/ktor/tests/auth/FetchOpenIdConfigurationTest.kt
Added comprehensive test suite for OpenID configuration discovery and OAuth flows, including validation of default scopes, custom scopes, missing fields, extra parameters, and token exchange. Includes discovery tests asserting correct mapping of endpoints, jwks_uri, and supported values with error handling for missing critical fields.
JWT Discovery Test Refactoring
ktor-server/ktor-server-plugins/ktor-server-auth-jwt/jvm/test/io/ktor/server/auth/jwt/JwkDiscoveryTest.kt
Removed mock HTTP provider tests and runBlocking usage. Updated OpenIdConfiguration instantiation to include new required authorizationEndpoint and tokenEndpoint fields. Retained Jwk discovery rotation test with updated configuration usage.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • osipxd
  • bjhham
  • e5l
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.39% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'OAuth2 auto-configuration' clearly and concisely summarizes the main change: adding OAuth2 auto-discovery and configuration support via OpenID Connect.
Description check ✅ Passed The description includes required sections (Subsystem, Motivation with ticket reference, and Solution with code example) and adequately explains the OpenID Connect auto-discovery feature, meeting the template requirements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch zibet27/oauth2-auto-configuration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@ktor-server/ktor-server-plugins/ktor-server-auth/api/ktor-server-auth.api`:
- Around line 488-493: The two 4-arg Kotlin overloads
oauth(AuthenticationConfig, String, OpenIdConfiguration, Function1) and
oauth(AuthenticationConfig, String, String, Function1) cause Java-call ambiguity
when the third parameter is null; annotate the Kotlin overloads with distinct
JVM names (e.g., add `@JvmName`("oauthWithOpenId") to the OpenIdConfiguration
variant and `@JvmName`("oauthWithClientId") to the String-variant) so the
generated methods have unique JVM signatures and Java callers can unambiguously
invoke the desired overload; apply the annotations to the corresponding oauth
functions (and their default dispatchers if necessary) so the compiled API
exposes disambiguated method names.

In
`@ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OAuthProcedure.kt`:
- Around line 193-209: The oauth wrapper leaves
OAuthAuthenticationProvider.Config.client uninitialized when
OpenIdOAuthConfig.client is null, causing UninitializedPropertyAccessException;
fix by ensuring a default HttpClient is provided: in AuthenticationConfig.oauth
(the function in OAuthProcedure.kt) assign this.client = openIdConfig.client ?:
HttpClient() (or alternatively make OpenIdOAuthConfig initialize client to
HttpClient() by default) so the provider's lateinit client is always set before
build(); reference symbols: AuthenticationConfig.oauth, OpenIdOAuthConfig,
OAuthAuthenticationProvider.Config.client.

In
`@ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OpenIdOAuthConfig.kt`:
- Around line 63-72: The KDoc for OpenIdOAuthConfig.defaultScopes says it should
default to the intersection of ["openid","profile","email"] with
OpenIdConfiguration.scopesSupported, but the code assigns
openIdConfiguration.scopesSupported directly; update the implementation where
`scopes` is computed (the `scopes = defaultScopes ?:
openIdConfiguration.scopesSupported ?: listOf("openid")` logic) to instead
compute `defaultScopes ?:
(openIdConfiguration.scopesSupported?.intersect(listOf("openid","profile","email"))?.toList())
?: listOf("openid")` so the effective scopes match the documented intersection
with `OpenIdConfiguration.scopesSupported`; alternatively, if you prefer to keep
current behavior, update the KDoc for `defaultScopes` to describe that it uses
`scopesSupported` directly and falls back to ["openid"].
🧹 Nitpick comments (3)
ktor-server/ktor-server-plugins/ktor-server-auth/jvm/test/io/ktor/tests/auth/FetchOpenIdConfigurationTest.kt (1)

121-138: HTTP error handling test is adequate.

The test verifies that a 404 response results in a DiscoveryException. Consider adding a test for network failures or malformed JSON responses for more comprehensive coverage.

ktor-server/ktor-server-plugins/ktor-server-auth/common/test/io/ktor/tests/auth/OpenIdOAuthTest.kt (1)

184-205: Validation tests for required configuration fields.

These tests ensure that IllegalArgumentException is thrown when required fields (clientId, clientSecret, urlProvider) are missing. The error messages clearly indicate which field is missing.

Note: The assertFailsWith message parameter (e.g., "clientId must be specified") is a test failure message, not a matcher for the exception message. If you intend to verify the exception message content, use assertTrue(exception.message!!.contains(...)) pattern as in FetchOpenIdConfigurationTest.

📝 Alternative: Verify exception message content
`@Test`
fun testOAuthWithOpenIdConfigurationMissingClientId() {
    val openIdConfig = OpenIdConfiguration(...)

    val exception = assertFailsWith<IllegalArgumentException> {
        testApplication {
            install(Authentication) {
                oauth("test-openid", openIdConfig) {
                    client = this@testApplication.client
                    clientSecret = "test-client-secret"
                    urlProvider = { "http://localhost/callback" }
                }
            }
        }
    }
    assertTrue(exception.message!!.contains("clientId"))
}

Also applies to: 207-241

ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OpenIdConfiguration.kt (1)

108-130: Validation logic ensures required endpoints are present.

The validation checks for blank values rather than just null, which is appropriate since the JSON might contain empty strings. Consider reordering validations to match the property declaration order for consistency (authorizationEndpoint → tokenEndpoint → jwksUri).

♻️ Optional: Reorder validations to match property order
-    if (config.jwksUri.isBlank()) {
-        throw DiscoveryException("OpenID configuration from $issuer is missing jwks_uri")
-    }
     if (config.authorizationEndpoint.isBlank()) {
         throw DiscoveryException("OpenID configuration from $issuer is missing authorization_endpoint")
     }
     if (config.tokenEndpoint.isBlank()) {
         throw DiscoveryException("OpenID configuration from $issuer is missing token_endpoint")
     }
+    if (config.jwksUri.isBlank()) {
+        throw DiscoveryException("OpenID configuration from $issuer is missing jwks_uri")
+    }
     return config

Comment on lines +488 to +493
public static final fun oauth (Lio/ktor/server/auth/AuthenticationConfig;Ljava/lang/String;Lio/ktor/server/auth/OpenIdConfiguration;Lkotlin/jvm/functions/Function1;)V
public static final fun oauth (Lio/ktor/server/auth/AuthenticationConfig;Ljava/lang/String;Ljava/lang/String;Lio/ktor/server/auth/OpenIdConfiguration;Lkotlin/jvm/functions/Function1;)V
public static final fun oauth (Lio/ktor/server/auth/AuthenticationConfig;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
public static final fun oauth (Lio/ktor/server/auth/AuthenticationConfig;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
public static synthetic fun oauth$default (Lio/ktor/server/auth/AuthenticationConfig;Ljava/lang/String;Lio/ktor/server/auth/OpenIdConfiguration;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public static synthetic fun oauth$default (Lio/ktor/server/auth/AuthenticationConfig;Ljava/lang/String;Ljava/lang/String;Lio/ktor/server/auth/OpenIdConfiguration;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find the OAuth-related source files
fd -e kt -t f oauth | grep -E "(auth|oauth)" | head -20

Repository: ktorio/ktor

Length of output: 1041


🏁 Script executed:

# Search for oauth function definitions
rg "fun.*oauth" --type kt -A 5 | head -100

Repository: ktorio/ktor

Length of output: 81


🏁 Script executed:

# Look for the main oauth implementation in ktor-server-auth
find . -path "*/ktor-server-auth/*" -name "*.kt" -type f | head -20

Repository: ktorio/ktor

Length of output: 2083


🏁 Script executed:

cat ./ktor-server/ktor-server-plugins/ktor-server-auth/jvm/src/io/ktor/server/auth/OAuth.kt

Repository: ktorio/ktor

Length of output: 2486


🏁 Script executed:

cat ./ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OpenIdConfiguration.kt

Repository: ktorio/ktor

Length of output: 5695


🏁 Script executed:

# Search for oauth function definitions - extension functions
rg "fun.*oauth\(" --type-add 'kotlin:*.kt' -t kotlin | head -50

Repository: ktorio/ktor

Length of output: 602


🏁 Script executed:

# Check OAuth2.kt which likely contains the oauth extension functions
cat ./ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OAuth2.kt | head -200

Repository: ktorio/ktor

Length of output: 6610


🏁 Script executed:

cat ./ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OAuthProcedure.kt

Repository: ktorio/ktor

Length of output: 11535


🏁 Script executed:

# Check if there are any `@JvmName` annotations on the oauth functions
rg "@JvmName" ./ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OAuthProcedure.kt

Repository: ktorio/ktor

Length of output: 37


🏁 Script executed:

# Look for Java test files that might use the oauth functions
find ./ktor-server/ktor-server-plugins/ktor-server-auth -name "*.java" -type f

Repository: ktorio/ktor

Length of output: 37


🏁 Script executed:

# Check if there's an actual overload ambiguity by creating a test case
cat > /tmp/test_ambiguity.kt << 'EOF'
// Simulating the overloads from the actual code
class TestAmbiguity {
    interface Config
    interface OpenIdOAuthConfig
    data class OpenIdConfiguration(val issuer: String)
    
    fun oauth(
        config: Any,
        name: String? = null,
        configure: Config.() -> Unit
    ) {}
    
    fun oauth(
        config: Any,
        name: String? = null,
        description: String? = null,
        configure: Config.() -> Unit
    ) {}
    
    fun oauth(
        config: Any,
        name: String? = null,
        openIdConfiguration: OpenIdConfiguration,
        configure: OpenIdOAuthConfig.() -> Unit
    ) {}
    
    fun oauth(
        config: Any,
        name: String? = null,
        description: String? = null,
        openIdConfiguration: OpenIdConfiguration,
        configure: OpenIdOAuthConfig.() -> Unit
    ) {}
    
    fun test() {
        // Test calls
        oauth(Any(), "name") {} // Should match first overload
        oauth(Any(), "name", null) {} // POTENTIALLY AMBIGUOUS - String or OpenIdConfiguration?
        oauth(Any(), "name", null as String?) {} // Should match second overload
        oauth(Any(), "name", OpenIdConfiguration("issuer")) {} // Should match third overload
    }
}
EOF
cat /tmp/test_ambiguity.kt

Repository: ktorio/ktor

Length of output: 1283


🏁 Script executed:

# Let's check the .api file to see what synthetic defaults were generated
grep -A 20 "oauth\$default" ./ktor-server/ktor-server-plugins/ktor-server-auth/api/ktor-server-auth.api | head -30

Repository: ktorio/ktor

Length of output: 2122


🏁 Script executed:

# Let's look at the actual parameter types more carefully - check if OpenIdConfiguration is nullable
cat ./ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OAuthProcedure.kt | grep -A 10 "fun AuthenticationConfig.oauth"

Repository: ktorio/ktor

Length of output: 1700


🏁 Script executed:

# Also check the `@api` file more carefully around the oauth definitions to see full signatures
grep -B 5 -A 10 "fun oauth" ./ktor-server/ktor-server-plugins/ktor-server-auth/api/ktor-server-auth.api | head -50

Repository: ktorio/ktor

Length of output: 2176


🏁 Script executed:

# Let's see the exact lines around 488-493 in the api file
sed -n '480,500p' ./ktor-server/ktor-server-plugins/ktor-server-auth/api/ktor-server-auth.api

Repository: ktorio/ktor

Length of output: 2130


🏁 Script executed:

# Let's create a test to see if Kotlin actually allows calling with null and which overload gets picked
cat > /tmp/test_kotlin_overload.kt << 'EOF'
data class OpenIdConfiguration(val issuer: String)

fun test(
    name: String? = null,
    openIdConfiguration: OpenIdConfiguration,
    configure: () -> Unit
) {
    println("OpenIdConfiguration overload called")
}

fun test(
    name: String? = null,
    description: String? = null,
    configure: () -> Unit
) {
    println("String overload called")
}

fun main() {
    // This should be unambiguous because the 3rd parameter type is different
    test("name", OpenIdConfiguration("issuer")) {}
    
    // Can we call with null?
    // test("name", null) {} // This would be ambiguous!
}
EOF
cat /tmp/test_kotlin_overload.kt

Repository: ktorio/ktor

Length of output: 648


🏁 Script executed:

# Check if there are any existing tests for the oauth functions
find ./ktor-server/ktor-server-plugins/ktor-server-auth -name "*Test.kt" -o -name "*Test.java" | xargs grep -l "oauth" 2>/dev/null | head -5

Repository: ktorio/ktor

Length of output: 345


🏁 Script executed:

# Look at one of the test files to see how oauth is actually called
cat ./ktor-server/ktor-server-plugins/ktor-server-auth/common/test/io/ktor/tests/auth/OpenIdOAuthTest.kt | head -100

Repository: ktorio/ktor

Length of output: 3800


🏁 Script executed:

# Check if the oauth functions in OAuthProcedure.kt have any `@JvmName` annotations
grep -B 2 "fun AuthenticationConfig.oauth" ./ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OAuthProcedure.kt | grep -E "@JvmName|fun AuthenticationConfig"

Repository: ktorio/ktor

Length of output: 210


🏁 Script executed:

# Let's verify if Java can actually call these functions with null - check for Java test files
find ./ktor-server/ktor-server-plugins/ktor-server-auth -name "*Test.java" -type f

Repository: ktorio/ktor

Length of output: 37


🏁 Script executed:

# Check the exact signature by looking at what parameters are required
sed -n '56,170p' ./ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OAuthProcedure.kt

Repository: ktorio/ktor

Length of output: 5233


🏁 Script executed:

# Let's verify the actual overload ambiguity by checking what Java sees
# Look at the complete function signatures with their parameter counts
cat ./ktor-server/ktor-server-plugins/ktor-server-auth/api/ktor-server-auth.api | grep -A 2 "OAuthProcedureKt"

Repository: ktorio/ktor

Length of output: 338


🏁 Script executed:

# Test if Kotlin compiler would accept a call with null for the 3rd parameter
cat > /tmp/test_null_ambiguity.kt << 'EOF'
package test

data class OpenIdConfiguration(val issuer: String)

interface AuthenticationConfig

fun AuthenticationConfig.oauth(
    name: String? = null,
    openIdConfiguration: OpenIdConfiguration,
    configure: (Any) -> Unit
) {
    println("OpenId overload")
}

fun AuthenticationConfig.oauth(
    name: String? = null,
    description: String? = null,
    configure: (Any) -> Unit
) {
    println("String overload")
}

fun main() {
    val config: AuthenticationConfig = object : AuthenticationConfig {}
    
    // Test 1: These should be unambiguous
    config.oauth("name", OpenIdConfiguration("issuer")) {}  // Clear - OpenId overload
    config.oauth("name", "desc") {}  // Clear - String overload
    
    // Test 2: What about null?
    // config.oauth("name", null) {}  // Would this compile?
}
EOF
python3 << 'PYEOF'
# Since we can't actually compile Kotlin, let's reason through the JVM signatures
# and what Java would see

api_lines = [
    "oauth (Lio/ktor/server/auth/AuthenticationConfig;Ljava/lang/String;Lio/ktor/server/auth/OpenIdConfiguration;Lkotlin/jvm/functions/Function1;)V",
    "oauth (Lio/ktor/server/auth/AuthenticationConfig;Ljava/lang/String;Ljava/lang/String;Lio/ktor/server/auth/OpenIdConfiguration;Lkotlin/jvm/functions/Function1;)V",
    "oauth (Lio/ktor/server/auth/AuthenticationConfig;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V",
    "oauth (Lio/ktor/server/auth/AuthenticationConfig;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V",
]

print("JVM Signatures for oauth:")
print("=" * 80)
for i, sig in enumerate(api_lines, 1):
    # Parse parameter count
    params = sig.count("L")
    print(f"{i}. Parameters: {params}")
    print(f"   {sig}")
    print()

print("\nAnalysis:")
print("-" * 80)
print("In Java, a call oauth(config, \"name\", null, function) has 4 arguments")
print("\nCan match:")
print("  - Signature 1 (4 params): oauth(..., String, OpenIdConfiguration, Function1)")
print("  - Signature 3 (4 params): oauth(..., String, String, Function1)")
print("\nBoth accept null as 3rd parameter:")
print("  - OpenIdConfiguration (nullable)")
print("  - String (nullable)")
print("\n⚠️  This IS ambiguous in Java!")
PYEOF

Repository: ktorio/ktor

Length of output: 1215


Java overload ambiguity with 4-argument oauth calls.

The 4-parameter overloads oauth(AuthenticationConfig, String, OpenIdConfiguration, Function1) and oauth(AuthenticationConfig, String, String, Function1) create ambiguity when called from Java with a null third argument—the compiler cannot determine which overload to invoke since both accept null. Consider adding @JvmName annotations to disambiguate, e.g., @JvmName("oauthWithOpenId") on the OpenIdConfiguration overload.

🤖 Prompt for AI Agents
In `@ktor-server/ktor-server-plugins/ktor-server-auth/api/ktor-server-auth.api`
around lines 488 - 493, The two 4-arg Kotlin overloads
oauth(AuthenticationConfig, String, OpenIdConfiguration, Function1) and
oauth(AuthenticationConfig, String, String, Function1) cause Java-call ambiguity
when the third parameter is null; annotate the Kotlin overloads with distinct
JVM names (e.g., add `@JvmName`("oauthWithOpenId") to the OpenIdConfiguration
variant and `@JvmName`("oauthWithClientId") to the String-variant) so the
generated methods have unique JVM signatures and Java callers can unambiguously
invoke the desired overload; apply the annotations to the corresponding oauth
functions (and their default dispatchers if necessary) so the compiled API
exposes disambiguated method names.

Comment on lines +193 to +209
public fun AuthenticationConfig.oauth(
name: String? = null,
description: String? = null,
openIdConfiguration: OpenIdConfiguration,
configure: OpenIdOAuthConfig.() -> Unit
) {
val openIdConfig = OpenIdOAuthConfig(openIdConfiguration).apply(configure)
val urlProviderFn = requireNotNull(openIdConfig.urlProvider) {
"urlProvider must be specified"
}
oauth(name, description) {
urlProvider = urlProviderFn
fallback = openIdConfig.fallback
settings = openIdConfig.toServerSettings()
openIdConfig.client?.let { this.client = it }
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

git ls-files | grep -E "(OAuthProcedure|OAuthAuthenticationProvider)" | head -20

Repository: ktorio/ktor

Length of output: 152


🏁 Script executed:

cat -n ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OAuthProcedure.kt | sed -n '1,100p'

Repository: ktorio/ktor

Length of output: 4771


🏁 Script executed:

cat -n ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OAuthProcedure.kt | sed -n '50,70p'

Repository: ktorio/ktor

Length of output: 1194


🏁 Script executed:

cat -n ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OAuthProcedure.kt | sed -n '190,220p'

Repository: ktorio/ktor

Length of output: 1507


🏁 Script executed:

cat -n ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OAuthProcedure.kt | sed -n '100,193p'

Repository: ktorio/ktor

Length of output: 4780


🏁 Script executed:

# Search for other oauth function definitions to understand patterns
rg -A 8 'public fun AuthenticationConfig\.oauth' ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OAuthProcedure.kt

Repository: ktorio/ktor

Length of output: 1212


🏁 Script executed:

# Check if there's validation of client in build() or elsewhere
rg -B 3 -A 5 'fun build' ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OAuthProcedure.kt

Repository: ktorio/ktor

Length of output: 442


🏁 Script executed:

# Find OpenIdOAuthConfig class definition
rg -B 5 -A 20 'class OpenIdOAuthConfig' ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/

Repository: ktorio/ktor

Length of output: 3580


🏁 Script executed:

# Check if HttpClient() can be instantiated without parameters and what it creates
rg -B 3 -A 3 'HttpClient\(\)' ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OAuthProcedure.kt

Repository: ktorio/ktor

Length of output: 37


🏁 Script executed:

# Look for how HttpClient is typically instantiated in Ktor auth module
fd '\.kt$' ktor-server/ktor-server-plugins/ktor-server-auth/ --exec grep -l 'HttpClient()' {} \;

Repository: ktorio/ktor

Length of output: 157


🏁 Script executed:

# Check the toServerSettings() method to see if it handles default client
rg -B 5 -A 15 'fun toServerSettings' ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/

Repository: ktorio/ktor

Length of output: 3252


🏁 Script executed:

# Look at the full OpenIdOAuthConfig class
cat -n ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OpenIdOAuthConfig.kt

Repository: ktorio/ktor

Length of output: 7079


🏁 Script executed:

cat -n ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OAuthProcedure.kt | sed -n '203,209p'

Repository: ktorio/ktor

Length of output: 324


Uninitialized client property causes runtime crash when omitted from OpenID configuration.

When openIdConfig.client is null (the default), line 207 does not assign a client to the provider config. However, OAuthAuthenticationProvider.Config.client is a lateinit var (line 58) that the constructor (line 32) directly accesses without validation. The build() method does not validate client initialization, resulting in UninitializedPropertyAccessException at runtime.

Additionally, the OpenIdOAuthConfig documentation states the client "Defaults to a new [HttpClient] instance" (line 28), but this is not actually implemented.

Fix: Provide a default HttpClient() when client is not specified:

Proposed fix
     oauth(name, description) {
         urlProvider = urlProviderFn
         fallback = openIdConfig.fallback
         settings = openIdConfig.toServerSettings()
-        openIdConfig.client?.let { this.client = it }
+        this.client = openIdConfig.client ?: HttpClient()
     }
🤖 Prompt for AI Agents
In
`@ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OAuthProcedure.kt`
around lines 193 - 209, The oauth wrapper leaves
OAuthAuthenticationProvider.Config.client uninitialized when
OpenIdOAuthConfig.client is null, causing UninitializedPropertyAccessException;
fix by ensuring a default HttpClient is provided: in AuthenticationConfig.oauth
(the function in OAuthProcedure.kt) assign this.client = openIdConfig.client ?:
HttpClient() (or alternatively make OpenIdOAuthConfig initialize client to
HttpClient() by default) so the provider's lateinit client is always set before
build(); reference symbols: AuthenticationConfig.oauth, OpenIdOAuthConfig,
OAuthAuthenticationProvider.Config.client.

Comment on lines +63 to +72
/**
* OAuth scopes to request during authorization.
*
* If not set, defaults to intersection of ["openid", "profile", "email"] with
* [OpenIdConfiguration.scopesSupported]. If [OpenIdConfiguration.scopesSupported] is null,
* defaults to ["openid"].
*
* [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.server.auth.OpenIdOAuthConfig.defaultScopes)
*/
public var defaultScopes: List<String>? = null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Documentation doesn't match implementation for scope fallback behavior.

The KDoc states: "defaults to intersection of ['openid', 'profile', 'email'] with [OpenIdConfiguration.scopesSupported]"

However, the implementation at Line 132 uses scopesSupported directly without filtering:

val scopes = defaultScopes ?: openIdConfiguration.scopesSupported ?: listOf("openid")

Either update the documentation to reflect the actual behavior, or implement the intersection logic as documented.

📝 Option 1: Update documentation to match implementation
     /**
      * OAuth scopes to request during authorization.
      *
-     * If not set, defaults to intersection of ["openid", "profile", "email"] with
-     * [OpenIdConfiguration.scopesSupported]. If [OpenIdConfiguration.scopesSupported] is null,
-     * defaults to ["openid"].
+     * If not set, defaults to [OpenIdConfiguration.scopesSupported] if available,
+     * otherwise defaults to ["openid"].
      *
🤖 Prompt for AI Agents
In
`@ktor-server/ktor-server-plugins/ktor-server-auth/common/src/io/ktor/server/auth/OpenIdOAuthConfig.kt`
around lines 63 - 72, The KDoc for OpenIdOAuthConfig.defaultScopes says it
should default to the intersection of ["openid","profile","email"] with
OpenIdConfiguration.scopesSupported, but the code assigns
openIdConfiguration.scopesSupported directly; update the implementation where
`scopes` is computed (the `scopes = defaultScopes ?:
openIdConfiguration.scopesSupported ?: listOf("openid")` logic) to instead
compute `defaultScopes ?:
(openIdConfiguration.scopesSupported?.intersect(listOf("openid","profile","email"))?.toList())
?: listOf("openid")` so the effective scopes match the documented intersection
with `OpenIdConfiguration.scopesSupported`; alternatively, if you prefer to keep
current behavior, update the KDoc for `defaultScopes` to describe that it uses
`scopesSupported` directly and falls back to ["openid"].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants