Webservice not working in Android Retrofit , but works in Postman and Swift / iOS, Getting 401 Unauthorized

16,263

Solution 1

SOLUTION (its the COOKIE)
Thanks to some of the tips, the actual reason for Incompatibility of service is , Supposedly POSTMAN and iOS client store and reuse COOKIE all by itself when requests are made without any need for explicit handling, Cookie in Postman can be tested using Postman Intercepter ,but cant be edited because chrome does not allow editing cookie by plugins

However Retrofit/OkHttp unless specified will consider it disabled(for security reason maybe) ,
Cookie is added either inside Interseptor as one of the header addHeader("Cookie","KEY-VALUE")
or
Use cookieJar to add into

OkHttpClient mIntercepter = new OkHttpClient.Builder()
                .cookieJar(mCookieJar)
                .addInterceptor(new RequestResponseInterseptor(context))
                .addInterceptor(logInter)
                .build();

based on your need and cookie type

Solution 2

I think you are overriding other headers Retrofit is adding for you, causing your API to not care about your Authorization header. Code below will add a header to your existing headers instead of overriding them.

    OkHttpClient mIntercepter = new OkHttpClient.Builder()
            ...
            .addInterceptor(new Interceptor() {
                 @Override 
                 public Response intercept(Chain chain) throws IOException {
                        Request request = chain.request().newBuilder().addHeader("Authorization", "Bearer " + "1ed6b7c1839e02bbf7a1b4a8dbca84d23127c68e").build();
                        return chain.proceed(request);
            })
            ...
            .build();

Format of those headers is correct here, key should be Authorization and value should be Bearer 1ed6b7c1839e02bbf7a1b4a8dbca84d23127c68e (in your case).

Solution 3

In my case, unfortunately, none of advices listed in @Ujju 's solution worked (i.e. neither "Cookie" header nor CookieJar applied). The only thing that helped me is just replacing addInterceptor with addNetworkInterceptor, and everything began to work.

Solution 4

401 Unauthorized http://www.stackoverflow.com/api/[email protected]&password=123456
Date: Fri, 07 Apr 2017 11:23:28 GMT
Server: Apache/2.4.25 (Amazon) PHP/5.6.29
X-Powered-By: PHP/5.6.29
Cache-Control: no-cache, private
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
Content-Length: 41
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: application/json
{"msg":"Invalid Credentials"}

I was facing issue like that not able fetch error message.

When server throwing error like 401 or other error we are getting null body from server.But you can get error message from server in errorBody

String response = response.errorBody().string()

Solution 5

I faced with this problem as well. Requests work fine not only in POSTMAN, but also in CURl. Having spent a lot of time I found a solution.

Service example for login:

@FormUrlEncoded
@POST("authentication/login")
fun login(
    @Field("login") login: String,
    @Field("password") password: String
): Single<Void>

Provide okhttp client:

private fun provideOkHttpClient(): OkHttpClient {

   val cookieManager = CookieManager()
   cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL)

   val client = OkHttpClient.Builder()
      .cookieJar(JavaNetCookieJar(cookieManager))
      .addNetworkInterceptor(provideRequestInterceptor())
      .addNetworkInterceptor(provideLoggingInterceptor())
      .protocols(Arrays.asList(Protocol.HTTP_1_1))
      .build()

   return client
}

Make enable JavaNetCookieJar:

implementation "com.squareup.okhttp3:okhttp-urlconnection:$okHttpVersion"

Provide authorization:

private fun provideRequestInterceptor() = Interceptor {
    val builder = it.request().newBuilder().url(it.request().url())

    val tokenStr = BuildConfig.SONAR_TOKEN
    builder.addHeader("Authorization", "Basic "+ getBase64String(tokenStr+":"))

    it.proceed(builder.build())
}

private fun getBase64String(value: String): String {
    return Base64.encodeToString(value.toByteArray(charset("UTF-8")), Base64.NO_WRAP)
}
Share:
16,263

Related videos on Youtube

Ujju
Author by

Ujju

Proud Programmer and a gamer

Updated on June 04, 2022

Comments

  • Ujju
    Ujju almost 2 years

    BRIEFING BEFORE 'technical stuff'
    Not new to working with Retrofit but came across this strange behaviour which I am having very hard time to understand and fix, I have two web service, both work fine as expected in Postman and iOS but only one works in Retrofit and not the other,
    In my defence I can say I am getting (Unauthorized) response,which means I was able to hit the server and get a result
    In API developer's defence he says it works in Postman and other devices so not a service issue

    If any Retrofit expert out there tell me what retrofit may be doing behind my back in order to get this error?

    TECHNICAL STUFF
    Talking about the type of service , It contains Authorization Bearer token as header which expires every 6 hours and contains no params at all (so it should be easy, right ?) and a simple url http://hashchuna.nn-assets.com/api/locations
    Unfortunately the header token cant be shared with valid key, cos it'l be expired before anyone can try it, but here it's anyway Authorization Bearer 3d44626a55dbb024725984e0d37868336fd7e48a

    WHAT I'VE TRIED
    I am using okhttp intercept to add Authorization Header to request using both addHeader/header method, no spaces in the url cos there r no params
    Getting 401 unauthorized error in retrofit?
    Java: Android: Retrofit - using Call but, Response{code = 401,message=unauthorized}
    https://github.com/square/retrofit/issues/1290
    But non of them helped

    WARNING
    Now the tricky part to keep in mind, the token when expired must give 401 error which is expected, but the problem is even for freshly created token I get 401 , which is my core problem

    LOG

    D/OkHttp: --> GET http://hashchuna.nn-assets.com/api/locations http/1.1
    D/OkHttp: Authorization: Bearer 7c0d53de006b6de931f7d8747b22442354cecef9
    D/OkHttp: --> END GET
    D/OkHttp: <-- 401 Unauthorized http://hashchuna.nn-assets.com/api/locations (773ms)
    D/OkHttp: Date: Mon, 20 Feb 2017 10:44:11 GMT
    D/OkHttp: Server: Apache
    D/OkHttp: X-Powered-By: PHP/7.0.15
    D/OkHttp: Access-Control-Allow-Origin: *
    D/OkHttp: Access-Control-Allow-Credentials: true
    D/OkHttp: Access-Control-Max-Age: 1000
    D/OkHttp: Access-Control-Allow-Headers: X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding
    D/OkHttp: Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT
    D/OkHttp: Expires: Thu, 19 Nov 1981 08:52:00 GMT
    D/OkHttp: Cache-Control: no-store, no-cache, must-revalidate
    D/OkHttp: Pragma: no-cache
    D/OkHttp: Set-Cookie: PHPSESSID=u477o8g0q387t92hms4nhc14n1; path=/
    D/OkHttp: Vary: Authorization
    D/OkHttp: X-Powered-By: PleskLin
    D/OkHttp: Keep-Alive: timeout=5
    D/OkHttp: Connection: Keep-Alive
    D/OkHttp: Transfer-Encoding: chunked
    D/OkHttp: Content-Type: application/json;charset=utf-8
    D/OkHttp: <-- END HTTP
    

    CODE
    Intercept

    Request request = chain
                            .request()
                            .newBuilder()
                            //.header("Authorization","Bearer "+ SharedPrefsUtils.getSPinstance().getAccessToken(context))
                            .addHeader("Authorization","Bearer 1ed6b7c1839e02bbf7a1b4a8dbca84d23127c68e")
                            //.addHeader("cache-control", "no-cache")
                            //.cacheControl(CacheControl.FORCE_NETWORK)
                            .build();
    

    Retrofit Instance

    private Api getApiInstance(Context context) {
            HttpLoggingInterceptor logInter = new HttpLoggingInterceptor();
            logInter.setLevel(HttpLoggingInterceptor.Level.BODY);
            OkHttpClient mIntercepter = new OkHttpClient.Builder()
                    .addInterceptor(new RequestResponseInterseptor(context))
                    .addInterceptor(logInter)
                    .build();
    
            Retrofit retrofitInstance = new Retrofit.Builder()
                    //.addConverterFactory(new NullOnEmptyConverterFactory())
                    .addConverterFactory(GsonConverterFactory.create())
                    .baseUrl(BASE_URL)
                    .client(mIntercepter)
                    .build();
            return retrofitInstance.create(Api.class);
        }
    
    • C0D3LIC1OU5
      C0D3LIC1OU5 about 7 years
      Are you ABSOLUTELY sure you're appending an updated token to your headers? Sounds like you're just using an old token.
    • C0D3LIC1OU5
      C0D3LIC1OU5 about 7 years
      what is this used for? D/OkHttp: Set-Cookie: PHPSESSID=u477o8g0q387t92hms4nhc14n1; path=/
    • BNK
      BNK about 7 years
      In Postman, only Authorization accepted, of couse the value I set is Bearer 961e171e4d27c95f29a31c91cbef2d6863c760fe, however, even with Postman still gets 401. Can you provide with the specs or requirement doc for that web service?
  • Ujju
    Ujju about 7 years
    I am using Retrofit 2.1.0 , both RestAdapter and RequestInterceptor has been replaced with Request and OkHttpClient
  • Ujju
    Ujju about 7 years
    But this looks same as my intercept right,,? my RequestResponseInterseptor extends Interceptor and creates the same request, ,,you can check my Intercept code
  • C0D3LIC1OU5
    C0D3LIC1OU5 about 7 years
    Not sure, I don't see full code for your RequestResponseInterseptor class. Key here is overriding the intercept function, which I don't see you doing anywhere.
  • Ujju
    Ujju about 7 years
    Ok, ya actually since der was too much of code I pasted only small part,,, yes the RequestResponseInterseptor extends interceptor, intercept contains code same as yours nothing else
  • C0D3LIC1OU5
    C0D3LIC1OU5 about 7 years
    Ok. BTW, the line you are using is not correct - you have the correct headers commented out (it's the line above). Have you tried hardcoding your token with the correct header keys? .header("Authorization","Bearer "+ "1ed6b7c1839e02bbf7a1b4a8dbca84d23127c68e").
  • Ujju
    Ujju about 7 years
    I was trying Authorization Bearer as key as BrianCooley mentioned in his comment, but that also returns same result as passing this key,, I'm pretty sure the issue is around header, but as u see in log, request is perfect
  • BNK
    BNK about 7 years
    For your but cant be edited because chrome does not allow editing cookie by plugins, I think you can try Postman App for Win/MacOS at getpostman.com
  • Ujju
    Ujju about 7 years
    Thanks for the reply, but my problem was not about not getting error msg, I got the errorBody just fine, In my case the msg was somthing like { "errors": [ { "code": "bad_request", "title": "not_authorised" } ]}
  • IgorGanapolsky
    IgorGanapolsky almost 6 years
    What is mCookieJar??
  • Ujju
    Ujju almost 6 years
    @IgorGanapolsky it's a library, let's yu add cookies to ur request by creating an object and adding values
  • Priyanka Singhal
    Priyanka Singhal about 5 years
    Still not able to get the response. There is some session issue showing in server.But since it runs in web and iOS as well as i have no other option just to check my code.
  • DragonFire
    DragonFire about 5 years
    for me it was filesize issue please see stackoverflow.com/a/55913121/3904109
  • kgandroid
    kgandroid almost 4 years
    What is mCookieJar?
  • Ujju
    Ujju almost 4 years
    @kgandroid CookieJar is a library to manage cookie, mCookieJar is just instance/object of that library