How to Logout when Token is expired Android LiveData - android

I am working on one android app And the problem is when a token is expired, then I need to logout the user. Right now I need to check the token expire in all my Fragment. So Is there any way so that I can do this task from centralized locations.

Are you using retrofit? If so, you could add a custom Interceptor, which intercepts every response and handles 401 by redirecting to login.
You could also define a SessionHandler, that requires a context and from the Interceptor you would call sessionHandler.onSessionExpired() for example. Hope you get the idea

Personally, I prefer to use pub/sub pattern for these kind of problems. In general, when you find out that your token has been expired (for example in an interceptor) raise an event (named session expired). Also, there are some consumers (for example a BaseActivity which all other activities extended it) that capture this event and react to it (according to your question, redirect to the LoginActivity). To implement this approach, you can both use some ready solution (like greenrobot) or develop it by using LiveData.

Related

Check user auth on every Activity

Is it appropriate to check user auth to server api in every activity (i assume using base activity on resume)? It is to prevent user login on multiple device with same account. Or is there better approach for this?
There is several approach to handle this.
Push notification - When same user login in another device then you need to send push notification to first login device. When first login device get notification then you can put your logic there.
Put Logout status on every api - You need to check logout status code in every api response. When you get logout status code then you can put your logic there.
Note: For second approach you need to make one generic api calling class. So, You don't need to put this status code in every api calling code.
So I'm assuming that every successful sign-in then the server gives you back a unique auth-token which should invalidate any previously assigned token.
My approach is usually just to allow the user to continue using the app with whatever cached data that's already there until I hit a scenario that needs to do an API call. And if I get an error code like HTTP 401 or 403 because the auth token was invalidated then should send me back to the sign-in screen.
Doing an explicit API call in onResume() seems wasteful and is basically client-side polling I would only do this if it was a very strict requirement to keep the auth information fresh because the information on screen is sensitive.

FCM: onNewToken vs FirebaseInstanceId

Firebase has deprecated some of the messaging calls in the com.google.firebase:firebase-messaging:17.1.0 release. This post goes over those changes nicely.
Question: Can someone tell me if is it considered bad practice to not implement onNewToken and instead just call the below block each app launch This probably seems excessive to Android folks but feels like home from an iOS point of view.
FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener(this) { instanceIdResult ->
// Just use this call
val newToken = instanceIdResult.token
Log.i("newToken", newToken)
}
#Override
public void onNewToken(String s) {
super.onNewToken(s);
// Leave this unimplemented
}
I am more familiar with iOS which calls its onNewToken equivalent on every app launch. So for iOS I put logic there to determine if my backend needs to be updated.
getInstanceId() docs say This generates an Instance ID if it does not exist yet, which starts periodically sending information to the Firebase backend. This makes me assume I can just call FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener each launch.
Something very important that no one has mentioned yet:
If you check for the current device token only after an app launch, you might loose the event of the token being updated while your app is in the background (of course) and you won't be able to receive remote push messages from your server until the user launches the app again and you send the new token to the server.
The whole purpose of having that callback which can also be called while your app is in the background is to prevent loosing backend messages (important if your app or some important features of it relies a lot on push notifications). It is important to be aware that this callback will not only deliver you the token when you register the device for the first time but also: Called if InstanceID token is updated. This may occur if the security of the previous token had been compromised.
So:
Can someone tell me if is it considered bad practice to not implement onNewToken and instead just call the below block each app launch This probably seems excessive to Android folks but feels like home from an iOS point of view.
Yes, it is actually a bad practice to not implement onNewToken().
First of all, I'm highly skeptical of any logic that suggests that if something is OK in iOS, that it would be OK on Android!
The implementation of push messaging between Android and iOS is extremely different. On Android, it's dependent on Play Services, which runs in another process. On iOS, it's something completely different. The rules of engagement are simply not at all the same.
Note that the suggested token retrieval method is via callback. That is suggesting that token generation is essentially asynchronous. In other words, at app launch (in whatever way you actually define that), the background stuff that manages to token might not be done with that yet. There simply might not be any token available when you ask for it. And who knows how long that takes? You're better off just accepting the token when the system tells you it's ready instead of making a guess about when it's ready. Follow the recommended implementation path.
Despite the documentation says that onNewToken is called upon the first app start, it is not. That is why I use FirebaseInstanceId respectively getToken() when I need the Id while onNewToken has not been called before although the app is already running for a while. (So we do both in our project)
What I observe is that Firebase will call onNewToken shortly after I was fetching the token via FirebaseInstanceId. It seems that fetching the token this way initiates something within the Firebase service.
However, it works that way and that is good enough for our current project.
Edit: As getToken() of FirebaseInstanceId got deprecated recently, please refer to Arthur Thompson's answer.
Calling FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener on every app launch is an option (an unnecessary option), onNewToken is there specifically to provide you access to the token when available.
Note that calling FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener on every app launch would require you to handle the case when the token is not yet available, using onNewToken avoids that.
Let's simplify this equation - you should do both.
In our app we were not manually checking FirebaseMessaging.getInstance().getToken(), but did have the onNewToken override set up. This worked for most users, but we had instances of people who would stop receiving notifications unless they reinstalled the app.
So if you really want to be sure you always have an up to date token, you need to do both here. Check at startup with getToken() to make sure you didn't miss an update, and subscribe to onNewToken to get notified if it changes in between app launches. Firebase documentation does allude to this, although to be honest it could be more clear:
The onNewToken() function works like it's predecessor, onTokenRefresh().
Implementing the first block on your post requests and waits for a token. This guarantees that a token (or an exception) would return. However, that token isn't guaranteed to stay the same forever. Similar with onTokenRefresh(), onNewToken() is triggered when a token for the corresponding app instance is generated, which in turn you should use and replace with the old token you got.
My answer here has more details.

Best method for running a "common" REST call before any other REST call

I have a REST API secured with JWT. The client is an Android app and a web app. Android app gets a new token when a user login, after that it works with that token. However the token will expire in 60 minutes, so I have to refresh it. I know there are 3 approaches for this.
issue a fresh token in every request
issue a fresh token when the current one is close to expire. e.g. 10
min
let client app request a new token when it needs it using a "refresh
service" of your api.
Please consider the following...
I am not happy about the first suggestion above
I did try the third suggestion. However in my android app I got like 60 REST calls. Then what I should be doing is with every REST call (ex: getUsers()), I first have to check whether the token is about to expire and if yes, get a new token from REST API (That means I have to run another REST call to the refresh() method in API). After this check and getting the new token I can execute the getUsers() method. The problem here is that every REST call should run inside the onResponse() method of my refresh() method (I am using Retrofit) and that is simply not possible as I have to then duplicate the same method for 60 times with 60 names. I am sure the same issue will arise with the web app as well.
Due to the above reasons I am considering the 2nd suggestions in my first list, renewing the token at the server it self. If the token is "valid" and if it is "about to expire" I will refresh it from server and will send to app as a header.
I need to know whether that method is an industry practicing method and whather it is the best choice. If not, how I can proceed with the 3rd suggestion in my first list.
I have recently implemented the similar setup with JWT in one of my Android apps. I don't know if my suggestion will help you, but it might give you an insight on how others are doing it.
Option 1: This is very much redundant, and it violates the sole purpose of using JWT in the first place. If I get a new JWT on every request, I can use that for further requests, thus practically have a token with no expiration.
Option 2: This requires server side extra operation, plus this is not feasible. How do you plan to detect which tokens are already distributed and "about to expire" so that you can renew them? Tons of users will have tons of tokens and if you plan to save JWT in database, then it would get too messy.
Option 3: This method is too redudndant and requires client side operation on checking whether token is about to expire and calling refresh service based on that. I always prefer lesser operation to do more work.
What I did, is that, I used two tokens for the system. One as a Refresh Token (token used to request JWT refresh) and one as JWT (for every request validation).
With successful login, received a refresh token and saved it locally in app (SharedPref). This token is also preserved in database too. Next, requested and validated JWT with that with that refresh token. From now onwards, every request contains this JWT in header. I also request new JWT everytime when my app is opened, i.e, in my Splash page.
Now, if any request contains expired JWT, simply return a common response like "Session expired" with a fixed status code. If any HTTP request has this specific status code in its response, I requested another API call with the refresh token to get fresh JWT for future requests.
As I needed to add this check (whether contains "Session Expired") in every HTTP response, I wrote a common function and passed the HTTP responses via that method, so that, I do not need to rewrite or copy-paste every bit of it.
This requires very less code and minimal operation on both server and app end. My system has a JWT expiration time of 20 minutes and I haven't faced any problem till now. Worst what happens is, in a single request, user receives token expired, gets token, and recalls that function. This results in a slightly delayed operation for users that is already in my app continuously for more than 20 minutes. If any user is in my app for that long, it's a good problem to have, right? :)
It's not clear whether the users have to log-in in your app every time or session is held so that logged-in users can directly enter the app. If session is hold, just keep the refreshToken in app locally (SharedPref) and use that to get JWT every time the app is opened. If any user is misusing your API (scraping or for any other purpose), simply move that specific refresh token to a black-list so that this user doesn't get new JWT.
Please let me know if anything's not clear. Thanks.

Paging does not work and maybe I found the cause

I am trying to implement the stream (news feed) paging function to my app, but it does not work as expected. I found many similar questions here but there are no solutions to solve this problem.
I tried both Graph API and FQL and the behavior was similar. It succeeds to get the result one or two times but after that, it fails to get the result (gets the empty JSON array).
Finally, I found this problem depends on access_token. If I just change the source code to use Android SDK Stream Example App ID rather than my own App ID for authentication, it works perfectly.
So, I believe the Facebook server checks the App ID and returns some weird or restricted access_token to my app.
Are there any condition to get the valid access_token? I tried the exact same permissions with Android SDK Stream example app, but it could not solve the problem.
Will changing the App ID (to get the valid access_token) solve the case?
Verify your app has the read_stream permission. Without it you will not get any non-public objects.
There also seems to be some general LIMIT OFFSET issues with FQL and stream. See Facebook FQL stream limit? and http://developers.facebook.com/bugs/303076713093995.
Try using the SDK on Facebook. Then get the access token.
Use the Facebook SDK for Android. It can be found here. There are getter and setter methods to get and set the access token. The first time you do a single sign-on (SSO), you must save the access token in SharedPreferences and there's no need to reauthenticate again and again.
Just set the access token from your preferences and make a call to the feed dialog. The offline_access permission is to be deprecated. The Facebook SDK for Android does the rest of the work. Just supply sufficient parameters through Bundle.
A couple of things on Android:
get right permissions "read_stream"
Use the android SDK
Check the sample that came w/ android. In the onCreate(), I authenticate Facebook object
this.facebook.authorize(this, new DialogListener() {....}
Test using Facebook's Graph API Explorer.
If you paste in some code I can help you debug further.
The access token returned by Facebook server is valid only for a particular period of time. After which you need to refresh your access token, which is a tedious job.
So, in order to avoid this and maintain your token, you need to add the permission "offline_access" to your permission list. This is the approach used by almost Facebook-related apps.

Handling login and remember me with the AccountManager

I've integrated android's account management in my application and I can manage accounts from the Accounts & Sync settings.
I want to have the classic login activity that forwards the user to his home activity on successful login, having the option to remember the user's password. However, the AccountAuthenticatorActivity must return its result to the AccountManager with the credentials and the rest of the account information, calling an explicit finish() and returning the intent.
How can I give the AccountManager the info it needs without having to finish() my login activity?
AccountManager is not meant to be called by an interactive application, but rather by a Sync Adapter. (A great tutorial is "Did You Win Yet? » Writing an Android Sync Provider" Part 1 and Part 2 which gives great code examples but doesn't do as great a job of explaining the data flow.) It's like this:
You develop a ContentProvider which wraps a database. You build a SyncAdapter (this is a background Service) to connect to a server and fetch data and sync the ContentProvider to match the server. Then, your UI queries to the ContentProvider to show the fetched data. There are some methods to directly query for specific information as well, if you want to search and cache results for example. See Developing RESTful Android Apps for a nice hour-long session on how the data model should look. They give three architecture examples, starting from a "naïve" implementation then progressing to the proper SyncAdapter model.
As for authentication itself, the way SyncAdapter uses the AccountManager is to obtain an authentication token. This is a (often) a big hexidecimal value, that is passed as part of the HTML headers in lieu of a username/password pair. Think of it as a one-session unique key. Posession of the key is proof of authentication, and they expire periodically. When they expire, you reauthenticate and fetch a new one. SyncAdapater asks AccountManager for an auth token for a specific account-type / username combination. AccountManager auths with the server (asking the user for a new password if necessary due to change) and returns the token to the SyncAdapter, which uses it from then on.
If this model isn't appropriate for your application, you need to manually handle login/logout in your app code instead. Kind of a pain, I know.
#jcwenger That is not entirely correct. You can use the AccountManager from an interactive application as well. For example, you can add accounts without invoking the account manager interface by using AccountManager's addAccountExplicitly() method.
On the "Did You Win Yet?" article you can clearly see that the account manager is invoked from the application's own Activity. This is useful if the application has its own account management interface.
My version of the 'classic flow' using AccountManager:
I use my AuthenticatorActivity both for the normal case where it's used via. Accounts & Sync Settings, but I also open it out for use by applications that rely upon the same Accounts.
I have a separate apk with the Authenticator implemented and other apps (separate apks) that rely upon these Accounts.
Cases handled:
the normal case: the user adds/authenticates via. Accounts & Sync (as per the Android sample project)
handle authentication requests from external apps:
How? I provide an intent filter in the Authenticator app's Manifest so other apps can instantiate the AuthenticatorActivity via. startActivityForResult (they must include an intent extra that indicates who they are (their app's package)). In the AuthenticatorActivity I detect this case and refrain from calling setAccountAuthenticatorResult when the authentication process has come to a end because I reserved it's use for the normal case above. The user enters their credentials and presses Sign In: AccountManger is checked for a matching account and if matched I
persist that Account's username as the active user for the calling app's package. I then return an intent to the calling app via. setResult indicating success, the username and account type. In the case where the Account didn't exist I go through the process that the normal case goes through, i.e. calling addAccountExplicitly and then set the active user and then as before calling setResult and finish.
Hope this helps someone.

Categories

Resources