Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,12 @@ class AuthActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val authContext = intent.getParcelableExtra<AuthContext>(AUTH_CONTEXT)
authContext ?.let {
authProvider = AuthProvider(this, authContext)
} ?: run {
val responseIntent = Intent().apply { putExtra("EXTRA_ERROR", "AUTH_CONTEXT was null") }
setResult(RESULT_CANCELED, responseIntent)
finish()
}
authContext?.let { authProvider = AuthProvider(this, authContext) }
?: run {
val responseIntent = Intent().apply { putExtra("EXTRA_ERROR", "AUTH_CONTEXT was null") }
setResult(RESULT_CANCELED, responseIntent)
finish()
}
}

private fun startAuth() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,26 @@ class AuthProvider(

private suspend fun sendPushedAuthorizationRequest(ssoConfig: SsoConfig) =
authContext.prefillInfo?.let {
val response =
authService.loginParRequest(
ssoConfig.clientId,
RESPONSE_TYPE,
Base64Util.encodePrefillInfoToString(it),
ssoConfig.scope ?: "profile",
)
val body = response.body()
body?.takeIf { response.isSuccessful }
?: throw AuthException.ServerError("Bad response ${response.code()}")
try {
val response =
authService.loginParRequest(
ssoConfig.clientId,
RESPONSE_TYPE,
Base64Util.encodePrefillInfoToString(it),
ssoConfig.scope ?: "profile",
)
val body = response.body()
body?.takeIf { response.isSuccessful }
?: run {
// PAR request failed, but continue authentication without metadata
// User can still login, just without pre-filled information
PARResponse("", "")
}
} catch (e: Exception) {
// PAR request failed due to network or other error
// Continue authentication without metadata for better user experience
PARResponse("", "")
}
} ?: PARResponse("", "")

private fun getQueryParams(parResponse: PARResponse) = buildMap {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,85 @@ class AuthProviderTest : RobolectricTestBase() {
assert(result is AuthResult.Success)
assert((result as AuthResult.Success).uberToken.authCode == "authCode")
}

@Test
fun `test authenticate when PAR request fails should continue without metadata`() = runTest {
whenever(ssoLink.execute(any())).thenReturn("authCode")
// Mock PAR request to return error response (e.g., 500)
val errorResponse: Response<PARResponse> = Response.error(500, mock())
whenever(authService.loginParRequest(any(), any(), any(), any())).thenReturn(errorResponse)
val prefillInfo = PrefillInfo("email", "firstName", "lastName", "phoneNumber")
val authContext =
AuthContext(
AuthDestination.CrossAppSso(listOf(CrossApp.Rider)),
AuthType.AuthCode,
prefillInfo,
)
val authProvider = AuthProvider(activity, authContext, authService, codeVerifierGenerator)
val argumentCaptor = argumentCaptor<Map<String, String>>()
val result = authProvider.authenticate()
// Verify PAR request was attempted
verify(authService).loginParRequest(any(), any(), any(), any())
// Verify authentication continued without request_uri
verify(ssoLink).execute(argumentCaptor.capture())
assert(argumentCaptor.lastValue.containsKey(REQUEST_URI).not())
// Verify authentication succeeded
assert(result is AuthResult.Success)
assert((result as AuthResult.Success).uberToken.authCode == "authCode")
}

@Test
fun `test authenticate when PAR request throws exception should continue without metadata`() =
runTest {
whenever(ssoLink.execute(any())).thenReturn("authCode")
// Mock PAR request to throw network exception
whenever(authService.loginParRequest(any(), any(), any(), any()))
.thenThrow(RuntimeException("Network error"))
val prefillInfo = PrefillInfo("email", "firstName", "lastName", "phoneNumber")
val authContext =
AuthContext(
AuthDestination.CrossAppSso(listOf(CrossApp.Rider)),
AuthType.AuthCode,
prefillInfo,
)
val authProvider = AuthProvider(activity, authContext, authService, codeVerifierGenerator)
val argumentCaptor = argumentCaptor<Map<String, String>>()
val result = authProvider.authenticate()
// Verify PAR request was attempted
verify(authService).loginParRequest(any(), any(), any(), any())
// Verify authentication continued without request_uri
verify(ssoLink).execute(argumentCaptor.capture())
assert(argumentCaptor.lastValue.containsKey(REQUEST_URI).not())
// Verify authentication succeeded
assert(result is AuthResult.Success)
assert((result as AuthResult.Success).uberToken.authCode == "authCode")
}

@Test
fun `test authenticate with PKCE when PAR fails should still include code challenge`() = runTest {
whenever(ssoLink.execute(any())).thenReturn("authCode")
whenever(codeVerifierGenerator.generateCodeVerifier()).thenReturn("verifier")
whenever(codeVerifierGenerator.generateCodeChallenge("verifier")).thenReturn("challenge")
whenever(authService.token(any(), any(), any(), any(), any()))
.thenReturn(Response.success(UberToken(accessToken = "accessToken")))
// Mock PAR request to fail
val errorResponse: Response<PARResponse> = Response.error(500, mock())
whenever(authService.loginParRequest(any(), any(), any(), any())).thenReturn(errorResponse)
val prefillInfo = PrefillInfo("email", "firstName", "lastName", "phoneNumber")
val authContext =
AuthContext(AuthDestination.CrossAppSso(listOf(CrossApp.Rider)), AuthType.PKCE(), prefillInfo)
val authProvider = AuthProvider(activity, authContext, authService, codeVerifierGenerator)
val argumentCaptor = argumentCaptor<Map<String, String>>()
val result = authProvider.authenticate()
// Verify PAR request was attempted
verify(authService).loginParRequest(any(), any(), any(), any())
// Verify authentication continued with code challenge but without request_uri
verify(ssoLink).execute(argumentCaptor.capture())
assert(argumentCaptor.lastValue.containsKey(REQUEST_URI).not())
assert(argumentCaptor.lastValue[CODE_CHALLENGE_PARAM] == "challenge")
assert(argumentCaptor.lastValue[CODE_CHALLENGE_METHOD] == CODE_CHALLENGE_METHOD_VAL)
// Verify authentication succeeded
assert(result is AuthResult.Success)
assert((result as AuthResult.Success).uberToken.accessToken == "accessToken")
}
}
2 changes: 1 addition & 1 deletion samples/login-with-auth-code-demo/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
description=Login to Uber Sample
UBER_CLIENT_ID=insert_your_client_id_here
UBER_REDIRECT_URI=insert_your_redirect_uri_here
UBER_REDIRECT_URI=insert_your_redirect_uri_here