The App: Logging In/Out User Experience
It seems that every supplier of identity libraries has their own idea about controlling the login flow. Of course, none of them seem to match ours.
The problem is, they automatically want to refresh the tokens against their own identity stores when they expire. And they all expire at different times. Microsoft tokens last 60 minutes and Facebook's last 60 days.
No one wants to login in every hour and we can't afford to have authenticated, valid tokens rolling around in the wild for 60 days without being checked. To be sure, these options are great for task lists and social apps. Not for financial apps.
To add to the problem, all the server based login approaches use a WebView component to manage the conversation with the server (which may be why they recommend a client SDK approach to this). And those login flows coupled with a WebView component means that cookies are being created with the identity provider authentication credentials stored in them.
Normally we wouldn't care about that, except that the next time we want to force a user to authenticate (via B2C) the WebView that's used will find the cookie and, thinking it's doing the user a favor, it will use the cookie to login again. In that case, the user might never be prompted for their username and password, which we don't want. Imagine logging out of our app, putting the phone down and having someone else launch our app and the credentials be "automatically" refreshed. Ugh.
So here's the plan:
- On the client device, we get a token from B2C for whatever identity provider they want. We can do this using the server or the client approach.
- We use that token to authenticate with our site, where we add the security information we need.
- At the same time, we create a "refresh token" entry in a database and attach that value to a new token we issue back to the client. An important part of this is that each login on a different device will be issued it's own refresh token. This refresh token is identified by device id. That means asking for it during app installation. We can offer a different approach of having a unique id stored, but it will be wiped out if the package is reinstalled, thereby orphaning any existing tokens for that device. This might not be a bad idea anyway.
- For our token, we specify an expiration date of two weeks (or so) in the future.
- We securely store our token on the client device.
- We then delete the B2C cookie, so that if another attempt is made to log in, it won't happen automatically, outside of the user's control.
- From then on, with every outgoing request to our server, we check the date/time that our token was issued with the current time. If more than an hour has elapsed since we issued it, we silently make a call to our server to refresh our token. At that time, a new token is created, with an expiration of two weeks in the future. We also check to see if all logins been disabled by the user or us. We also check to see if the refresh token is still valid.
- At this point, we have a token with a rolling security window. So long as they continue to use the app at least once within that two weeks (or whatever we specify) they do not get prompted to log in. If more than two weeks elapse, they will prompted to begin the login process with username and password.
- Each time a new token is issued, we store it securely on the device.
- Each time the app starts up, we retrieve the token stored on the device and attempt a refresh.
- Naturally, they can log out at any time. When that happens, the token supplied on logout call will contain the refresh token currently in use on that device, which can be invalidated at that time.
- They will be prompted to log in the next time they use the app on that device. Since a new refresh token will be issued, any stolen or orphaned tokens automatically expire and don't have to be tracked.
- In the event they want to disable logins for their entire identity:
- They can change a setting in their user profile that prevents new logins and token refresh operations for all devices, effective immediately or whenever their tokens expire.
- They can force a deletion of all refresh tokens for all devices. For example, this would happen with a password reset.
- We have to consider the ultimate expiration of a refresh token at some point in the future, forcing a user to re-authenticate regardless of how often they use the app. I'm thinking a year, maybe 6 months.