Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a refresh token process with JWT for Android apps

I'm working on a Oauth2 Token system to access my REST API for my Android app. I'm having some problems with the token refreshment part on the client side.

Here is the flow : My app makes a request (with an access Token in parameter) to the server thanks some asynctask ( PostCommentAsyncTask(), AddFriendAsyncTask() etc...), so if the accessToken is valid it's ok, but if it has expired I call another AsyncTask (GetRefreshTokenAsyncTask()) from the onPostExecute() method of the precedent AsyncTask to get new accessToken. Here is the tricky part for me. When I get the new access Token I want to re-execute the initial AsyncTask request to the server. I can't figure out how to do it properly.

example1 :

request PostCommentAsyncTask() --> (acessToken expired) -->GetRefreshTokenAsyncTask()-->request PostCommentAsyncTask()--> (good token)--> Ok

EDIT:

I finally chose to use the Volley library ( no need to use Asynctask anymore ). As I use JSON Web Token I can check the expire date wich is encoded in the payload of the token.

Here is the isAccessTokenExpired() method to check if the Access Token is not expired before making a request to the server :

public Boolean isAccessTokenExpired(String accessToken){

        String[] accessTokenPart = accessToken.split("\\.");
        String header =accessTokenPart[0];
        String payload =accessTokenPart[1];
        String signature =accessTokenPart[2];

        try {

            byte[] decodedPayload = Base64.decode(payload, Base64.DEFAULT);
            payload = new String(decodedPayload,"UTF-8");
        } catch(UnsupportedEncodingException e) {
            e.printStackTrace();
        }


        try {
            JSONObject obj = new JSONObject(payload);
            int expireDate = obj.getInt("exp");
            Timestamp timestampExpireDate= new Timestamp( expireDate);
            long time = System.currentTimeMillis();
            Timestamp timestamp = new Timestamp(time);

            return  timestamp.after(timestampExpireDate);

        } catch (JSONException e) {
            e.printStackTrace();
            return true;
        }

    }

And here is the refreshJsonWebToken() method to get a new pair of Access token/Refresh token from my OAUTH2 server:

public void refreshJsonWebToken(){


            SharedPreferences settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
            String refreshToken = settings.getString("refreshToken", null);

            final HashMap<String, String> params = new HashMap<String, String>();
            params.put("grant_type","refresh_token");
            params.put("client_id","client");
            params.put("refresh_token",refreshToken);

            JsonObjectRequest req = new JsonObjectRequest(URL_OAUTH2, new JSONObject(params), new Response.Listener<JSONObject>() {

                @Override
                public void onResponse(JSONObject response) {

                        try {

                            String newRefreshToken = response.getString("refresh_token");
                            SharedPreferences settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
                            SharedPreferences.Editor editor = settings.edit();
                            editor.putString("accessToken", newAccessToken);
                            editor.putString("refreshToken", newRefreshToken);
                            editor.apply();
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    }
            }, new Response.ErrorListener() {


                @Override
                public void onErrorResponse(VolleyError error) {
                    Log.e("grid", "Error: " + error.getMessage());

                    }
                }
            });

            AppController.getInstance().addToRequestQueue(req);


    }

And finnally the getPost() method where I use the precedent methods :

private void getPost(String latitude, String longitude) {

        SharedPreferences settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
        String accessToken = settings.getString("accessToken", null);
        final HashMap<String, String> params = new HashMap<String, String>();
        params.put("action", "getLocalPosts");
        params.put("latitude", latitude);
        params.put("longitude", longitude);

        if (isAccessTokenExpired(accessToken)){
            refreshJsonWebToken();
        }

        settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
        accessToken = settings.getString("accessToken", null);
        JsonObjectRequest req = new JsonObjectRequest(URL_APP+accessToken, new JSONObject(params), new Response.Listener<JSONObject>() {

            //Some code ....
        });

        AppController.getInstance().addToRequestQueue(req);

    }
like image 491
Frédéric Avatar asked Jul 23 '15 19:07

Frédéric


People also ask

How do you use refresh tokens in JWT?

In the URL field enter the address to the refresh token route of your local API - http://localhost:4000/users/refresh-token . Click the Send button, you should receive a "200 OK" response containing the user details and a JWT token, and a cookie containing a new refresh token.

Can you use JWT in mobile app?

Get the User Profile To retrieve the User Profile, your mobile application can decode the ID Token using one of the JWT libraries. This is done by verifying the signature and verifying the claims of the token.

Does JWT have refresh token?

JWT (JSON Web Token) It may also have a validity period. Once this validity period has elapsed, the server will no longer allow access to resources with this token. In this step, the user will have to get a new access token by reauthentication or with some additional method: refresh token.

How do I renew my JWT token?

To refresh the token, your API needs a new endpoint that receives a valid, not expired JWT and returns the same signed JWT with the new expiration field. Then the web application will store the token somewhere.


1 Answers

I think Handler is better in this case because Looper has synchronous message queue which is convenient here. You create a HandlerThread and associate your Handler with it. Then you can call postRunnable on it depending on your needs, e.g. you add PostCommentRunnable, if token has expired you add GetRefreshTokenRunnable and PostCommentRunnable, - they will be executed sequentially.

If you still want to go with AsyncTasks, can you check whether token has expired before launching PostCommentAsyncTask? I think that will a be better design. If you can't, then you can execute them one after another because they work on the same background thread by default, e.g.:

new PostCommentAsyncTask().execute();

class PostCommentAsyncTask extends AsyncTask {
    //...
    onPostExecute() {
        if (tokenExpired) {
            new GetRefreshTokenAsyncTask().execute();
            new PostCommentAsyncTask().execute(); // this guy will wait till refresh token is executed.
        }
    }
}
like image 72
Gennadii Saprykin Avatar answered Oct 17 '22 16:10

Gennadii Saprykin