Skip to content

Commit

Permalink
Suggestion for adding Retrofit support to API with minimal impact on …
Browse files Browse the repository at this point in the history
…existing solution:

* Only change to Client.kt is to make it override a ClientInterface
* Added RetrofitClientFacade to wrap normal client, that add Retrofit service support
* Retrofit service will be built when calling resumeLastLoggedInUser() RetrofitClientFacade
* All other client calls to RetrofitClientFacade will by proxied to the normal Client instance
* For Retrofit clients, makeAuthenticatedRequest() should be caused on RetrofitClientFacade instance and not on User object
  • Loading branch information
Anders Carlsen committed Sep 27, 2021
1 parent 605bb1e commit c034350
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ sealed class RefreshTokenError {
typealias LoginResultHandler = (Either<LoginError, User>) -> Unit

/** Represents a client registered with Schibsted account. */
class Client {
class Client : ClientInterface {
internal val httpClient: OkHttpClient
internal val schibstedAccountApi: SchibstedAccountApi
internal val configuration: ClientConfiguration
Expand Down Expand Up @@ -133,7 +133,7 @@ class Client {
* @param authRequest Authentication request parameters.
*/
@JvmOverloads
fun getAuthenticationIntent(context: Context, authRequest: AuthRequest = AuthRequest()): Intent {
override fun getAuthenticationIntent(context: Context, authRequest: AuthRequest): Intent {
val customTabsIntent = CustomTabsIntent.Builder().build().apply {
intent.data = generateLoginUrl(authRequest)
}
Expand All @@ -146,7 +146,7 @@ class Client {
* @param authRequest Authentication request parameters.
*/
@JvmOverloads
fun launchAuth(context: Context, authRequest: AuthRequest = AuthRequest()) {
override fun launchAuth(context: Context, authRequest: AuthRequest) {
CustomTabsIntent.Builder()
.build()
.launchUrl(context, generateLoginUrl(authRequest))
Expand All @@ -164,7 +164,7 @@ class Client {
* This only needs to be used if manually starting the login flow using [launchAuth].
* If using [getAuthenticationIntent] this step will be handled for you.
*/
fun handleAuthenticationResponse(intent: Intent, callback: LoginResultHandler) {
override fun handleAuthenticationResponse(intent: Intent, callback: LoginResultHandler) {
val authResponse = intent.data?.query ?: return callback(
Left(LoginError.UnexpectedError("No authentication response"))
)
Expand Down Expand Up @@ -223,7 +223,7 @@ class Client {
}

/** Resume any previously logged-in user session */
fun resumeLastLoggedInUser(): User? {
override fun resumeLastLoggedInUser(): User? {
val session = sessionStorage.get(configuration.clientId) ?: return null
return User(this, session.userTokens)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.schibsted.account.webflows.client

import android.content.Context
import android.content.Intent
import com.schibsted.account.webflows.activities.AuthorizationManagementActivity
import com.schibsted.account.webflows.user.User

interface ClientInterface {
/**
* Start login flow.
*
* Requires [AuthorizationManagementActivity.setup] to have been called before this.
*
* @param authRequest Authentication request parameters.
*/
fun getAuthenticationIntent(context: Context, authRequest: AuthRequest = AuthRequest()): Intent

/**
* Start auth activity manually.
*
* @param authRequest Authentication request parameters.
*/
fun launchAuth(context: Context, authRequest: AuthRequest = AuthRequest())

/**
* Call this with the intent received via deep link to complete the login flow.
*
* This only needs to be used if manually starting the login flow using [launchAuth].
* If using [getAuthenticationIntent] this step will be handled for you.
*/
fun handleAuthenticationResponse(intent: Intent, callback: LoginResultHandler)

/** Resume any previously logged-in user session */
fun resumeLastLoggedInUser(): User?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.schibsted.account.webflows.client

import com.schibsted.account.webflows.user.User
import retrofit2.Retrofit

/** Represents a client registered with Schibsted account - with Retrofit support. */
class RetrofitClientFacade<S>(
private val client: Client,
private val serviceClass: Class<S>,
private val retrofitBuilder: Retrofit.Builder,
): ClientInterface by client {

private var retrofitApi: S? = null
private var user: User? = null

/** Resume any previously logged-in user session */
override fun resumeLastLoggedInUser(): User? {
val loggedInUser = client.resumeLastLoggedInUser()

if (loggedInUser != null) {
user = loggedInUser
retrofitApi = retrofitBuilder
.client(loggedInUser.httpClient)
.build()
.create(serviceClass)

return loggedInUser
}

user = null
retrofitApi = null
return null
}

/**
* Perform the given [request] with user access token as Bearer token in Authorization header.
*
* If the initial request fails with a 401, a refresh token request is made to get a new access
* token and the request will be retried with the new token if successful.
*
* If the refresh token request fails with an OAuth 'invalid_grant' error response, meaning the
* refresh token has expired or been invalidated, the user will be logged-out (and all existing
* tokens will be destroyed).
*
* @param request retrofit suspend request to perform with authentication using user tokens
*/
suspend fun <V> makeAuthenticatedRequest(request: suspend (S) -> V?): V? {
if (user?.isLoggedIn() == true) {
retrofitApi?.let {
return request(it)
}
}
return null
}
}

0 comments on commit c034350

Please sign in to comment.