diff --git a/authentication/src/main/kotlin/com/uber/sdk2/auth/internal/AuthProvider.kt b/authentication/src/main/kotlin/com/uber/sdk2/auth/internal/AuthProvider.kt index fa23881..4648339 100644 --- a/authentication/src/main/kotlin/com/uber/sdk2/auth/internal/AuthProvider.kt +++ b/authentication/src/main/kotlin/com/uber/sdk2/auth/internal/AuthProvider.kt @@ -117,6 +117,7 @@ class AuthProvider( private fun getQueryParams(parResponse: PARResponse) = buildMap { parResponse.requestUri.takeIf { it.isNotEmpty() }?.let { put(REQUEST_URI, it) } authContext.prompt?.let { put(UriConfig.PROMPT_PARAM, it.value) } + authContext.nonce?.let { put(UriConfig.NONCE_PARAM, it) } if (authContext.authType is AuthType.PKCE) { val codeChallenge = codeVerifierGenerator.generateCodeChallenge(verifier) put(CODE_CHALLENGE_PARAM, codeChallenge) diff --git a/authentication/src/main/kotlin/com/uber/sdk2/auth/request/AuthContext.kt b/authentication/src/main/kotlin/com/uber/sdk2/auth/request/AuthContext.kt index 12b62df..ccc2205 100644 --- a/authentication/src/main/kotlin/com/uber/sdk2/auth/request/AuthContext.kt +++ b/authentication/src/main/kotlin/com/uber/sdk2/auth/request/AuthContext.kt @@ -35,6 +35,10 @@ import kotlinx.parcelize.Parcelize * @param environment The [UriConfig.UberEnvironment] to target for OAuth flows. Defaults to * [UriConfig.UberEnvironment.PRODUCTION]. Use [UriConfig.UberEnvironment.SANDBOX] to target * Uber's sandbox environment for development and testing. + * @param nonce An optional, opaque, single-use string sent on the `/authorize` request. Required by + * the server when `openid` is one of the requested scopes; the same value is returned as the + * `nonce` claim of the issued ID token and must be validated by the caller's backend to mitigate + * token replay. The SDK does not generate, store, or validate the value — it only forwards it. */ @Parcelize data class AuthContext @@ -45,4 +49,5 @@ constructor( val prefillInfo: PrefillInfo? = null, val prompt: Prompt? = null, val environment: UriConfig.UberEnvironment = UriConfig.UberEnvironment.PRODUCTION, + val nonce: String? = null, ) : Parcelable diff --git a/authentication/src/test/kotlin/com/uber/sdk2/auth/internal/AuthProviderTest.kt b/authentication/src/test/kotlin/com/uber/sdk2/auth/internal/AuthProviderTest.kt index eb0a35b..ae43fa1 100644 --- a/authentication/src/test/kotlin/com/uber/sdk2/auth/internal/AuthProviderTest.kt +++ b/authentication/src/test/kotlin/com/uber/sdk2/auth/internal/AuthProviderTest.kt @@ -202,6 +202,40 @@ class AuthProviderTest : RobolectricTestBase() { assert((result as AuthResult.Success).uberToken.authCode == "authCode") } + @Test + fun `test authenticate when nonce is present should forward it as query param`() = runTest { + whenever(ssoLink.execute(any())).thenReturn("authCode") + whenever(authService.loginParRequest(any(), any(), any(), any())) + .thenReturn(Response.success(PARResponse("requestUri", "codeVerifier"))) + val authContext = + AuthContext( + AuthDestination.CrossAppSso(listOf(CrossApp.Rider)), + AuthType.AuthCode, + null, + nonce = "n-0S6_WzA2Mj", + ) + val authProvider = AuthProvider(activity, authContext, authService, codeVerifierGenerator) + val argumentCaptor = argumentCaptor>() + val result = authProvider.authenticate() + verify(ssoLink).execute(argumentCaptor.capture()) + assert(argumentCaptor.lastValue[UriConfig.NONCE_PARAM] == "n-0S6_WzA2Mj") + assert(result is AuthResult.Success) + } + + @Test + fun `test authenticate when nonce is absent should not include nonce query param`() = runTest { + whenever(ssoLink.execute(any())).thenReturn("authCode") + whenever(authService.loginParRequest(any(), any(), any(), any())) + .thenReturn(Response.success(PARResponse("requestUri", "codeVerifier"))) + val authContext = + AuthContext(AuthDestination.CrossAppSso(listOf(CrossApp.Rider)), AuthType.AuthCode, null) + val authProvider = AuthProvider(activity, authContext, authService, codeVerifierGenerator) + val argumentCaptor = argumentCaptor>() + authProvider.authenticate() + verify(ssoLink).execute(argumentCaptor.capture()) + assert(argumentCaptor.lastValue.containsKey(UriConfig.NONCE_PARAM).not()) + } + @Test fun `test authenticate when PAR request fails should continue without metadata`() = runTest { whenever(ssoLink.execute(any())).thenReturn("authCode") diff --git a/core/src/main/kotlin/com/uber/sdk2/core/config/UriConfig.kt b/core/src/main/kotlin/com/uber/sdk2/core/config/UriConfig.kt index ddb1a20..b8c362a 100644 --- a/core/src/main/kotlin/com/uber/sdk2/core/config/UriConfig.kt +++ b/core/src/main/kotlin/com/uber/sdk2/core/config/UriConfig.kt @@ -89,4 +89,5 @@ object UriConfig { const val CODE_CHALLENGE_METHOD = "code_challenge_method" const val CODE_CHALLENGE_METHOD_VAL = "S256" const val PROMPT_PARAM = "prompt" + const val NONCE_PARAM = "nonce" }