How to manage token authentication in android app - android

I am using JWT to manage server authentication for my application. I manage it in the server. In my angular app, I am using angular-jwt to manage jwt. If it expired how to call refresh token API. I am using volley for API call. When the token is expired the server responds with 401 error code. That time needs to call a refresh token api. After refresh call, i need to call the previous API call again without any user input.
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest
(method, request_url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
if (response.getInt("status") == 1) {
callback.onSuccessResponse(response.get("data"));
} else {
callback.onSuccessResponse(new JSONArray());
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
NetworkResponse networkResponse = error.networkResponse;
if (networkResponse != null && networkResponse.statusCode == 401) {
}
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
String token = AppPreferencesHelper.getAccessToken(context);
headers.put("Authorization", "Bearer " + token);
return headers;
}
};
Volley.newRequestQueue(context).add(jsonObjectRequest);

You need to call api to validate token, if its expired than you can generate new.
Or if you have login feature than you can check for JWT in login and generate new if old expired.
I have use a very basic way in my app, I am creating a JWT using a unique email address per user and stores it into database in server and it never expires. Whenever a user logout, I remove the JWT and when login again I generate new token.

Related

What is the right way use refreshToken?

I am working with an API that needs for some request an access token. The token get refreshed every 10 minutes, so i need to use a refresh token request. The problem is that the refresh token request requires a valid token that didn't expired yet. To summarize my question, is it normal that the refresh token requires unexpired token or there is another safe way to change the logic of refreshing it?
P.S: In the android app usually to refresh token you need get the first failed request than you request an new one and if you you choose to work with WorkManager you'll need at least 15 minutes for the periodicWorkRequest.
In my case this work me as below:
httpClient.authenticator(new Authenticator() {
#Nullable
#Override
public Request authenticate(Route route, Response response) throws IOException {
String SavedAccessToken = "Bearer "+MyApplication.getLoginUser().getAccessToken();
String responseHeader = response.request().header("Authorization");
if (!SavedAccessToken.equals(responseHeader))
return null;
// request new key
String accessToken = null;
Call<ResponModel<User>> call = RetrofitManager.getRetrofitManager().getApiService().requestAccessToken(MyApplication.getLoginUser());
try {
retrofit2.Response responseCall = call.execute();
ResponModel<User> responseRequest = (ResponModel<User>) responseCall.body();
if (responseRequest != null) {
accessToken = responseRequest.getData().getAccessToken();
User user = new User();
user.setUsername(PrefsUtils.get("UserName","").toString());
user.setAccessToken(accessToken);
user.setRefreshToken(responseRequest.getData().getRefreshToken());
MyApplication.updateUser(user);
}
} catch (Exception ex) {
Log.d("ERROR", "onResponse: " + ex.toString());
}
if (accessToken != null)
// retry the failed 401 request with new access token
return response.request().newBuilder()
.header("Authorization", "Bearer "+ accessToken) // use the new access token
.build();
else
return null;
}
});

ANDROID: onesignal notifications using tags

I am creating push notifications using onesignal,I send push notifications by player ids but it has a limitation.
I have a login system and i want to send notifications for different users and if two or more users login from same device then i can't differentiate between them.
Right now i have a table and whenever user logins it adds the playerid to that user and then through php I send notifications by playerId which is in the user table
I searched a lot and couldn't find a solution
May be your approach is wrong you need to de-register user as soon as user logs out.
Simple words you need to remove that user from device ID table when ever user do logout.
You can add tags to users and send notifications based on tags attached to different users and send notifications to them by making a POST using the URL https://onesignal.com/api/v1/notifications
Segments can be created only by using the OneSignal dashboard, but tags can be created by using the SDK/API.
Add Headers "Content-Type":"application/json" and "Authorization":"Basic <REST API Key>"
In body add
{
"app_id":"<App Id>",
"headings":{
"en":"Title Here"
},
"contents":{
"en":"Description Here"
},
"filters":[
{
"field":"tag",
"key":"level",
"relation":"=",
"value":"10"
},
{
"operator":"OR"
},
{
"field":"amount_spent",
"relation":">",
"value":"0"
}
]
}
Then make a JSON object request to complete the process.
You can refer the following code to see how to attach headers to a JSON object request & to attach tags to your users. (This request has been made by using android volley)
String url = "https://onesignal.com/api/v1/notifications";
JSONObject jsonBody;
try {
jsonBody = new JSONObject(
"{'app_id':'app-id'," +
"'headings': {'en': 'title'}, " +
"'contents': {'en': 'message'}," +
"'filters':[{'field':'tag','key':'"+id+"','relation':'=','value':'true'}]}"
);
//request a json object response
JsonObjectRequest jsonRequest = new JsonObjectRequest(Request.Method.POST, url, jsonBody, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
//now handle the response
Toast.makeText(Activity.this, "Notification successfully sent", Toast.LENGTH_SHORT).show();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
//handle the error
Toast.makeText(Activity.this, "An error occurred", Toast.LENGTH_SHORT).show();
error.printStackTrace();
}
})
{ //adding header to the request
#Override
public Map<String, String> getHeaders() {
Map<String, String> params = new HashMap<String, String>();
params.put("Authorization", "Basic <REST API KEY>");
params.put("Content-type", "application/json");
return params;
}
};
// Add the request to the queue
Volley.newRequestQueue(Activity.this).add(jsonRequest);
} catch (JSONException e) {
e.printStackTrace();
}
Attaching tags to a user
JSONObject tags = new JSONObject();
try {
tags.put("key","value");
//for the above JSON request I have used the following key value pair
// tags.put(id,true);
// where id is a string containing the userId
//true is a boolean value
} catch (JSONException e) {
e.printStackTrace();
}
OneSignal.sendTags(tags);
This should successfully complete your query.

Django Rest Auth Token in Android Volley

So I'm running into a problem when calling rest-auth/user/.
I am able to login and obtain the key from said login, but from that I'm not sure how to use it in regards to rest-auth/user/. I've tried using it with GET in volley, as well as POSTing it in volley. But everytime I try to do so, I get a 403 back saying credentials were not provided. I've also tried saving the token to Android's SharedPreferences.
I'm not sure what could be wrong or how to fix this problem, so any help would be appreciated.
My code looks like this:
getUserQueue = Volley.newRequestQueue(this);
JSONObject jsObj = new JSONObject();
try {
jsObj.put("token", token);
} catch (JSONException e) {
e.printStackTrace();
}
JsonObjectRequest jsObjRequest = new JsonObjectRequest
(Request.Method.GET, "http://hurst.pythonanywhere.com/supportal/rest-auth/user/", jsObj, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
result = response.getString("username");
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
});
// add the request object to the queue to be executed
getUserQueue.add(jsObjRequest);
From comments it is clear that you are using different authentication schemes for rest, will explain these,
SessionAuthentication
For authenticating session authentication you need persistant cookie implementation in android volley, check here for this, where Set-Cookie header is parsed from server api response & send over next requests.
BasicAuthentication
In Basic authentication scheme, username & password are send over every request ( after login ?), with Authorization header value ( Base64 encoded).To check how to implement this check here
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> params = new HashMap<String, String>();
String creds = String.format("%s:%s","USERNAME","PASSWORD");
String auth = "Basic " + Base64.encodeToString(creds.getBytes(), Base64.DEFAULT);
params.put("Authorization", auth);
return params;
}
JSONWebTokenAuthenticatio‌​n
In JWT authentication scheme, after successful login you will get a JWT token, you need to send this token in every request that need user authorization, for this to work, set Authorization header value with JWT token_after_login;Only difference with Basic authentication is how header is send.
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> params = new HashMap<String, String>();
String auth = "JWT " + token // token you will get after successful login
params.put("Authorization", auth);
return params;
}

Where to store a JWT token?

I'm implementing a REST service that requires authentication. I am using JWT.
Now the Android App sends a request when logging in, gets a token, and has to send the token in the header for every subsequent request.
My question is, how to store the token, or where should I store it?
Shared Preferences
SQLite Database
In a file
What would be the best practice way to do it? Or am I going about this the totally wrong way?
If you are using REST service and want to store JWT the best way available is SharedPreferences.You should store in PrivateMode for security.
SharedPreference and SharedPreference.Editor is used to store and retrieve JWT. JWT is retrieved after POST request of Username and Password
private void makeJsonRequest() {
String json_req = "json_req";
// String url = getContext().getString(R.string.LOGIN_URL);
String url="";
final JSONObject obj=new JSONObject();
try{
obj.put("username",name);
obj.put("password",pass);
}catch (JSONException e)
{
e.printStackTrace();
}
JsonObjectRequest req = new JsonObjectRequest(Request.Method.POST, url, obj,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<>();
return headers;
}
};
AppController.getInstance().addToRequestQueue(req, json_req);
To retrieve JWT from response and save in shared preference use
SharedPreferences prefs;
SharedPreferences.Editor edit;
prefs=getActivity().getSharedPreferences("myPrefs",Context.MODE_PRIVATE);
edit=prefs.edit();
try {
String saveToken=response.getString("token");
edit.putString("token",saveToken);
Log.i("Login",saveToken);
edit.commit();
}
catch (JSONException e)
{
e.printStackTrace();
}
To get Token from SharedPreference
private void getToken() {
prefs=this.getActivity().getSharedPreferences("myPrefs",Context.MODE_PRIVATE);
String token = prefs.getString("token","");
}
I found this ans here (src)
If you’re writing an Android app, for instance, you’ll want to store all access tokens in SharedPreferences (here’s the API docs you need to make it work). If you’re an iOS developer, you will want to store your access tokens in the Keychain.
for ios
for android

how to solve 401 error

I am trying to sent JSON format data (using Volley) from two EditText-views and a method that return unique Device ID to a URL from my Android application and I receive
"[8970] BasicNetwork.performRequest: Unexpected response code 401 for https://gorountsiallyetlasornall:5wxGq5UNlY6wdWmNAyYPVVrN#bulberadev.cloudant.com/notebook"
Here is My method:
private void doPost() {
final String url = "https://gorountsiallyetlasornall:5wxGq5UNlY6wdWmNAyYPVVrN#bulberadev.cloudant.com/notebook";
final String deviceId = getDeviceId(getApplicationContext());
try {
try {
JSONObject jsonObject = new JSONObject();
String title = editTitle.getText().toString();
String content = editContent.getText().toString();
jsonObject.put("title", title);
jsonObject.put("content", content);
jsonObject.put("deviceId", "<" + deviceId + ">");
} catch (JSONException e) {
e.printStackTrace();
}
requestQueue = Volley.newRequestQueue(this);
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST,
url, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
VolleyLog.v("Response:%n %s", response.toString(4));
} catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// Log.e("VOLLEY", "ERROR");
}
});
requestQueue.add(jsonObjectRequest);
} catch (Exception e) {
e.printStackTrace();
}
}
it should be in a format :
{
"title":"Birth day",
"content":"Buy a gift for my mom!",
"deviceId":"<Device ID>"
}
A 401 is an Unauthorized error.
https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Client_Error
This means that the user and password is not getting recognized. You're providing it by means of the URL, but this only works for the browser. If the service you're using accepts Basic HTTP authorization headers, this code will provide you the needed headers:
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> params = new HashMap<String, String>();
String creds = String.format("%s:%s","USERNAME","PASSWORD");
String auth = "Basic " + Base64.encodeToString(creds.getBytes(), Base64.DEFAULT);
params.put("Authorization", auth);
return params;
}
Original code from https://stackoverflow.com/a/18980454/3286819
Of course, username and password needs to be your own. In this case:
username: gorountsiallyetlasornall
password: 5wxGq5UNlY6wdWmNAyYPVVrN
URL: https://bulberadev.cloudant.com/notebook (notice I've removed the user and password)
Some more info: https://yakivmospan.wordpress.com/2014/04/04/volley-authorization/
Error 401 is an HTTP error for unauthorised. This is not a Volley or android related fault. In this case the URL you have provided
https://gorountsiallyetlasornall:5wxGq5UNlY6wdWmNAyYPVVrN#bulberadev.cloudant.com/notebook
Cannot be interpreted by Volley as a login either. This url is sometimes used by cURL and other tools to hardcode the username in password into the URI for Basic Authentication HTTP.
For this your username is gorountsiallyetlasornall and your password is 5wxGq5UNlY6wdWmNAyYPVVrN.
According to Basic Autentication, explained https://luckymarmot.com/paw/doc/HTTP_Basic_Auth
It needs to be converted to Base 64 then added to the header of the request.
I have converted your username and password into a Basic Auth base 64 encoded String for you bellow.
Z29yb3VudHNpYWxseWV0bGFzb3JuYWxsOjV3eEdxNVVObFk2d2RXbU5BeVlQVlZyTg==
Add this into your Volley header by extending a Volley request and overriding the function getHeaders to return a HashMap with the following key value pair.
"Authorization" , "Basic Z29yb3VudHNpYWxseWV0bGFzb3JuYWxsOjV3eEdxNVVObFk2d2RXbU5BeVlQVlZyTg"
Your request will now work.
Please let me know if you want a more detailed explanation.
PS. Hopefully the values you posted in your question is not your real username and password. If so, then don't do that in the future.

Categories

Resources