When I would like to use #Get I get 401 HTTP error which means I am not authorized.
Before that I make POST on /authentication and POST on /authorization and it works I am successfully logged in.
The weird thing for me is I get two tokens, can someone explain why?
class AuthInterceptor(context: Context) : Interceptor {
private val sessionManager = SessionManager(context)
override fun intercept(chain: Interceptor.Chain): Response {
val requestBuilder = chain.request().newBuilder()
// If token has been saved, add it to the request
sessionManager.fetchAuthToken()?.let {
requestBuilder.addHeader("Authorization", "Bearer $it")
Log.d("authorization", "$it")
// on authorization I get two tokens, after click Login first appears, 2 seconds later next one
}
return chain.proceed(requestBuilder.build())
}
}
Api
// I think I do not need add here #Headers if I use method in previous class requestBuilder.addHeader~
interface Api {
#GET("device")
suspend fun getDetails(#Query("id") id: String): CameraResponse
}
authApi
interface AuthApi {
#POST("authenticate")
fun login(#Body request: User): Call<AuthResponse>
#POST("authorize")
fun authorize(#Body request: AuthResponse): Call<AuthorizeResponse>
}
error from stacktrace
W/System.err: retrofit2.HttpException: HTTP 401
at retrofit2.KotlinExtensions$await$2$2.onResponse(KotlinExtensions.kt:53)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:161)
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
W/System.err: at java.lang.Thread.run(Thread.java:923)
My documentation
curl -X GET https://myApiDocumentation/device -d "id=[MY_ID]" -H "Authentication: [API_KEY]" --cookie "auth_key=[AUTH_KEY]" -G
If you need more code from program, here is my post which is fixed and another problem occurs...
Why it gets HTTP 404 error with #GET retrofit?
Related
I'm using OkHttp with Kotlin in Android to query a REST API using basic auth.
When I use Postman to GET the resource I get:
200 when using basic auth -> So my credentials are correct and working
401 without using basic auth -> Expected behaviour (Unauthorized)
But when I use OkHttp I always get 403 with both preemptive and reactive authentication or even if I don't add any auth header at all.
Here's what I tried so far:
Adding "Authorization" header to request
Adding an Interceptor to client adding auth header to all requests
Adding an Authenticator to client
I only found this issue to be of any relevance, but I already use https instead of http in the URI. (Using http doesn't work either.)
My code to send the GET request:
private val client = OkHttpClient().newBuilder().build()
fun getOrders() {
val credentials = Credentials.basic(/*username*/, /*password*/)
val request = Request.Builder()
.url("https://example.com")
.addHeader("Authorization", credentials)
.build()
// Shows that request actually contains the header so that's OK
request.headers.forEach { println(it) }
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
e.printStackTrace()
}
override fun onResponse(call: Call, response: Response) {
println("Response STATUS: " + response.code)
}
})
}
I think I ruled out the problem being on the server's end with Postman handling all this correctly and I don't have any other idea what might work.
I'm using Retrofit with OkHttp Interceptor to work with API.
Interceptor adding cookie header to each request.
Interceptors code:
class AddCookiesInterceptor: Interceptor {
#Inject
lateinit var cookiesDao: CookiesDao
init {
App.getAppComponent().inject(this)
}
#SuppressLint("CheckResult")
override fun intercept(chain: Interceptor.Chain): Response {
val builder = chain.request().newBuilder()
cookiesDao.getAll()
.subscribeOn(Schedulers.io())
.subscribe { cookies ->
builder.addHeader("Cookie", "JWT=" + cookies.jwt)
}
return chain.proceed(builder.build())
}
}
While debuging i see, that interceptor updates request and adds cookie header with value, but when server reachs the request it returns an error (400 http code auth again).
if i manualy add Header into request like this
#GET("/api.tree/get_element/")
#Headers("Content-type: application/json", "X-requested-with: XMLHttpRequest", "Cookie: jwt_value")
fun getElementId(): Maybe<ResponseBody>
Api returns 200 http code and it works.
Your code is not working because you are adding the header asynchronously, this is a "timeline" of what's happening in your flow:
init builder -> ask for cookies -> proceed with chain -> receive cookies dao callback -> add header to builder which has been already used
What you need to do is retrieve the cookies synchronously, to accomplish this you can use the BlockingObseervable and get something like this.
Using a synchronous function won't cause any trouble since the interceptor is already running on a background thread.
#SuppressLint("CheckResult")
override fun intercept(chain: Interceptor.Chain): Response {
val builder = chain.request().newBuilder()
val cookies = cookiesDao.getAll().toBlocking().first()
builder.addHeader("Cookie", "JWT=" + cookies.jwt)
return chain.proceed(builder.build())
}
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.
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.
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.
If that doesn't work, could you trace the HTTP request and verify that you are sending the message you think you are.
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:
"unsupported_grant_type".
I was passing a raw string as the body parameter, which was including the quotation marks in the request. 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:
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.
I am trying to post data to REST API server with retrofit + RxJava . When I am trying to send data to server , it said " HTTP 500 Internal Server Error Occurred". But when the data is send with POSTMAN, it succeeded.
This is the function for sending data in Model.
// Encountering with 500 server error
fun postSchedule(data : ScheduleResponse , errorLD: MutableLiveData<String>){
Log.d("POST DATA", "${data.title} ${data.remindMeAt}" )
userClient.postScheduleItem(data)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(Schedulers.io())
.subscribe(object : io.reactivex.Observer<ServerResponse>{
override fun onComplete() {
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(t: ServerResponse) {
errorLD.value = t.status
}
override fun onError(e: Throwable) {
errorLD.value = e.message
}
})
}
This is my API interface
#Headers("Accept: application/json")
#POST("schedules")
fun postScheduleItem(#Body data: ScheduleResponse): Observable<ServerResponse>
This is the retrofit client.
val httpLoggingInterceptor = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
val httpClient = OkHttpClient.Builder()
var dbInstance: TodoDB = TodoDB.getInstance(context)
var rxJavaAdapter = RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())
val retrofitBuilder =
Retrofit.Builder()
.baseUrl(AppConstants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(rxJavaAdapter)
fun <T> createService(serviceClass: Class<T>, authToken: String?): T {
if (!TextUtils.isEmpty(authToken)) {
val interceptor = AuthenticationInterceptor(authToken!!)
if (!httpClient.interceptors().contains(interceptor)) {
httpClient.addInterceptor(interceptor)
retrofitBuilder.client(httpClient.build())
}
}
return retrofitBuilder.build().create(serviceClass)
}
Please help me with this.Thank you.
Client side code is not enough to determine what causes the server to respond with 500. The best you can do is start debugging the issue.
There are several directions you can go:
If you have access to the server or know someone who does, you could debug the server and determine what causes the Internal server error. Maybe the server logs can help as well and you don't have to actually step through the server code.
If you don't have access to the server, you could look at the body of the server response. Maybe there's a detailed error description there in html, json or some other format that will help you find out the root cause.
If the above steps don't help then it's very useful that you know the request works with POSTMAN. You can compare the exact POSTMAN request with the exact Retrofit request, header by header,
line by line. To do that, you should first add your httpLoggingInterceptor to your okhttp client builder with
val httpClient = OkHttpClient.Builder().addNetworkInterceptor(httpLoggingInterceptor)
and look for the request log in logcat.
If you spot the differences between the working and the not working requests, then you should work your way through all the differences, and adjust the retrofit request by adding or modifying headers using okhttp interceptors so that, at the end, the retrofit request looks exactly the same as the POSTMAN request. I suggest you remove the AuthenticationInterceptor at first and simulate it "manually" with a custom interceptor and a hard coded auth token.
Retry the request every time you eliminate a difference to isolate the cause of the internal server error.
Hope this helps!
I am using retrofit and I have a post request
interface NetworkApi {
#Headers("Content-Type: application/json")
#POST("/")
fun startLoginRequest(#Body loginModel : LoginModel) : Call<BaseResponseModel>
class Factory {
var retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
companion object{
fun getApi (): NetworkApi {
val serverApi = NetworkApi.Factory()
val api = serverApi.retrofit.create(NetworkApi::class.java)
return api
}
}
}
}
when I use this method and on response method were calling response body is always null.
fun startLoginRequest(){
val api = NetworkApi.Factory.getApi()
val request = api.startLoginRequest(loginRequestModel)
request.enqueue(object : Callback<BaseResponseModel>{
override fun onFailure(call: Call<BaseResponseModel>?, t: Throwable?) {
}
override fun onResponse(call: Call<BaseResponseModel>?, response: Response<BaseResponseModel>?) {
//here response.body is null
}
})
}
Strange thing is that, if I will send the same object using Postman everything works fine
this is httpClient interceptor log
--> POST http://myExampleHost.net/ http/1.1
Content-Type: application/json
Content-Length: 95
Authorization: auth-value
--> END POST (95-byte body)
<-- 405 Method Not Allowed http://myExampleHost.net/ (198ms)
Allow: GET, HEAD, OPTIONS, TRACE, COPY, PROPFIND, LOCK, UNLOCK
Content-Type: text/html
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
X-Powered-By-Plesk: PleskWin
Date: Thu, 17 Nov 2016 08:04:57 GMT
Content-Length: 1107
<HTML>
<HEAD>
<TITLE>405 Method Not Allowed</TITLE>
<BASE href="/error_docs/"><!--[if lte IE 6]></BASE><![endif]-->
</HEAD>
<BODY>
<H1>Method Not Allowed</H1>
The HTTP verb used to access this page is not allowed.<P>
<HR>
<ADDRESS>
Web Server at example address
</ADDRESS>
</BODY>
</HTML>
For now I'm using OkHttp RequestBuilder and everything works fine, I don't realize what I'm missing in the above example
val client = OkHttpClient()
val JSON = MediaType.parse("application/json; charset=utf-8")
val body = RequestBody.create(JSON,json)
val request1 = Request.Builder()
.url("http:example.com")
.addHeader("content-type", "application/json")
.method("POST", body)
.build()
client.newCall(request1).enqueue(object : okhttp3.Callback{
override fun onFailure(call: okhttp3.Call?, e: IOException?) {
}
override fun onResponse(call: okhttp3.Call?, response: okhttp3.Response?) {
response?.body()?.string()?.print("RETROFIT response")
}
})
My best guess is that you are trying to post to the wrong url www.example.com/login// instead of www.example.com/login/. APIs can be a bit finicky about posts.
You can add the logging interceptor so you can see what you are posting and to what URL so its a little easier to debug. To set it up you need to add compile "com.squareup.okhttp3:logging-interceptor:$okhttp_version" to your gradle file and change your retrofit setup to something like this:
val httpClient = OkHttpClient.Builder().apply {
if (BuildConfig.DEBUG) {
httpClient.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
}
}
var retrofit = Retrofit.Builder()
.client(httpClient.build())
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
That's the best advice I can give without seeing the error logs.
For me the main reason was i was calling http instead https
For Example:
i was using this url as a base url which give me this error
http://www.facebook.com/
when i changed it to this one it worked like a charm
https://www.facebook.com/
I was missing "/" at the end of my url in the POST request.
Changing url from
www.example.com/list_products
to www.example.com/list_products/ worked for me.
Method not allowed error with status code-405 is showed when we are not sending our request method or wrong request method is getting sent.
In my scenario,Api is of POST type and I was checking for GET request type,
So try change your proper Request method(GET/POST/PUT/DELETE).
I found the answer, the problem was url, my baseUrl were http//www.example.com/url1/url2/...../service
and #POST("/")
when I saw the POST endpoint it was http//www.example.com/ and I don't know why
I just changed this urls
baseUrl = http//www.example.com/
and #POST("url1/url2/...../service")