Search code examples
androidkotlintwitteroauthtwitter-oauth

Calculating oauth_signature and oauth_nonce in Kotlin


I'm developing a twitter android app that has sign-in with twitter feature. Sign-in works great. But my problem is that i want to search for users. I googled it and find the endpoint.

https://api.twitter.com/1.1/users/search.json

Then i created a retrofit function to get searched users:

@GET("users/search.json")
suspend fun searchUser(
    @Header("authorization") header: String,
    @Query("q") search: String
) : Response<List<InspectedUserPOJO>>

Then called it from fragment like that:

CoroutineScope(viewModelJob).launch {
            withContext(IO) {
                try {
                    val response = RetrofitInstance.retrofitInstance!!.searchUser(generateHeader(),"teyyihan")
                    if (response.isSuccessful) {
                        if (response.body() != null) {
                           // getting response
                        } else {
                        }
                    } else {} } catch (e: Exception) {}
            }

        }

Here is generateHeader() function:

fun generateHeader(): String{
    var header = "OAuth oauth_consumer_key=\""+/* my consumer key*/+"\",oauth_token=\""+CurrentUserInfo.authToken+"\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"1580300189\",oauth_nonce=\"iBryT2SIbXa\",oauth_version=\"1.0\",oauth_signature=\"EvMhzhOKw%2BVhRKw5Iz%2F48VdWdlQ%3D\""
    return header
}

Actually this header is from postman. I tested this header and it worked great. However everytime i sent a request oauth_nonce and oauht_signature changes. I tried to change oauth_token with my oauht_token but it doesn't work. So how can i calculate oauth_nonce and oauth_signature?

Also i worked with java for 1 year if you know the java way please answer me.


Solution

  • I found a solution, it uses retrofit and you don't need to explicitly calculate oauth_nonce or oauth_signature.

    First you need to implement these libraries:

    implementation 'se.akerfeldt:okhttp-signpost:1.1.0'
    implementation 'com.squareup.okhttp3:okhttp:3.0.0-RC1'
    implementation 'oauth.signpost:signpost-core:1.2.1.2'
    implementation("com.squareup.okhttp3:logging-interceptor:4.3.1")
    

    We will have a class named RetrofitClient:

    class RetrofitClient {
    companion object {
        private var retrofit: Retrofit? = null
        private val gSON = GsonBuilder()
            .setLenient()
            .create()
    
        fun getClient(baseUrl: String, consumer: OkHttpOAuthConsumer): Retrofit? {
            val logging = HttpLoggingInterceptor()
            if (BuildConfig.DEBUG) {
                logging.level = HttpLoggingInterceptor.Level.BODY
            } else {
                logging.level = HttpLoggingInterceptor.Level.NONE
            }
    
            val httpClient = OkHttpClient.Builder()
            httpClient.connectTimeout(60000, TimeUnit.SECONDS)
            httpClient.writeTimeout(120000, TimeUnit.SECONDS)
            httpClient.readTimeout(120000, TimeUnit.SECONDS)
            httpClient.retryOnConnectionFailure(true)
            httpClient.addInterceptor(SigningInterceptor(consumer))
            httpClient.addInterceptor { chain ->
                val request = chain.request()
                val requestBuilder = request.newBuilder()
                val modifiedRequest = requestBuilder.build()
                chain.proceed(modifiedRequest)
            }
    
            httpClient.addNetworkInterceptor(logging)
    
            if (retrofit == null) {
                retrofit = Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addConverterFactory(GsonConverterFactory.create(gSON))
                    .client(httpClient.build())
                    .build()
            }
            return retrofit
        }
    
    }
    

    }

    And finally in your Retrofit interface create a companion object:

    companion object {
        fun getOauthAPIService(): RetrofitTwitterAPI? {
            val consumer = OkHttpOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET)
            consumer.setTokenWithSecret(CurrentUserInfo.authToken, CurrentUserInfo.authTokenSecret)
            return RetrofitClient.getClient(BASE_URL, consumer)?.create(YOUR_RETROFIT_INTERFACE::class.java)
        }
    
    }
    

    So you can call YOUR_RETROFIT_INTERFACE.getOauthAPIService() and make https calls. Retrofit and all these codes will handle extra headers like oauht_nonce, oauth_signature etc.