简体   繁体   English

Kotlin Android oauth2 令牌请求仅返回错误

[英]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.我正在为当地慈善组织开发用户应用程序,需要访问他们的 API。 The API is from wild apricot, and this is the documentation for making a token request: API来自野杏,这是进行令牌请求的文档:

Authentication tokens are obtained from Wild Apricot's authentication service, which is located at https://oauth.wildapricot.org .身份验证令牌是从 Wild Apricot 的身份验证服务获得的,该服务位于https://oauth.wildapricot.org This service adheres to oAuth 2.0.此服务遵循 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: -----------------为了使用 API 密钥获取访问令牌,您必须发出以下请求:

POST /auth/token HTTP/1.1 POST /auth/token HTTP/1.1

Host: oauth.wildapricot.org主持人:oauth.wildapricot.org

Authorization: Basic BASE64_ENCODED("APIKEY:YOUR_API_KEY")授权:基本 BASE64_ENCODED("APIKEY:YOUR_API_KEY")

Content-type: application/x-www-form-urlencoded内容类型:application/x-www-form-urlencoded

grant_type=client_credentials&scope=auto grant_type=client_credentials&scope=auto

-------------------------------So. - - - - - - - - - - - - - - - -所以。 finally your request will look like:最后,您的请求将如下所示:

POST /auth/token HTTP/1.1 POST /auth/token HTTP/1.1

Host: oauth.wildapricot.org主持人:oauth.wildapricot.org

Authorization: Basic QVBJS0VZOm85c2U4N3Jnb2l5c29lcjk4MDcwOS0=授权:基本 QVBJS0VZOm85c2U4N3Jnb2l5c29lcjk4MDcwOS0=

Content-type: application/x-www-form-urlencoded内容类型:application/x-www-form-urlencoded

grant_type=client_credentials&scope=auto 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.我正在尝试使用 Retrofit2 和 okhttp3 拦截器进行此调用,并获得了错误的请求响应(我非常陌生并且正在学习,除了 400 个错误的请求(当我使用“ /auth/token" 作为端点),或者找不到 404(当我使用“/auth/token HTTP/1.1”作为端点时)。如果有人能告诉我我到底在哪里搞砸了这将不胜感激,我试过的代码如下。

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: Main Activity 中的函数进行调用:

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.我想我只是不明白如何以某种基本方式实现这种类型的请求,我也无法让它在 Postman 中工作。 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?).我知道我需要将凭据发送到身份验证服务器,并接收一个访问令牌,该令牌将过期并需要刷新,并且它将包含在每个实际的 API 端点调用中,我想我只是错过了一些东西在该过程中最重要的步骤中至关重要(获得实际令牌,我想这是一个简单的、拍打额头的误会?)。 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. Wild apricot API 位于 swagger hub 上,我可以使用我的 API 密钥通过该 UI 访问并查看响应,因此我知道它是有效的。

Your client credentials request looks mostly all good.您的客户端凭据请求看起来大部分都很好。 The only thing I can see that looks wrong is no space character in the AUTH header between 'Basic' and the encoded credential.我能看到的唯一错误是 AUTH 标头中“基本”和编码凭据之间没有空格字符。

If that doesn't work, could you trace the HTTP request and verify that you are sending the message you think you are.如果这不起作用,您能否跟踪 HTTP 请求并验证您正在发送您认为的消息。

Thank you for that observation, it led me to figuring out what ultimately was wrong in my initial attempt.谢谢你的观察,它让我弄清楚了我最初的尝试最终出了什么问题。 After adding that space, I traced the request and found that It was actually sending two headers for content type.添加该空间后,我跟踪请求,发现它实际上发送了两个内容类型的标头。

The fix for that was to set the header in the retrofit call from the interface:解决方法是在接口的改造调用中设置标头:

interface WAApiCall {
@POST("auth/token")
fun callPost(@Body Body: okhttp3.RequestBody, @Header("Content-type") type: String): Call<AuthToken>
}

As you can see the body is also slightly different, the call was getting through but was returning:正如你所看到的 body 也略有不同,调用正在通过但正在返回:

"unsupported_grant_type". “unsupported_grant_type”。

I was passing a raw string as the body parameter, which was including the quotation marks in the request.我传递了一个原始字符串作为 body 参数,它在请求中包含引号。 The solution there was to pass the okhttp3.Request body type rather than a raw string, in the function that makes the actual call it looks like this:解决方案是在进行实际调用的函数中传递 okhttp3.Request 正文类型而不是原始字符串,如下所示:

val body: "grant_type=client_credentials&scope=auto&obtain_refresh_token=true"
val requestBody = RequestBody.create("text/plain".toMediaTypeOrNull(),body)
val call = WAApiCallService.call(this)
call.callPost(requestBody,"application/x-www-form-urlencoded")
    .enqueue(object: Callback<AuthToken>{

With those changes the call succeeds and my long running headache is over.通过这些更改,呼叫成功了,我长期的头痛也结束了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM