Retrofit add header with token and id - java

I have a problem with getting authenticated user. Before it I got token and user id. Now i need to get user from server using access token and id.
I have header format
Now I'am trying to add header with user token and id using interceptor.
My code:
Interceptor interceptor = new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request newRequest = chain.request().newBuilder()
.addHeader("Accept", "application/json")
.addHeader("authorization", token) <-??
.addHeader("driver_id", id) <-??
.build();
return chain.proceed(newRequest);
}
};
OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();
okHttpBuilder.addInterceptor(interceptor);
OkHttpClient okHttpClient = okHttpBuilder.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
Interface:
#GET("driver/v1/driver")
Call<Driver> getAuthorizedDriver();
Different variants throws 401 error, don't know what to do
Log:
I/Response code: 401
I/Response message: Unauthorized`

I got it.
It's must look like:
#GET("driver/v1/driver")
Call<Driver> getAuthorizedDriver(#Header("authorization") String auth);
And auth:
Call<Driver> call = apiInterface.getAuthorizedDriver("Token token=" + token + ", driver_id=" + id);

Try to pass the header values via the method call:
#GET("driver/v1/driver")
Call<Driver> getAuthorizedDriver(#Header("authorization") String token,
#Header("driver_id") Integer id);
You also wouldn't have to deal with this huge chunk of Interceptor code

Related

Is it possible to pass a string to a interface? Or at lest fetch from SharedPreferences inside a interface?

I'm trying to fetch a stored JWT from the SharedPreferences so I can send them as a Header in my requrest but I'm not able to get that data inside the API interface. Is this possible?
Thanks
SOLVED:
For anyone looking for this: you can pass a Header as a param, ex.:
#FormUrlEncoded
#POST("users/getUser")
Call<String> getUser(
#Header("Token") String token,
#Field("user") String user
);
also you can use an integrated Interceptor to adding this token on ALL requests like this:
public class AuthInterceptor
implements Interceptor {
#Override
public Response intercept(Chain chain)
throws IOException {
Request request = chain.request();
request = request.newBuilder()
.addHeader("Token", new MySharedPref().getToken())
.build();
return chain.proceed(request);
}
}
and after that add an instance of it on your OkHttpClient :
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addNetworkInterceptor(new AuthInterceptor());
Retrofit retrofit = new Retrofit.Builder()
...
.client(httpClient.build())
.build();

Retrofit unable to set new Token to Header of Requests

I have a retrofit Client that helps me set a header for any requests i make to my REST APIs. On user login i get the token from the server and set this token to the header of the requests. I save this token to SharedPreferences so that i can get it anytime i need to make requests to my REST APIs. The problem is that anytime i set a new token to my SharedPreferences file when a new user signs in, it still gets the old token instead of saving this new token to use for future requests.
This is my Retrofit Client below:
public class RetrofitClient {
private static Retrofit retrofit = null;
public static Retrofit getClient(String token) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient okClient = new OkHttpClient();
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.create();
okClient.interceptors().add(chain -> chain.proceed(chain.request()));
okClient.interceptors().add(chain -> {
Request original = chain.request();
Request request = original.newBuilder()
.header(Config.X_AUTH_TOKEN, "Bearer" + " " + token)
.method(original.method(), original.body())
.build();
Log.d("Authorization", token);
return chain.proceed(request);
});
okClient.interceptors().add(logging);
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(Config.BASE_URL1)
.client(okClient)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
return retrofit;
}
}
this is my codes for setting and getting the token
public String getToken() {
return prefs.getString(AuthUser.USER_TOKEN, "");
}
public void setToken(String token) {
SharedPreferences.Editor editor = prefs.edit();
editor.putString(AuthUser.USER_TOKEN, token);
editor.apply();
}
this is where i call my set token method to save the new token to SharedPreference
authUser.setToken(token);
I completely don't see how this is surprising. Your RetrofitClient is a confusingly (and arguably badly written) singleton. Let's go through a typical situation where this will fail.
You launch your app with a previously saved token. At first everything works fine. At some point you call RetrofitClient.getClient(token) and all requests succeed. After some time the server invalidates the token. You probably get a 403 response from your server, lauch the login screen again and update your token in your SharedPreferences. Here is where your problems begin. Although you saved your new token correctly, your RetrofitClient will do what singletons do and continue to return the first instantiation of itself stored in the private static Retrofit retrofit filed.
A quick workaround would be to add an invalidate method to your RetrofitClient. Something like.
public static void invalidate() {
this.retrofit = null;
}
Call it when you get your 403 response, or when you logout.
PS: Please move the following line if (retrofit==null) { at the beginning of your getClient method. Creating a new okHttp client, for nothing, every time someone calls getClient is just wasteful.
You can write intercepter to execute each time before network request happen.Create new file as HeaderIntercepter
public class HeaderIntercepter implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String token = context.getSharedPreferences(FILENAME, MODE_PRIVATE).getString("TOKEN","");
Request tokenRequest = request.newBuilder()
.addHeader("Authorization", "Bearer " + token)
.addHeader("Content-Type", "application/json")
.build();
return chain
.proceed(tokenRequest);
}
}
Add intercepter to okHttpclient
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.addInterceptor(new HeaderIntercepter());

Basic authorization in retrofit

Interface:
#GET("burrowedbooks/")
Call<JsonArray> getCategoryList(#Header("Authorization") String token);
Usage:
private LibraryAPi service;
Retrofit retrofit = new Retrofit.Builder()
//.client(client)
.baseUrl(String.valueOf(R.string.base_url))
.addConverterFactory(GsonConverterFactory.create())
.build();
service = retrofit.create(LibraryAPi.class);
// Extract token from Shared Preferences.
SharedPreferences prefs = getActivity().getSharedPreferences(getString(R.string.login_data), MODE_PRIVATE);
String token = "Bearer "+prefs.getString("token","");
Call<JsonArray> categoryListResponseCall = service.getCategoryList(token);
categoryListResponseCall.enqueue(new Callback<JsonArray>() {
#Override
public void onResponse(Call<JsonArray> call, Response<JsonArray> response) {
int statusCode = response.code();
Toast.makeText(getContext(), ""+statusCode, Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<JsonArray> call, Throwable t) {
}
});
I'm trying to send authentication token stored in shared preferences. The code above is not working. It returns 403 forbidden status code. What is the correct way to send authentication header?
You are wrong at .baseUrl(String.valueOf(R.string.base_url))
You should get string from resource using .baseUrl(getActivity().getString(R.string.base_url))
But your code will not send data to the server and onFailure would be called.
If you get the string properly and still are getting 403, you may want to verify your back end implementation using postman.
Also you can create a custom interceptor to add your header automatically on new requests.
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
Request request = original.newBuilder()
.header("Authorization", token)
.method(original.method(), original.body())
.build();
return chain.proceed(request);
}
}
OkHttpClient client = httpClient.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
Also, check if token is received good from SharedPreferences. Looks odd how you read it.

Add Header to Retrofit Requests

I have a token which i save to sharedPreferences and then i get that token and pass it as an authorization to a Retrofit requests. This is my codes below which i used to add a header to my retrofit requests.
I need to add the header below:
"Authorization" "Bearer" + token
public static Retrofit getClient(String token) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient okClient = new OkHttpClient();
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.create();
okClient.interceptors().add(chain -> {
Response response = chain.proceed(chain.request());
return response;
});
okClient.interceptors().add(chain -> {
Request original = chain.request();
Request request = original.newBuilder()
.header("Authorization", token)
.method(original.method(), original.body())
.build();
return chain.proceed(request);
});
okClient.interceptors().add(logging);
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(Config.BASE_URL1)
.client(okClient)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
return retrofit;
}
This how i send my token to the retrofit client
Retrofit retrofit = RetrofitClient.getClient("Bearer" + " " + authUser.getToken());
APIService mAPIService = retrofit.create(APIService.class);
But unfortunately the server responds with the message no authorization
You can send header to server without using an interceptor. Just add a field in your method declaration in your service interface like this:
#GET("my/orders/{id}")
Call<Order> getOrder(#Header("Authorization") String token,
#Path("id") int order_id);
Then create a Call object to send request as below:
APIService apiService= retrofit.create(APIService.class);
Call<Order> call = apiService.getOrder(token, id);
call.enqueue(/*callback*/);
Add a method in the BaseCaller Class for your headers like below:
public HashMap<String, String> getHeaders() {
HashMap<String, String> headerHashMap = new HashMap<>();
headerHashMap.put("Content-Type", "application/x-www-form-urlencoded");
headerHashMap.put("time_zone_name", DateTimeHelper.getTimeZoneName());
headerHashMap.put("gmt_offset", DateTimeHelper.getGMTOffset());
return headerHashMap;
}
Now create a method in your service class for url like :
#FormUrlEncoded
#POST("switch_user")
Call<JsonObject> switchUser(#HeaderMap Map<String, String> headers, #FieldMap Map<String, String> fields);
Finally in your class caller class call the methods as follows :
call = loginService.switchUser(getHeaders(), apiParams.mHashMap);
This will do the needful :)
Try this for same:
String credentials = "put your token here";
final String basic = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization",basic);

Basic authentication with retrofit2 for cloudinary

Im trying to authenticate to Cloudinary API service using the below code but i get 401 unauthorized error, it expects credentials in this format https://API_KEY:API_SECRET#..., when i substitute with actual values it works great with browser/postman but fails with retrofit2, below is my code.
// create and initialize retrofit2 client
public static OkHttpClient getClient(){
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(Level.BASIC);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder()
.addHeader("API_KEY","API_SECRET")
.addHeader("Accept","Application/JSON").build();
return chain.proceed(request);
}
})
.addInterceptor(interceptor)
.build();
return client;
}
private static Retrofit retrofit = null;
public static Retrofit getClient(String baseUrl){
if (retrofit == null){
retrofit = new Retrofit.Builder()
.client(getClient())
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
// Interface with get methods to access image resources
public interface CloudinaryService {
#GET("resources/image")
Call<imageresponse> getImageResource();
}
// Util class to make requests
public class ApiUtils {
private static final String BASE_URL = "http://api.cloudinary.com/v...";
public static CloudinaryService getImageService(){
return RetrofitClient.getClient(BASE_URL)
.create(CloudinaryService.class);
}
}
Any help fixing the error will be highly appreciated, not sure if need custom converter. thanks
***** Edit******
public static String credentials = Credentials.basic(API_KEY,API_SECRET);
OkHttpClient client = new OkHttpClient.Builder()
// .authenticator(new Authenticator() {
// #Override
// public Request authenticate(Route route, Response response) throws IOException {
//
// return response.request().newBuilder().header("Authorization", credentials).build();
// }
// })
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = (chain.request().newBuilder()
.header("Accept","Application/JSON")
.header("Cache-Control", "public, max-age=" + 60)
.header("Authorization",credentials).build());
return chain.proceed(request);
}
})
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.addInterceptor(provideOfflineCacheInterceptor())
.addNetworkInterceptor(provideCacheInterceptor())
.cache(getCache())
.build();
return client;
}
I was able to fix the issue with adding authenticator to the builder.
.authenticator(new Authenticator() {
#Override
public Request authenticate(Route route, Response response) throws IOException {
return response.request().newBuilder().header("Authorization", credentials).build();
}
})
thanks for all your help.
request = chain.request();
builder = request.newBuilder();
if (TextUtils.isEmpty(request.header(AUTH)) && UserPreference.getInstance().isSignin())
builder.addHeader(AUTH, UserPreference.getInstance().getAccessToken());
if (NetUtil.hasNetwork(GridInnApplication.getInstance()))
builder.header(USER_AGENT, userAgent);
else
builder.cacheControl(CacheControl.FORCE_CACHE);
request = builder.build();
Response response = chain.proceed(request);
if (NetUtil.hasNetwork(GridInnApplication.getInstance())) {
String cacheControl = request.cacheControl().toString();
return response.newBuilder()
.header(CACHE_CONTROL, cacheControl)
.removeHeader(PRAGMA)
.build();
} else {
return response.newBuilder()
.addHeader(CACHE_CONTROL, CACHE_CONTROL_ONLY_CACHED)
.removeHeader(PRAGMA)
.build();
}
//you can results before returing intercept
The answer provided by leafNext will work but will cause every request to be sent twice - The authenticator only kicks in if the server responds with 401. You send the request, get 401 and then send it again with proper credentials.
The correct solution is to provide the credentials from the get go, using the interceptor. It's similar to what you tried to do originally, but you got the syntax wrong. The expected format is basic authentication.
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
// Request customization: add request headers
return chain.proceed(chain.request().newBuilder()
.header("Authorization", credentials).build());
}
});
Where credentials should follow the basic authentication protocol: Assuming the Api key is key and the secret is secret, you base64-encode the expression key:secret and prefix it with Basic. In this example the value of credentials should end up like so:
Basic a2V5OnNlY3JldA==
Edit - Added a fully working independent code bit to verify basic auth is working for okhttp (and thus with retrofit when using okhttp):
public int testBasicAuth() throws IOException {
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = (chain.request().newBuilder()
.header("Authorization",okhttp3.Credentials.basic(KEY, SECRET)).build());
return chain.proceed(request);
}
}).build();
Request request = new Request.Builder()
.url("https://api.cloudinary.com/v1_1/[cloud_name]/resources/image")
.build();
int code = client.newCall(request).execute().code();
return code; // 200
}

Categories