I have an app that uses Spring for Android on the Client side and Spring Boot on the Server side. We would like to add client authorization to some requests. We already use Firebase and OAuth2 and after reading on the subject, I feel like the best way to go would be to use the Authentification header to send the JWT to our server, with the bearer method for the authorization :
Authorization: Bearer <token>
Something like that ...
My problem is this : Spring for Android only has BasicAccessAuthentification built-in, but I dont have a username:password scheme for my client credentials, only a JWT. I quite naively tried creating a Class that extends HttpAuthentication :
import org.springframework.http.HttpAuthentication;
public class HttpBearerAuthentification extends HttpAuthentication {
private String token;
public HttpBearerAuthentification(String token){
this.token = token;
}
public String getHeaderValue() {
return String.format("Bearer %s", token);
}
#Override
public String toString() {
return String.format("Authorization: %s", getHeaderValue());
}
}
This class is based on the HttpBasicAuthentication class in Spring for Android.
And then doing the request :
// regular request that I know works
...
HttpBearerAuthentification auth = new HttpBearerAuthentification(token);
headers.setAuthorization(auth)
...
// send request and get answer as usuall
I know the requests themselves work (requests without without authorization, at least) so this is not the problem. Would that be the right way to do it ? I understand the success of the request also depends on how the server handle the request. But for now, my question is really about the client side. Is this code enough to send the JWT to the server ?
After some testing on requests sent with SpringForAndroid, it seems that is is quite easy to set values for headers. Because the HttpHeaders class actually implements the Map interface, all I had to do was :
protected HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + userRegistrationToken);
So I am building an Android app which talks to an API.
Whenever a user logs in, I see the response in the log cat which contains the user details including the authentication token but the user gets unauthorized access although the login is successful.
My question is; how do I authorize the user? How do I save the token header in android using volley? This concept is new to me. Please help.
After you login, you need to save the session cookie, access token or whatever your api provides. I usually use SharedPreferences.
Later within your next api Request, you need to override the getHeaders();
JsonObjectRequest volleyRequest = new JsonObjectRequest(url, null, responseListener, errorListener){
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> params = new HashMap<>();
// put your accesstoken if available
params.put("X-CSRF-Token", accessToken);
// put a session cookie if available
params.put("cookie", sessionName + "=" + sessionId);
return params;
}
};
I want to use Http Digest with Volley. So far I have used the following code:
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> params = new HashMap<String, String>();
String creds = String.format("%s:%s","admin","mypass");
String auth = "Digest " + Base64.encodeToString(creds.getBytes(), Base64.NO_WRAP);
params.put("Authorization", "Digest " +auth);
return params;
}
So far I get a response from server as wrong credentials which means that authentication is working but only wrong credentials are getting passed. But my credentials are right.
You use Base64 encoding which is fine for Basic Auth but this is not how digest works.
Digest & Basic Auth specs can be found here: https://www.rfc-editor.org/rfc/rfc2617
the newer Digest Specs can be found here: https://www.rfc-editor.org/rfc/rfc7616
And nice extra explanation on wikipedia here: https://en.wikipedia.org/wiki/Digest_access_authentication
For Volley implementation of Digest Auth you can use:
http://www.java2s.com/Open-Source/Android_Free_Code/Framework/platform/com_gm_android_volleyHttpDigestStack_java.htm
You will just need to pass this http stack when you create your Network which you then use to create your RequestQueue:
RequestQueue requestQueue;
requestQueue = new RequestQueue(
new DiskBasedCache(rootDir),
new BasicNetwork(new HttpDigestStack())
);
requestQueue.start();
I am using an API that uses an authorization scheme that requires a special "X-Authorization" header to be set to authenticate the request. For example, this Retrofit setup works perfectly for the user whose auth token is abc123:
#Headers("X-Authorization: abc123")
#GET("/posts")
Observable<List<Post>> get_posts();
I cache the user's X-Authorization token, so I have access to that, however, I can't just drop it in the #Headers declaration.
#Headers("X-Authorization: " + token)
#GET("/posts")
Observable<List<Post>> get_posts();
I get a compile error here: Error:(41, 34) error: element value must be a constant expression
Any ideas on how I could get around this?
Since Retrofit 2.0 you have two options
1) Using OkHttp 2.2+ use Interceptor
At the Http level, you have more control over the request, so you could do things like applying headers only to a specific request made to a specific endpoint, and so on.
public class MyOkHttpInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
if (!"/posts".contains(originalRequest.url()) ) {
return chain.proceed(originalRequest);
}
String token = // get token logic
Request newRequest = originalRequest.newBuilder()
.header("X-Authorization", token)
.build();
return chain.proceed(newRequest);
}
[...]
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.networkInterceptors().add(new MyOkHttpInterceptor());
OkClient okClient = new OkClient(okHttpClient);
YourApi api = new RestAdapter.Builder()
.setEndpoint(url)
.setClient(okClient)
.build()
.create(YourApi.class);
Edit:
Adding #JakeWarthon comment as another option as is also valid.
2) Put #Header on a method parameter and pass it as a value when invoking.
From the docs:
// Replaces the header with the the value of its target.
#GET("/")
void foo(#Header("Accept-Language") String lang, Callback<Response> cb);
Header parameters may be null which will omit them from the request. Passing a List or array will result in a header for each non-null item.
Note: Headers do not overwrite each other. All headers with the same name will be included in the request.
EDIT: This option should not be considered as Retrofit 2.* dropped support for interceptors.
3) User retrofit RequestInterceptor
From the docs:
Intercept every request before it is executed in order to add additional data.
You could do something like
public class MyRetrofitInterceptor implements RequestInterceptor {
#Override
public void intercept(RequestFacade req) {
String token = // get token logic
if (token != null) {
req.addHeader("X-Authorization", token);
}
}
[...]
YourApi api = new RestAdapter.Builder()
.setEndpoint(url)
.setRequestInterceptor(new MyRetrofitInterceptor())
.build()
.create(YourApi.class);
The "problem" with this approach is that the interceptor will get executed on all the endpoints, as it's set at the RestAdapter level, and not per endpoint. Also, the RequestFacade doesn't expose much information about the request, so no chance to add much logic around it.
Passing header in parameter would be helpful. Look to the following code;
#GET("/posts")
Observable<JsonElement> getDataFromService(
#HeaderMap Map<String, String> headers,
#QueryMap HashMap<String, Object> queryParams
);
hashMap1.put("Authorization", token);
return ApiService.getAPI_test().getDataFromService(hashMap1, url, hashMap)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io());
Update:
More better would be
#GET("/posts")
Observable<JsonElement> getDataFromService(
#Header("Authorization") token: String = "Bearer " + PreferenceUtils.getToken(),
#QueryMap HashMap<String, Object> queryParams
);
Dynamic Header In Retrofit 2
I have struggled too much to add Dynamic Header In Retrofit 2.
I have gone through so many blogs and StackOver flow. Everyone has shown example with Interceptor.
And it’s not a wise thing ,just for one API call we need to do that much work.
You just have to add #HeaderMap as argument of fun. I have done in very simple way :-
In Kotlin
val headers = HashMap<String, String>()
headers["KEY_AUTHORIZATION"] = "paste AUTHORIZATION value here"
headers["KEY_TOKEN"] = "paste TOKEN value here"
val jsonObject= JsonObject()
I am passing here header and other data also
Calling of fun:-
postEvent(headers,jsonObject)
API Declaration
#POST("/v1/post_data")
fun postEvent(#HeaderMap headers: Map<String, String>, #Body jsonObject: JsonObject): Call<JsonObject>
API Declaration with RxAndroid
#POST("/v1/post_data")
fun postEvent(#HeaderMap headers: Map<String, String>, #Body jsonObject: JsonObject): Single<JsonObject>
2nd argument here i have JsonObject. You can replace with anything whatever you need to pass or you can remove it also.
In Java
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("KEY_AUTHORIZATION","paste AUTHORIZATION value here");
headers.put("KEY_TOKEN", "paste TOKEN value here");
JsonObject jsonObject= new JsonObject();
I am passing here header and other data also
Calling of fun:-
postEvent(headers,jsonObject);
API Declaration
#POST("/v1/post_data")
Call<JsonObject> postEvent(#HeaderMap Map<String, String> headers, #Body JsonObject jsonObject);
API Declaration with RxAndroid
#POST("/v1/post_data")
Single<JsonObject> postEvent(#HeaderMap Map<String, String> headers, #Body JsonObject jsonObject);
2nd argument here i have JsonObject. You can replace with anything whatever you need to pass or you can remove it also.
A request Header can be updated dynamically using the #Header annotation. A corresponding parameter must be provided to the #Header. If the value is null, the header will be omitted. Otherwise, toString will be called on the value, and the result used.
#GET("user")
Call<User> getUser(#Header("Authorization") String authorization)
When the last part of this answer
How to dynamically set headers in Retrofit (Android)
did not work for me (halfway of the project), I improved it:-
public class MyRetrofitInterceptor implements RequestInterceptor {
// volatile variable
public static String token = null; //change at start of app
#Override
public void intercept(RequestFacade req) {
// change token from outside the class.
if (token != null) {
req.addHeader("X-Authorization", token);
}
}
It worked as soon as the token was updated from the response from the server API.
I think it worked as the string variable 'token' was used as the reference to its value, in global terms (being public static).
I am using volley in my android application and I use it to load images most of the time, however once I have to download some file(not images) I use native HttpURLConnection instead of volley.
Now I want both the volley and the HttpURLConnection will use the same cookie in my whole application.
Is this possible?
You can create your own VolleyRequest class that extends the com.android.volley.Request. All your requests in the app will be using this class.
To use the cookie with all these requests override the getHeaders method and you can do the same operation of setting the cookie as you might be doing with HttpURLConnection
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headersMap = new HashMap<String, String>();
headersMap.put("Cookie", "COOKIE_KEY=COOKIE_VALUE");
return headersMap;
}