babbel/okhttp-aws-signer

Get Response String/JSON

Gorebar opened this issue · 6 comments

How to get the response JSON or String from this signed request?

s-ka commented

Hey,
this library depends on OkHttp. You usually use it along with Retrofit.
We do the signing with the help of a okhttp3.Interceptor which you can add to Retrofit on initialization.

Do you need more information?

Hey,
this library depends on OkHttp. You usually use it along with Retrofit.
We do the signing with the help of a okhttp3.Interceptor which you can add to Retrofit on initialization.

Do you need more information?

Could you send me a working example singing and getting the response JSON from a request? This is making me crazy...

Always I am getting as unusual canonical string.

s-ka commented

I don't have a example lying around here, but this is how our interceptor looks like

class AwsInterceptor constructor(
    private val credentialsProvider: CredentialsProvider,
    private val signer: OkHttpAwsV4Signer
) : Interceptor {
    private val dateFormat: ThreadLocal<SimpleDateFormat>

    init {
        dateFormat = object : ThreadLocal<SimpleDateFormat>() {
            override fun initialValue(): SimpleDateFormat {
                val localFormat = SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.US)
                localFormat.timeZone = TimeZone.getTimeZone("UTC")
                return localFormat
            }
        }
    }

    override fun intercept(chain: Interceptor.Chain): Response =
        chain.run {
            val newRequest = request().newBuilder()
                .addRequiredHeaders(request().host)
                .build()
                .sign()

            proceed(newRequest)
        }

    /**
     * Some headers are needed for the signing to work. This method makes sure they're part
     * of the request
     */
    private fun Request.Builder.addRequiredHeaders(host: String) =
        apply {
            header("x-amz-date", dateFormat.format(Date())
            header("host", host)
            val sessionToken = credentialsProvider.sessionToken
            if (!sessionToken.isNullOrBlank())
                header("x-amz-security-token", sessionToken)
        }

    private fun Request.sign() =
        signer.sign(
            this,
            credentialsProvider.accessKeyId,
            credentialsProvider.secretAccessKey
        )

    private fun ThreadLocal<SimpleDateFormat>.format(date: Date): String = get()!!.format(date)

    private val Request.host: String
        get() = url().host()
}

Then you you need to create your Retrofit instance somehow like this

val okhttpClient = OkHttpClient.Builder()
    .addInterceptor(AwsInterceptor(...)) // add your dependencies
    .build()

val retrofit = Retrofit.Builder()
    .baseUrl(baseUrl)
    .client(okHttpClient)
    .addConverterFactory(MoshiConverterFactory.create()) // We use Moshi for the json deserialization
    .addCallAdapterFactory(ErrorCallAdapterFactory
        .create(RxJava2CallAdapterFactory.create()))
    .build();

And the rest is how you use Retrofit usually.
I hope that helps?

The same error... Im getting always the next response:

"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details."

The canonical String is wrong.

s-ka commented

Are you sure you are using the right credentials?
I guess this can vary on our setup, but this is how it works for us:

On login we get the auth token of the user.
With the auth token we can query for the accessKeyId, secretAccessKey and a short lived sessionToken from our backend, which we use to sign the requests as you can see above.
The session token expires after a while, for this case we have another intercepter which listens for this error and refreshes the signing credentials.

It is working now, I was using wrong credentials. Thank you so much.