jonb
jonb

Reputation: 875

Authenticate from Retrofit with JWT token to Rest server

my server is Flask based, my client is android studio, and i'm communication using retrofit.

The problem is that i'm not able to pass the jwt token correctly from the android to the server after logging in.

With postman it's working good: {{url}}/auth - I'm logging in as the user, and getting the JWT token. Later i'm adding "Authorization" header, with the Value "JWT {{jwt_token}}" and {{url}}/users/john - I'm asking for user info, which is recieved without problems.

The endpoint from android studio:

public interface RunnerUserEndPoints {
//    @Headers("Authorization")
    @GET("/users/{user}")
    Call<RunnerUser> getUser(@Header("Authorization") String authHeader, @Path("user") String user);

The call itself (The access_token is correct before sending!):

final RunnerUserEndPoints apiService = APIClient.getClient().create(RunnerUserEndPoints.class);
Log.i("ACCESS","Going to send get request with access token: " + access_token);
Call<RunnerUser> call = apiService.getUser("JWT" + access_token, username);
Log.i("DEBUG","Got call at loadData");
call.enqueue(new Callback<RunnerUser>() {
    @Override
    public void onResponse(Call<RunnerUser> call, Response<RunnerUser> response) { ....

The response error log from the server:

File "C:\Users\Yonatan Bitton\RestfulEnv\lib\site-packages\flask_restful\__init__.py", line 595, in dispatch_request
    resp = meth(*args, **kwargs)
  File "C:\Users\Yonatan Bitton\RestfulEnv\lib\site-packages\flask_jwt\__init__.py", line 176, in decorator
    _jwt_required(realm or current_app.config['JWT_DEFAULT_REALM'])
  File "C:\Users\Yonatan Bitton\RestfulEnv\lib\site-packages\flask_jwt\__init__.py", line 151, in _jwt_required
    token = _jwt.request_callback()
  File "C:\Users\Yonatan Bitton\RestfulEnv\lib\site-packages\flask_jwt\__init__.py", line 104, in _default_request_handler
    raise JWTError('Invalid JWT header', 'Unsupported authorization type')
flask_jwt.JWTError: Invalid JWT header. Unsupported authorization type
10.0.0.6 - - [30/Sep/2017 01:46:11] "GET /users/john HTTP/1.1" 500 -

My api-client

public class APIClient {
    public static final String BASE_URL = "http://10.0.0.2:8000";
    private static Retrofit retrofit = null;


    public static Retrofit getClient(){
        if (retrofit==null){
            retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();

        }
        Log.i("DEBUG APIClient","CREATED CLIENT");
        return retrofit;
    }
}

Actually i'm really stuck. Tried to follow along all of the tutorials at retrofit's website without success. I'm sure that there is a simple solution, I just need to add "Authorization" Header with Value "JWT " + access_token like it works in postman and that's it! Thanks.

EDIT: The problem was the build of the access_token in my client. I did: JsonElement ans = response.body().get("access_token"); access_token = "JWT " + ans.toString(); Which I should have done: JsonElement ans = response.body().get("access_token"); access_token = "JWT " + ans.getAsString();

So before it sent "JWT "ey..." " (Double "" ) And now it sends "JWT ey ... "

Upvotes: 0

Views: 2096

Answers (1)

rzetterberg
rzetterberg

Reputation: 10268

Let's start to look at what we know about the problem.

  • We know that the request is sent
  • We know that the server processes the request
  • We know that the JWT is invalid thanks to the error:

    JWTError('Invalid JWT header', 'Unsupported authorization type')

If we look for that error in the flask_jwt source code, we can see that this is where our error is raised:

def _default_request_handler():
    auth_header_value = request.headers.get('Authorization', None)
    auth_header_prefix = current_app.config['JWT_AUTH_HEADER_PREFIX']

    if not auth_header_value:
        return

    parts = auth_header_value.split()

    if parts[0].lower() != auth_header_prefix.lower():
        raise JWTError('Invalid JWT header', 'Unsupported authorization type')
    elif len(parts) == 1:
        raise JWTError('Invalid JWT header', 'Token missing')
    elif len(parts) > 2:
        raise JWTError('Invalid JWT header', 'Token contains spaces')

    return parts[1]

Basically flask_jwt takes the Authorization header value and tries to split it into two. The function split can split a string by a delimiter, but if you call it without a delimiter it will use whitespace.

That tells us that flask_jwt expects a string that contains 2 parts separated by whitespace, such as space, and that the first part must match the prefix we are using (in this case JWT).

If we go back and look at your client code, we can see that when you are building the value to be put in the Authorization header you are not adding a space between JWT and the actual token:

apiService.getUser("JWT" + access_token, username);

This is what you should have been doing:

apiService.getUser("JWT " + access_token, username);

Notice the space after JWT?

Upvotes: 0

Related Questions