I have an Android Application which talks to a server through REST APIs web services. I need to apply session management in android application. At the server side if there is 15 mins of inactivity the user will get logged out and a new authentication token is generated.I want to do session management in my android application. I am using Volley for the network calls.
REASON behind doing this:
I want to apply session management because after 15 mins of inactivity the server will generate a new token key and invalidate the session. Then the android application needs to have the new token key generated by the server for authentication and successful web service call.
What I have till now for session management in Android:
My MainActivity code:
public class MainActivity extends AppCompatActivity {
public static final long DISCONNECT_TIMEOUT = 600000;// 15 min
private Handler disconnectHandler = new Handler() {
public void handleMessage(Message msg) {
}
};
private Runnable disconnectCallback = new Runnable() {
#Override
public void run() {
// Perform any required operation for log out
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
}
};
public void resetDisconnectTimer() {
disconnectHandler.removeCallbacks(disconnectCallback);
disconnectHandler.postDelayed(disconnectCallback, DISCONNECT_TIMEOUT);
}
public void stopDisconnectTimer() {
disconnectHandler.removeCallbacks(disconnectCallback);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public void onUserInteraction() {
resetDisconnectTimer();
}
#Override
public void onResume() {
super.onResume();
resetDisconnectTimer();
}
#Override
public void onStop() {
super.onStop();
stopDisconnectTimer();
}
}
How can I check if the session has be timed out at the server side or How can I logout the user after 15mins of inactivity.
Like you said, In your server side, you should have a token and a expiration date which you should always check, on every user request. Token becomes invalid If the date expired and no more response will user get until start new session (this way, you can redirect to login page, because server response is 'invalid token' or ' session time ellapsed').
In other words, in a simply way, no one needs to be watching if session is already expired. Only when user makes a new request, there the server validates user session. It's secure enough.
As previous Answers stated you should let the server ( the REST API ) handle that.
On every request from your android application send the token you have as a header ( just best practice ) and let the server validate that token first at all times before doing anything else. If the token is not valid anymore you can let him send you a special response with HTTP 401 or some other HTTP Code, telling you that your session timed out.
In your Android application you have to handle this response and automatically start/redirect to the login. Once he is logged in everything is as usual.
If you want you can try to remember where the user was before getting thrown out and redirect back to that activity after he is authorized again.
Here are some resources for REST session management:
https://www.quora.com/What-is-the-best-way-of-session-management-in-REST-architecture
If REST applications are supposed to be stateless, how do you manage sessions?
http://blog.synopse.info/post/2011/05/24/How-to-implement-RESTful-authentication
https://www.owasp.org/index.php/REST_Security_Cheat_Sheet
You don't have to use any session management in android application, better for every request you have to send the token within header params, in server-side the token authentication is taken care and returns the response JSON, in client-side you have to apply logic with respect to response JSON
Related
i was try to create login session with session key, the session key always generate new key either we do Login/registration, i can retrieve the data from my gson
LoginService loginService = retrofit.create(LoginService.class);
Observable<LoginResponse> obs = loginService.logins(emai,pass,"1", Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID), Build.MODEL, Build.VERSION.RELEASE);
obs.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).
subscribe(new Observer<LoginResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
public void onNext(LoginResponse loginResponse) {
int responses = loginResponse.getCODE();
String texts="";
if(responses == 1)
{
User user = loginResponse.getDATALIST().get(0);
setPrefIsLogin(true);
setPrefSessionUid(user.getSESSIONKEY(),user.getUSERID());
nextActivity();
}
else{
}
}
});
the question is, how to make handler to handle the save session check if there is another login activity with the same account?
You should never assign two accessToken/Session for one user. You will send the same accessToken to the other instance of the new user. Plus side, user won't be able to duplicate his/her work by two accessToken.
If you want to force the other/first one AUTO-LOGOUT, you can use Firebase notification feature to send a notification to that particular deviceID and force it to stop. You can check firebase's tutorial to see how they work.
Another slow procedure is to check before login & everyother server side work to check if there are instance of same user. You will send an error and user end will receive it and show the error accompanying by logging out the user.
I have an android app with a login that sends login information over https to a java rest api that verifies the login credentials and then sends back a response stating whether the login was successful. My question is simple, what should I do to make sure the user doesn't have to login in again when they restart the app?
There are many ways to deal with one time login, it depends a lot on how the architecture is implemented on server side to make it work. Usually Login API are closely coupled for security reason. let me example what I mean by closely coupled.
As you want to Login in to Mobile App work once and the next time user opens the Mobile app you don't want to prompt user with SignIn screen again. and surely you don't want to save the confidential information like Username and Password on Mobile app as persistent data, as it can be fetched easily from android device. so what you do.
Lets assume you pass login credentials with a deviceID unique to the Android Device. Something like shown below. following is the JSON data sent to the LoginAPI
{
"username": "example#example.com",
"password": "it's not a secret",
"deviceId": "123456789"
}
Now as you don't want to save the Login credentials, server will generate a random alpha numeric String pass it to you in response every time you login to the Mobile App.
{
"Success": true,
"SuccessMessage": "credentials are correct, really!",
"ErrorMessage": null,
"Date": "dd/mm/yyyy",
"token": "1eghe4qha23aehraeh456789" // now this is a nasty String
}
you may now save the date and the token in Mobile App as persistent data.
So next time your user opens the app you may let user bypass the SignIn screen, and in background you can check if users token id is correct by sending that to the server, something like this. you may choose SharedPreferences or create a file and save it there
{
"API_TYPE": "login",
"deviceId": "123456789",
"token": "1eghe4qha23aehraeh456789"
}
Server may check this token id against the deviceID to check if this was the correct token for the device and respond.
You may ask why are we checking the token again as this was sent by the server in first place stating the credentials were correct. I agree you do have a point, lets say it is a fail safe condition, if the user changes the password from the website or something which causes to change the token on server for that user to change, if the server denies the token provided by you just ask the user to login again.
This will make sure that a user is logged in to only one Android Device at any given point of time.
You can store the credentials after the first login.
So when the user restart app, you can automatically make the request auth.
You can chose the best option to storage the credentials using the doc:
Data-Storage
This is simple, but maybe not the best way to do this.
I have the same problme,and I got a Json data from the server like this
{"error":0,"message":"ok","token":"7c75015e92e40511911e34752ee456e1","execute_time":"0.2723"}
so I keep the StuTokenin SharedPreferences,and when my app start,check the StuToken does it exist,like this
Map<String, String> loginInfo = InfoUtil.getLoginInfo(MainActivity.this);
if (loginInfo != null) {
if (loginInfo.get("StuToken") != null) {
getStuInfo(loginInfo.get("StuToken"));
Toast.makeText(MainActivity.this, "登录状态", Toast.LENGTH_SHORT).show();
} else {
initIntent(LoginActivity.class);
this.finish();
}
} else {
initIntent(LoginActivity.class);
this.finish();
}
}
hope that will works for you
You can use shared preference to store the values of the user and to check whether the user is already logged in or not. You can follow this Link.
Try out this code in your SplashScreen Activity.You can store a value in a sharedPrefence to check whether user is Logged in or not.
public class SplashActivity extends AppCompatActivity {
private static final long TIME_OUT_MILI = 3000;
private SharedPreferences mAppPreferences;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
mAppPreferences = AppUtil.getAppPreferences(this);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
if (mAppPreferences.getBoolean("is_Logged_in", false)) {
startActivity(new Intent(SplashActivity.this, MainActivity.class));
} else {
startActivity(new Intent(SplashActivity.this, LoginActivity.class));
}
finish();
}
}, TIME_OUT_MILI);
}
}
In Your LoginActivity's do this:
public class LoginActivity extends AppCompatActivity {
private SharedPreferences mAppPreferences;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
SharedPreferences.Editor editor = mAppPreferences.edit();
editor.putBoolean(Constants.SETTINGS_IS_LOGGED_IN, true);
editor.commit();
}
}
We are using:
BMSClient.getInstance().registerAuthenticationListener("realm", new CustomAuthentication(this));
and:
AuthorizationManager.createInstance(this.getApplicationContext());
AuthorizationManager.getInstance().setAuthorizationPersistencePolicy(AuthorizationManager.PersistencePolicy.ALWAYS);
to store the authorization data on the phone. "The authorization data will be saved on local storage" is set to ALWAYS.
The code above is always run on our splashscreen on startup so it is always run when the app restarted.
The problem we have had is that after some time (hours or days) when what we suspect the token has expired we get response in the form of HTTP 307. Even after restarting the app we keep getting this response on our requests. The only way to get around it is to go into the app from settings and clear all data.
The following questions would help us go forward in our testing and possible solution:
How long is the token cached in BMSClient? (testing purposes)
Can AuthorizationManager help us in any way to force a new fetch of token?
Are they working on log out functionality?
Our custom listener:
public class CustomAuth implements AuthenticationListener {
private Context activityContext;
public CustomAuth(Context activityContext) {
this.activityContext = activityContext;
}
#Override
public void onAuthenticationChallengeReceived(AuthenticationContext authContext, JSONObject challenge, Context context) {
//1. read the challenge JSONObject
//2. handle the challenge (use the context for handling UI based operations)
//3. return response using the AuthenticationContext authContext
SharedPreferences preferences = activityContext.getSharedPreferences("UserPreference", Context.MODE_PRIVATE);
String email = preferences.getString("email", "");
if(email.equals("")) {
email = "unidentified-user#error.com";
}
JSONObject jsonEmail = new JSONObject();
try {
jsonEmail.put("email", email);
} catch (JSONException e) {
authContext.submitAuthenticationChallengeAnswer(null);
}
authContext.submitAuthenticationChallengeAnswer(jsonEmail);
}
#Override
public void onAuthenticationSuccess(Context context, JSONObject info) {
//additional operations in case of authentication success
Log.d("Authentication", "Auth success: " + String.valueOf(info));
}
#Override
public void onAuthenticationFailure(Context context, JSONObject info) {
//additional operations in case of authentication failure
Log.d("Authentication", "Auth failure ." + String.valueOf(info));
}
}
Have you tried using AuthorizationManager.clearAuthorizationData() API when 307 is received and resending the request?
To answer your questions:
1) The authorization token will be cached indefinitely. The token expires after a 60 minute time period, but will remain cached until a new token is obtained.
It is best practice to obtain a new token once the original token has expired. This can be accomplished by running a new authorization challenge once the previous token has expired.
2) You can always use AuthorizationManager to obtain a new token once the previous token has expired by accessing a protected resource, using obtainAuthorizationHeader, etc.
3) There is currently no way to log out of MCA using the AuthorizationManager. I will speak with the development team about future plans.
In regards to the main problem I see in your question. I would expect you are experiencing this problem because you are attempting to use an expired token against the authorization service. Even though the token is still cached on the device it expired an hour after creation. I would attempt to run a new authorization challenge against the MCA service once the token expires.
If you want to provide your code that may also help me investigate further.
I need to identify user who made request to my endpoint api, via Android client. Though I am able to follow best practice by keeping my Api and App within one project using gradle and android studio. Also I am able to send request to my endpoint api and receive response without authorization.
Basically I need to send authorization token as header in the request, people suggest that, merely by adding instance of "GoogleAccountCredential" along with the request will do the trick as in the code below. The class in which below code is present that extends android.os.AsyncTask; I have been following https://cloud.google.com/appengine/docs/java/endpoints/consume_android#using_the_account_picker, but code fragments are not very clear.
#Override
protected String doInBackground(Pair<Context, String>... params) {
..
MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), credential).setRootUrl("https://myapp.appspot.com/_ah/api/");
..
What I have :
I have an Activity called ExpandedListViewActivity
another thing is ExpandedListAdaptor, which populates views dynamically ( form ).
My Objective
When User clicks on submit present in the form.
Android should be able to find the google account and its credentials and attach it with the request.
If it does not find then show account selector view, so that user can select account, if we can do it silently without user consent that would be very nice.
Extra methods that I have in :
class EndpointsAsyncTask extends AsyncTask, Void,
String>
void chooseAccount() {
mActivity.startActivityForResult(credential.newChooseAccountIntent(),
REQUEST_ACCOUNT_PICKER);
}
protected String fetchToken() throws IOException {
try {
return GoogleAuthUtil.getToken(mActivity, mEmail, mScope);
} catch (UserRecoverableAuthException userRecoverableException) {
// GooglePlayServices.apk is either old, disabled, or not present
// so we need to show the user some UI in the activity to recover.
userRecoverableException.printStackTrace();
} catch (GoogleAuthException fatalException) {
// Some other type of unrecoverable exception has occurred.
// Report and log the error as appropriate for your app.
}
return null;
}
public void getSettings(){
Log.d(APP, "get Settings ");
settings = mActivity.getSharedPreferences("Api", 0);
credential = GoogleAccountCredential.usingAudience(mActivity,
"server:client_id:Android-clientId.apps.googleusercontent.com");
setSelectedAccountName(settings.getString(PREF_ACCOUNT_NAME, null));
}
// setSelectedAccountName definition
private void setSelectedAccountName(String accountName) {
SharedPreferences.Editor editor = settings.edit();
editor.putString(PREF_ACCOUNT_NAME, accountName);
editor.commit();
credential.setSelectedAccountName(accountName);
this.accountName = accountName;
}
Please take into account that my endpoint server side is properly configured and running.
It should be straight forward, but I am not able to solve this, Please point the mistake or show me a direction to solve this..
Thanks for reading.
Shashank
This solves my problem.
http://android-developers.blogspot.in/2013/01/verifying-back-end-calls-from-android.html
https://cloud.google.com/appengine/docs/java/endpoints/auth
Specify the client IDs (clientIds) of apps authorized to make requests to your API backend.
Add a User parameter to all exposed methods to be protected by authorization.
Generate the client library again for any Android clients
Redeploy your backend API. <-- This was the key, to solve this problem.
Thanks,
Shashank
I am working with QuickBlox library for video chat. How can i manage it session?? because when i move to the next activity from the live chat activity i just lost the session because it says "Chat can't initialized" then i have to create the session again to do the calling. So what's the lifetime of quickblox session and how can i manage that.
I am also facing problem with recalling when stop the call or move to the next activity and try to recall i was not able to do that actually i tried different things so each time i am getting different errors. So if any one has experience with QuickBlox library need help here.
When i stop a call i call this function.
private void stopCall() {
//Toggle view show the smile view again
//ToggleSmileView();
try
{
cancelCallTimer();
if (videoChat != null) {
videoChat.stopCall();
videoChat = null;
}
if (videoChannel != null) {
videoChannel.close();
videoChannel = null;
}
sessionId = null;
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
and when i do the call i call this function
private void call() {
//toggle view
//ToggleSmileView();
// get opponent
//
VideoChatApplication app = (VideoChatApplication)getApplication();
opponent = new QBUser();
opponent.setId((app.getCurrentUser().getId() == VideoChatApplication.FIRST_USER_ID ? VideoChatApplication.SECOND_USER_ID : VideoChatApplication.FIRST_USER_ID));
// call
//
callTimer = new Timer();
callTimer.schedule(new CancelCallTimerTask(), 30 * 1000);
createSenderChannel();
initVideoChat();
if (videoChat != null)
{
videoChat.call(opponent, getCallType(), 3000);
//toggleMicrophoneMute();
}
else
{
logAndToast("Stop current chat before call");
}
}
For: Lifetime of quickblox session and how can i manage that.
To authenticate your application you have to set valid a auth_key and
generate a signature using your application auth_secret and receive a
session token which you should use to send requests to QuickBlox API
And,
Expiration time for token is 2 hours. Please, be aware about it. If
you will perform query with expired token - you will receive error
Required session does not exist.
Source: Authentication and Authorization Session Info
That part fits the Android sample code of creating the session,
QBAuth.createSession(new QBEntityCallbackImpl<QBSession>() {
#Override
public void onSuccess(QBSession session, Bundle params) {
Log.i(TAG, "session created, token = " + session.getToken());
}
#Override
public void onError(List<String> errors) {
}
});
Source: Android developers documentation
I have worked with the Android SDK, and feel it still needs some work, esp to reach a stage equivalent to the iOS SDK and REST API.
Though looking at your code, you should use getToken() before creating the new QBUser and related video chat calls, if the token has expired, just create a new one.
I have implemented similar code, not a video chat application, but in a general manner, write the functions in onSuccess() of session creation if the session needs to be recreated.
Fyi, for the multiple ones, you can try checking the error with the summary that has been given, categorized into 4; ..developers/Errors