How to add token from shared preferences to an interceptor - java

After I authenticate and get JWT in header I want to add token to every request sent from person logged in with this token.
But I don't know how to pass token from shared preferences to my Api client class.
I tried defining sharedPref object, but it needs Context to use .getSharedPreferences method.
This is how I retrieve and save token from authentication endpoint: (this is in loginActivity class)
private void login() {
LoginRequest loginRequest = new LoginRequest();
loginRequest.setUsername(usernameLog.getText().toString().trim());
loginRequest.setPassword(passwordLog.getText().toString().trim());
Toast.makeText(getApplicationContext(), "Clicked", Toast.LENGTH_SHORT).show();
Call<Void> loginResponseCall = ApiClient.getUserService().login(loginRequest);
loginResponseCall.enqueue(new Callback<Void>() {
#Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
token = response.headers().get("Authorization");
SharedPreferences preferences = getSharedPreferences("logged", MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("isLogged", true);
editor.putString("token", token);
editor.apply();
Toast.makeText(getApplicationContext(), "User logged in successfully", Toast.LENGTH_SHORT).show();
startActivity(new Intent(LoginActivity.this, MainActivity.class));
finish();
}
}
#Override
public void onFailure(Call<Void> call, Throwable t) {
Toast.makeText(getApplicationContext(), "User failed logging in: " + t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
}
});
}
And this is my ApiClient class, where I'm trying to use token saved from Shared Preferences.
public class ApiClient {
private static Retrofit getRetrofit() {
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(chain -> {
Request newRequest = chain.request().newBuilder()
.addHeader("Authorization", TOKEN)
// Instead of "TOKEN" I need to somehow pass
//
// SharedPreferences preferences = getSharedPreferences("logged", MODE_PRIVATE);
// preferences.getString("token", "");
.build();
return chain.proceed(newRequest);
})
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.1.60:8080/")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
return retrofit;
}
public static UserService getUserService() {
UserService userService = getRetrofit().create(UserService.class);
return userService;
}
}
When I hard code it, it works. But It needs to pass token dynamically depending on which user is logged in.

Related

How can I use the Retrofit response outside the OnResponse function so I can return it up the recommended architecture model?

This question follows on from How can I use the Retrofit response outside the OnResponse function?, but I'm not allowed to comment, so I'm asking it for myself here.
I'm trying to use the Android Studio Login template because it follows the recommended architecture, but I'm having trouble returning the Result in LoginDataSource.login. The result is trapped in the Call.enqueue function and I can't get it out to return. I've reproduced the callback suggested in the above link, but that just traps the result in a new class.
How can I access the LoggedInUser returned by my server to return to my repository?
Original attempt: user is stuck in Call.enqueue - onResponse
public class LoginDataSource {
public static Retrofit retrofit = null;
LoggedInUser user;
public Result<LoggedInUser> login(String username, String password) {
user = new LoggedInUser();
try {
// TODO: handle loggedInUser authentication
retrofit = new Retrofit.Builder()
.baseUrl("https://myserver")
.addConverterFactory(GsonConverterFactory.create())
.build();
PostEndPoint endPoint = retrofit.create(PostEndPoint.class);
Call<LoggedInUser> call = endPoint.getUser("login", username, password);
call.enqueue(new Callback<LoggedInUser>() {
#Override
public void onResponse(Call<LoggedInUser> call, Response<LoggedInUser> response) {
Log.d(TAG, "onResponse: code " + response.code());
if (response.isSuccessful()){
callback.getUser();
user = response.body();
Log.d(TAG, "onResponse: " + user); // this returns valid user data
}
}
});
Log.d(TAG, "retrofit complete:" + user.getName()); // this returns null
return new Result.Success<>(user);
}
}
}
And after implementing callback: user is stuck in GetUserResponse - getUser
public class LoginDataSource {
public static Retrofit retrofit = null;
LoggedInUser user;
public Result<LoggedInUser> login(String username, String password) {
user = new LoggedInUser();
try {
// TODO: handle loggedInUser authentication
retrofit = new Retrofit.Builder()
.baseUrl("https://myserver")
.addConverterFactory(GsonConverterFactory.create())
.build();
PostEndPoint endPoint = retrofit.create(PostEndPoint.class);
Call<LoggedInUser> call = endPoint.getUser("login", username, password);
sendLoginRequest(call, new GetUserResponse(){
#Override
public void getUser(LoggedInUser userFromResponse) {
user = userFromResponse;
}
});
Log.d(TAG, "retrofit complete:" + user.getName()); // this returns null
return new Result.Success<>(user);
}
}
private void sendLoginRequest (Call call, final GetUserResponse callback) {
call.enqueue(new Callback<LoggedInUser>() {
#Override
public void onResponse(Call<LoggedInUser> call, Response<LoggedInUser> response) {
if (response.isSuccessful()){
callback.getUser(response.body());
}
}
});
}
}
public interface GetUserResponse {
void getUser(LoggedInUser user);
}
I feel like I need to have sendLoginRequest return the user, but I can't work out how to do that. Am I heading in the right direction? Any advice is welcome.
I switched to Kotlin along the way, so this may not be correct Java, but it shows the process
Set the user model as LiveData
update the user model using .postValue()
set up an observer for the user model in the viewmodel (not shown)
public class LoginDataSource {
public static Retrofit retrofit = null;
MutableLiveData<LoggedInUser> user=new MutableLiveData<>(); // changed this to LiveData so it can be observed from the viewmodel
public Result<LoggedInUser> login(String username, String password) {
user = new LoggedInUser();
try {
// TODO: handle loggedInUser authentication
retrofit = new Retrofit.Builder()
.baseUrl("https://myserver")
.addConverterFactory(GsonConverterFactory.create())
.build();
PostEndPoint endPoint = retrofit.create(PostEndPoint.class);
loggedInUser = endPoint.getUser("login", username, password);
user.postValue(loggedInUser); // updated the livedata here
}
}
}

Android - Display loading bar when calling API with retrofit

In my app i am using retrofit 2 to retrieve data from a API. I have no problem about this. The problem is that i want to display a loading bar while this execution. The code is this
Call<MainInvestorProducts> call = apiInterface.getUseraccounts("Bearer "+bearerToken);
mkLoader.setVisibility(View.VISIBLE);
call.enqueue(new Callback<MainInvestorProducts>() {
#Override
public void onResponse(Call<MainInvestorProducts> call, Response<MainInvestorProducts> response) {
// If success response set the textViews
if (response.code() == 200) {
retrievedData = response.body();
//else display error message
}else if (response.code() == 401) {
Toasty.error(getApplicationContext(), getString(R.string.expired_token),Toasty.LENGTH_LONG).show();
finish();
}
}
#Override
public void onFailure(Call<MainInvestorProducts> call, Throwable t) {
}
});
mkLoader.setVisibility(View.GONE);
The problem is that mkloader is never show up.
My APIClient code
public class APIClient {
public static Retrofit retrofit = null;
public static Retrofit getClient(){
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
retrofit = new Retrofit.Builder()
.baseUrl("https://api-test01.moneyboxapp.com")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
return retrofit;
}
}
And this is an example of the Interface
#Headers({
"AppId: 3a97b932a9d449c981b595",
"Content-Type: application/json",
"appVersion: 5.10.0",
"apiVersion: 3.0.0"
})
#POST("/users/login")
Call<MainUserLogin> logInUser(#Body LoginBody loginBody);
Before calling the api you should start showing the loader (be sure that the api call is going to happen) and when once completed dismiss that
mkLoader.setVisibility(View.VISIBLE);
Call<MainInvestorProducts> call = apiInterface.getUseraccounts("Bearer "+bearerToken);
call.enqueue(new Callback<MainInvestorProducts>() {
#Override
public void onResponse(Call<MainInvestorProducts> call, Response<MainInvestorProducts> response) {
// If success response set the textViews
if (response.code() == 200) {
retrievedData = response.body();
//else display error message
}else if (response.code() == 401) {
Toasty.error(getApplicationContext(), getString(R.string.expired_token),Toasty.LENGTH_LONG).show();
finish();
}
}
#Override
public void onFailure(Call<MainInvestorProducts> call, Throwable t) {
mkLoader.setVisibility(View.GONE);
}
});
you just need to make the loader visibility to VISIBLE before calling the API and in case the api is succes or failure you set visibility to GONE

Retrofit 2 returns HTML instead of JSON

I am making a POST Request to an ASP.NET API using Retrofit 2, I am getting an HTML in response instead of JSON. If I change the target URL and call a different API and get JSON response
Here is my API interface
#POST("PosLogin")
Call<CinekinRequest> login();
Rest Manager
public static final String BASE_URL = "******************";
private API homeApi;
public API getAPi() {
if (homeApi == null) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(logging);
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.client(httpClient.build())
.build();
homeApi = retrofit.create(API.class);
}
return homeApi;
}
Executing my login()
public void login(final Context context, CinekinRequest login){
Log.e("login", "starting");
Call call = manager.getAPi().login( );
call.enqueue(new Callback<CinekinRequest>() {
#Override
public void onResponse(Call<CinekinRequest> call, Response<CinekinRequest> response) {
Toast.makeText(context, "Success " + response.message(), Toast.LENGTH_SHORT).show();
Log.e("res", "success");
}
public void onFailure(Call<CinekinRequest> call, Throwable t) {
Toast.makeText(context, "error: " + t.getMessage(), Toast.LENGTH_LONG).show();
Log.e("error", t.getMessage());
}
});
}
Please add statement below in retrofit onResponse
if (response.isSuccessful()) {
// Do something
} else {
// Do something
}
or if you want only accept response 200
if (response.code() == 200) {
// Do something
} else {
// do something
}

Android: Execute enqueue first Retrofit

I have some troubles trying to execute this code:
#Override
public void loginProcessGoogle(User googleUser) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.callbackExecutor(Executors.newSingleThreadExecutor())
.addConverterFactory(GsonConverterFactory.create())
.build();
RequestInterface requestInterface = retrofit.create(RequestInterface.class);
User user = new User();
String email = googleUser.getEmail() == null ? "" : googleUser.getEmail();
String name = googleUser.getName();
String googleId = googleUser.getProvider_id();
user.setEmail(email);
user.setName(name);
user.setProvider_id(googleId);
user.setProvider_name(User.provider_name_google);
ServerRequest request = new ServerRequest();
request.setUser(user);
Call<ServerResponse> response = requestInterface.socialAuthenticate(request);
response.enqueue(new Callback<ServerResponse>() {
#Override
public void onResponse(Call<ServerResponse> call, retrofit2.Response<ServerResponse> response) {
ServerResponse resp = response.body();
Snackbar.make(getView(), resp.getMessage(), Snackbar.LENGTH_LONG).show();
if(resp.getResult().equals(Constants.SUCCESS)){
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean(Constants.IS_LOGGED_IN,true);
editor.putString(Constants.EMAIL,resp.getUser().getEmail());
editor.putString(Constants.NAME,resp.getUser().getName());
editor.putString(Constants.ID,resp.getUser().getId());
editor.apply();
goToProfile();
}
progress.setVisibility(View.INVISIBLE);
}
#Override
public void onFailure(Call<ServerResponse> call, Throwable t) {
progress.setVisibility(View.INVISIBLE);
Log.d(Constants.TAG,"failed");
Snackbar.make(getView(), t.getLocalizedMessage(), Snackbar.LENGTH_LONG).show();
}
});
String testString= "Hello";
}
When im debuggin this code, i put a breakpoint for example in user.setEmail(email);,it enters, also with another before the response.enqueue callback, when im trying to put a breakpoint inside onResponse method, it goes immediately to the string variable in the bottom (testString)
What is the best way to enter first into the response.enqueue before the string variable in the bottom, for example in ServerResponse resp = response.body();?
You cannot, because the enqueue method run in another thread with the thread you setEmail, untill retrofit receive the result it execute onResponse method.
Yo can use excute method instead enque. See here for more details https://futurestud.io/tutorials/retrofit-synchronous-and-asynchronous-requests.

Retrofit says auth token exists when logged out

When I log into my account in my app, I save an auth token in my SharedPreferences, like this:
PreferenceUtils.setAuthToken(LoginActivity.this, authToken);
Here is my PreferenceUtils class:
public class PreferenceUtils {
public static SharedPreferences getSharedPreferences(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context);
}
public static String getAuthToken(Context context) {
SharedPreferences sp = getSharedPreferences(context);
return sp.getString("auth_token", null);
}
public static void setAuthToken(Context context, final String token) {
SharedPreferences sp = getSharedPreferences(context);
sp.edit().putString("auth_token", token).apply();
}
}
When I log out of the account, I delete the auth token by calling the logOut() method in my UserUtils class:
public class UserUtils {
public static void logOut(Context context) {
SharedPreferences prefs = PreferenceUtils.getSharedPreferences(context);
SharedPreferences.Editor editor = prefs.edit();
editor.remove("auth_token");
editor.apply();
}
}
However, even after logging out of my account and removing the auth token from SharedPreferences, all Retrofit calls still somehow have the auth token saved and I'm not sure how.
In other words, when I log out of my account and Retrofit makes a new call, it will print out the auth token that I thought I had deleted when the user logged out.
Only when I restart my app does the auth token get fully removed.
Why is it doing this?
Here is my Retrofit client class (note the comment):
public class ApiClient {
public static final String API_BASE_URL = "https://www.example.com/";
private static OkHttpClient.Builder httpClient =
new OkHttpClient.Builder();
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
private static Retrofit retrofit = builder.build();
private static HttpLoggingInterceptor logging =
new HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BODY);
public static Retrofit getRetrofit() {
return retrofit;
}
public static <S> S createService(Class<S> serviceClass) {
if (!httpClient.interceptors().contains(logging)) {
httpClient.addInterceptor(logging);
builder.client(httpClient.build());
retrofit = builder.build();
}
return retrofit.create(serviceClass);
}
public static <S> S createService(Class<S> serviceClass, final String authToken) {
if (authToken != null) {
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
// THIS STILL PRINTS THE AUTH TOKEN EVEN AFTER I'VE
// REMOVED IT FROM THE SHARED PREFERENCES
Log.d("AUTH TOKEN", authToken);
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", "Bearer " + authToken)
.method(original.method(), original.body());
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
}
OkHttpClient client = httpClient.build();
Retrofit retrofit = builder.client(client).build();
return retrofit.create(serviceClass);
}
}
How do I fix this??
Most likely the reason you still are seeing the token is because while you delete the token from the share preferences, you never reset the variable in the program. You need to make sure that you set the variable to null or empty, not just delete it from shared preferences.
As you requested, here is my previous comment as an answer:
It's because the interceptor you added is still alive in the httpClient. Once you've removed the token (or pass a null to the createService() method, you need to remove the interceptor as well.

Categories