api – Kotlin Android oauth2 token request only returning errors


I am working on an user app for a local charitable organization, and need to access their API. The API is from wild apricot, and this is the documentation for making a token request:

Authentication tokens are obtained from Wild Apricot’s authentication service, which is located at https://oauth.wildapricot.org. This service adheres to oAuth 2.0.

This is the access option I need to implement:

—————–In order to obtain access token with API key, you have to make the following request:

POST /auth/token HTTP/1.1

Host: oauth.wildapricot.org

Authorization: Basic BASE64_ENCODED(“APIKEY:YOUR_API_KEY”)

Content-type: application/x-www-form-urlencoded

grant_type=client_credentials&scope=auto

——————————-So. finally your request will look like:

POST /auth/token HTTP/1.1

Host: oauth.wildapricot.org

Authorization: Basic QVBJS0VZOm85c2U4N3Jnb2l5c29lcjk4MDcwOS0=

Content-type: application/x-www-form-urlencoded

grant_type=client_credentials&scope=auto

I am attempting to make this call with retrofit2, and an okhttp3 interceptor, and getting a bad request response (I am very much new and learning, and have not been able to get anything other response than a 400 bad request (when I use “/auth/token” as the endpoint), or a 404 not found (when I use “/auth/token HTTP/1.1” as the endpoint). If someone could tell me where exactly I am messing this up It would be greatly appreciated, the code I have tried is below.

enter code here

Interface:

interface WAApiCall {

@POST("auth/token")
fun callPost(@Body body:String ): Call<AuthToken>

}

Call Service:

object WAApiCallService {

private const val API_KEY = "xxxxxxxxIxHavexAxValidxKeyxxxx"
private const val BASE_URL = "https://oauth.wildapricot.org/"
private val AUTH = "Basic" + Base64.encodeToString(API_KEY.toByteArray(), Base64.NO_WRAP) 
private const val CONTENT_TYPE = "application/x-www-form-urlencoded"


private var api:WAApiCall? = null

private fun getWAApi(context: Context) : WAApiCall {

    if(api==null){
        val OkHttpClient = OkHttpClient.Builder()
        val logging = HttpLoggingInterceptor()
        logging.level = HttpLoggingInterceptor.Level.BASIC

        OkHttpClient.addInterceptor{chain ->
           
            val request = chain.request()
            Log.d("CALL", request.body.toString())
            
            val newRequest = request.newBuilder()
                .addHeader("Host", "oauth.wildapricot.org")
                .addHeader("Authorization", AUTH ) 
                .addHeader("Content-type", CONTENT_TYPE)
                .method(request.method, request.body)
                .build()
            chain.proceed(newRequest)
        }

        api = Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(OkHttpClient.build())
            .build()
            .create(WAApiCall::class.java)
    }

    return api!!

}

fun call(context: Context) =
    getWAApi(context)

}

Function in Main Activity to make the call:

fun testRequest(){
val call = WAApiCallService.call(this)
call.callPost("grant_type=client_credentials&scope=auto")
    .enqueue(object: Callback<AuthToken>{
        override fun onFailure(call: Call<AuthToken>, t: Throwable) {
            Log.i("FAILURE", t.localizedMessage)
        }

        override fun onResponse(call: Call<AuthToken>, response: Response<AuthToken>) {
            Log.i("SUCCESS", "TOKEN = ${response.body().toString()}")
            Log.i("SUCCESS", "${response}")
            val token = response.body()?.accessToken
            Log.i("SUCCESS", "TOKEN = $token")
        }
    })

}

Error message:

I/SUCCESS: TOKEN = null
I/SUCCESS: Response{protocol=http/1.1, code=400, message=Bad Request,   url=https://oauth.wildapricot.org/auth/token}

I think that I am just not understanding how to implement this type of request in some basic way, I could not get it to work in Postman either. I understand that I need to send the credentials to the authentication server, and receive an access token, that will expire and need to be refreshed, and that It will be included in each actual API endpoint call, I guess I’m just missing something crucial in the most important step of that process (getting the actual token, I am imagining it is a simple, forehead slapping kind of misunderstanding on my part?). The wild apricot API is on swagger hub, and I am able to gain access through that UI, with my API key, and see the responses, so I know that it is valid.