TL;DR: with colleagues from Ackee we developed RxOauth2, an open source library to handle OAuth with RxJava streams. You may take a look at it directly or read this blog post (part 1 is here) to find out why and how we implemented it.
In almost every Ackee project we have so called
ApiInteractor, it is a layer between Retrofit API description (endpoints declaration layer) and repositories working with API requests. When using the first versions of RxOauth library, we had to separately wrap each of our requests with refresh token logic. We wanted to create a solution, which will integrate this logic to our API calls automatically by default.
It was Java time and annotation processors were on the peak of popularity. So we came up with a solution to generate a wrapper on top of
ApiDescription(Retrofit interface with endpoints declaration) during build time. We decided to take more general approach and created RxWrapper, a library that allows to generate a wrapper class around interface methods that return
Completable. That wrapper then applies the custom
Transformer to each of those methods via
compose() operator. To exclude a method from applying the
NoCompose annotation is used.
You may still explore and use RxWrapper library, but we stepped back from using it due to a couple of reasons:
- The wrapper is generated during build time, so the library user has to run the build at least once to be able to use the wrapper
- The wrapper is generated in Java, after Kotlin adoption we weren’t able to use its main features: optional values, default values or nullability
Rewriting the wrapper to Kotlin seemed more complex and we decided to look into another way to achieve required functionality. That’s how
RxOauthCallAdapter came into life.
To convert API
Call into arbitrary type Retrofit uses an interface called
CallAdapter. Retrofit itself has a couple of adapter implementations, for example
RxJava2CallAdapter allows us to work with API requests as RxJava2 streams. Initializing the
CallAdapter is done by setting the
CallAdapter.Factory to the
Retrofit.Builder when building the
The cool thing about this decoupled architecture that it is possible to write your own
CallAdapter.Factory implementation and do whatever you wand with requests handling. For example convert them to RxJava2 streams andwrap with OAuth logic. Unfortunately
RxJava2CallAdapter as well as its factory are final, that’s why we almost copied its functionality to create
RxOauthCallAdapterFactory and added our wrapping. Now there is no need to work with each request manually or generate redundant classes, all the library user has to do is to add our custom factory to Retrofit client. Together with setting the
OAuthInterceptor it will look like this:
val apiDescription: ApiDescription = retrofitBuilder .client(OkHttpClient.Builder() .addNetworkInterceptor(rxOAuthManager.provideAuthInterceptor()) .build()) .addCallAdapterFactory(RxOauthCallAdapterFactory(rxOAuthManager)) .build() .create(ApiDescription::class.java)
Now all our requests inside
ApiDescription will be wrapped with OAuth logic.
To exclude some requests from OAuth wrapping (registration, login etc.) you ma use
IgnoreAuth annotation, which should be added to
@IgnoreAuth @POST("signup") fun signUp(): Single<SignUpResponse>