I am building an app (for web and mobile) that requires a user pass two stages of authentication/authorisation in order to access a server-side API and subsequently use the app. First, they must supply valid credentials (username/password). Second, they must meet a series of variable criteria, for example the current time being within a defined range.
I am planning to implement this through the use of two tokens:
- A long-term, randomly generated, opaque session token
- A short-lived JWT authorisation token, with self-contained user and expiry data
On the client side, the presence of the session token would allow the user to skip re-entering their (hopefully long and complex) username/password. On the server side, a valid session token would be required to issue an authorisation token, and a valid authorisation token would be required to access the API.
While the goal of the session token is to simplify access (particularly on mobile devices) by removing the need to enter full username/password, I would prefer the user still re-confirm their identity before a new authorisation token is issued. A shorter numeric PIN (or potentially a fingerprint/face scan on supported devices) could allow this.
However, storing such a PIN along with the user’s other data on the server would require full management facilities, as with their password (“I forgot my PIN”). To avoid this overhead, I am thinking about the following approach.
On initial login (no known session token):
- Ask for and submit username, password, and PIN.
- If username/password are valid, generate the session token.
- Encrypt the session token under a key derived from a server-known secret plus the submitted PIN.
- Return the encrypted session token to the client.
On subsequent login (known session token):
- Ask for PIN.
- Submit PIN and encrypted session token.
- Decrypt the session token, using the submitted PIN, and compare the result to that stored on the server.
- If the decrypted session token matches a valid session, the user has confirmed their identity and an authorisation token can be issued.
In my mind, this allows a simple “identity confirmation” step with little overhead. The user can reset their PIN at any time simply by fully logging out and logging back in again, choosing a new PIN. And while the PIN is short and simple, it is combined with a server-known secret in order to derive the encryption key, so an offline brute-force of the encrypted session token should be extremely difficult. And server-side use of a slow key derivation function, rate limiting, and lockouts on failed attempts should mitigate online attacks on the PIN.
So my question is: is my thinking correct? Is this a secure way to achieve my goal?