17

If I have a server where I authenticate with username/password and get auth token for subsequent requests, what would be the best approach addressing this problem?

The flow should be like this: - Start request - If we don't have auth token - get it with username and password - Make request with auth token - If request failed because token expired, get new auth token with user name and password - Retry request with new auth token - Finish

I've noticed that Volley already might have something that might solve this issue - Authenticator https://android.googlesource.com/platform/frameworks/support/+/4474bc11f64b2b274ca6db5a1e23e8c1d143d5fa/volley/src/com/android/volley/toolbox/Authenticator.java It contains getAuthToken() and invalidateAuthToken() methods which would be exactly what I want. But it seems that it's never used in the library at all.

MantasV
  • 1,485
  • 3
  • 11
  • 9
  • I've just checked the sources, and it looks like you are - Authenticator is not used in the code. So probably you will have to do it manually. – negersiu Jul 01 '13 at 21:08
  • Yeah. In the meantime I've copied BasicNetwork class and made adjustments there to do authentication for me. – MantasV Jul 04 '13 at 14:57
  • 2
    can you post how you did it as answer, please – M-Wajeeh Aug 15 '13 at 10:41

5 Answers5

12

I used volley for an authentication system using longlive (LLT) and shortlive (SLT) tokens.

I did it manually but it really wasn't much work once you get it all laid out.

Have all secure requests subclass a baseSecureRequest that can handle this token mechanism common to all secure request in its onResponse() and onErrorResponse().

It becomes a little node.js style, where requests send other requests and await callbacks.


An app may have a dozen screens, with only half requiring auth access - so each screen should be ignorant as to the requirements of its request.

Scenario A

  • We attempt to send a secure request. We notice we don't have a SLT in memory, so make a TokenRequest.
  • TokenRequest's onResponse() saves that token to memory (let a singleton session manager hold onto it or similar omni-present class)
  • Now callback to the original concrete-class request object to continue with the newly updated token.

Scenario B

  • We send a secure request but our SLT is stale (expired)

  • The server returns an error code or msg that you can catch in the general onErrorResponse() of your baseSecureRequest.

  • In this onError(), you send a refreshTokenRequest() object that attempts to refresh the SLT in memory by requesting a new SLT from the server using the LLT.

  • the onResponse() of the refreshTokenRequest can now callback to the original request to resend.

  • however the onErrorResponse() should probably abandon the entire thing because chances are anything that isn't a connectivity error - is an error caused by invalid LLT. If you keep trying to refresh with a bad LLT you will never get out.
mjw
  • 914
  • 1
  • 8
  • 15
  • 3
    I disagree. onErrorResponse is too late for refreshing the token, because the request will be considered as failed no matter what you do. The correct place for requesting a new token will be in a custom RetryPolicy class. The retry policy will be asked to prepare for retry in 2 cases: 1. Timeout 2. AuthFailureError - which is what you want. – Avi Shukron Oct 31 '14 at 14:03
  • This is not a valid solution. By the time volley returns error it has already retried your request. The correct place to do this is to write a custom retry policy. Let's assume you have 3 threads for your API request. That means from your network queue you can theoretically have 3 requests in parallel. Now if the SLT expired, you will get 3 failures at different times. That means you will retry bunch of your refreshTokenRequest with either old SLT or keep retrying with new tokens with even if you have valid token. – SaKet Oct 26 '15 at 18:38
  • @SaKet Could you explain in detail how to handle refresh token in case of multi-thread where request are processed in parallel. It would be great if you can share a code snippet. – Praveen Dec 07 '16 at 05:44
1
  1. You might want to use the AccountManager API in Android for authentication and authorization. You may follow the blog here.
  2. For OAuth 2.0 server side implementation you may read the IETF draft V2-31 here.
  3. For better understanding of OAuth 2.0 you may read the blog by Nitesh Kumar here.
  4. For Server side implementation of OAuth 2.0 you can fork Apis Autherization Server repo at Github.
  5. More implementation option can be found at the website of OAuth 2.0 here.
Community
  • 1
  • 1
Gaurav Agarwal
  • 18,754
  • 29
  • 105
  • 166
  • Thanks for response, but my questions is specifically about Volley. It's not about how to do authorization, but how to integrate it with Volley – MantasV Jul 21 '13 at 20:50
  • AndroidAuthenticator seems to use accountManager, but I am not sure how we are supposed to use AndroidAuthenticator. – Pierre-Antoine Dec 08 '13 at 15:34
1

Did you see this blog post? https://smaspe.github.io/2013/06/06/volley-part2.html

Demonstrates a simple way of overriding request object to use twitter tokens.

@Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        Map<String, String> headers = new HashMap<String, String>();
        String auth = "Basic "
                + Base64.encodeToString((TwitterValues.CONSUMER_KEY 
                + ":" + TwitterValues.CONSUMER_SECRET).getBytes(),
                        Base64.NO_WRAP);
        headers.put("Authorization", auth);
        return headers;
    }
Eugen Konkov
  • 22,193
  • 17
  • 108
  • 158
cbrulak
  • 15,436
  • 20
  • 61
  • 101
0

In my case, I changed a "Basic" authentication to a Token authentication as follows:

@Override
public Map<String, String> getHeaders() throws AuthFailureError {
    Map<String,String> headers = new HashMap<>();
    //todo Esta es una authenticación basica (usuario y contraseña)
    /*String credentials = USER+":"+PASSWORD;
    String auth = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
    headers.put("Authorization", auth);*/
    //todo Esta es una authenticación con token (por tiempos)
    headers.put("Authorization", "Bearer" + " " + "tokenString");//App.getToken()
    return  headers;
}

What I did was to save the Login in a global static variable and then be able to use it.

Gerrard
  • 819
  • 9
  • 7
-2

getToken() failed. Status BAD_AUTHENTICATION error

I also faced the same problem.

Solution: check whether the device is sign-in with your Google account.