Passwordless.ID - Sessions

I'll start this blog entry with a question I received in the Discord chat last week.

I would like to automatically log out the current user if there is no activity for x time. Haven't thought of the implementation, but is there an API for that?

Before answering that, let's start with the basics.

What is a session?

HTTP is at its core a stateless protocol. In order to "remember" things (like products in a shopping cart), associating the HTTP requests to a certain user (signed in or anonymous) is required. This is also called a session and is typically done in either of two ways:

  • A Cookie storing some ID

    • Automatically sent by the browser on each request

    • For security reasons, it is highly recommended to enforce Secure (ensures HTTPS) and HttpOnly (inaccessible by javascript) cookies.

    • Session tracking can be performed before the user even signs in, or even be unrelated

  • An Authorization header with some token

    • The token can be anything: a JWT from a thrid-party, an API key...

    • Tokens must be sent explicitly in the Authorization header for each HTTP request

    • Tokens add some logic client side to obtain them, refresh them and potentially cache them

On the server side, the cookie or token is read, making it possible to associate the HTTP request to the adequate "session".

Cookies are the classic way to handle sessions server side, they are simple, secure and practical. On the other hand, Json Web Tokens (JWT) are often used to obtain a signed token from a third party to act as proof of identity or for distributed APIs.

Whose session is it?

When using Passwordless.ID, there are actually two sessions involved.

Please note also that a session for example.xyz is not strictly required. It could simply directly forward the id_token from passwordless.id directly to the server/APIs with Authorization: Bearer {{id_token}} and not use sessions at all. Or, it could send it once at the beginning to establish a cookie based session.

The session of Passwordless.ID is independent, a bit like how you are currently signed in with Google/Microsoft/Apple. If you are currently signed in with Passwordless.ID, every website you visit using Passwordless.ID will see you as signed in, and when you sign out or switch the account, it will reflect on all other websites too. That is also why you cannot forcibly sign out the user from Passwordless.ID. This would affect everyone.

Typically, when example.xyz requires authentication, it will perform a call to passwordless.id() using the @passwordless-id/connect packacge. If the passwordless.id session is still alive, a brand new id_token/token will be returned without user interaction, also called silent authentication. Otherwise, if the passwordless.id session expired or because the user deliberately signed out, the sign in dialog will appear before the id_token/token is returned.

Lastly, the user information of Passwordless.ID might be cached by other websites in their own sessions. This results in the signed in effect appear to be "sticky" even if the user has signed out from Passwordless.ID separately.

So what about inactivity expiration?

Activity on your website is for you to track. Whether you use a timer in the browser or check with your server if the session still exists is up to you. How you keep track of activity and expiration delays are all under your control.

But as stated before, even if the session at example.xyz expires, the session at passwordless.id might still be active, silently re-authenticating automatically.

But I want explicit re-authentication!

Currently, this is unavailable. However, future support for this is planned as explained below.

While silent re-authentication might be really comfortable in some cases, in other cases you may want explicit re-authentication. This is especially true in security sensitive contexts like banking or finance-related apps. After some inactivity delay, explicitly re-verify the user may be required.

Moreover, this explicit user verification might also be desired during a session, for example when accessing a restricted area or before triggering some sensitive operation. For example, it is common to verify the user before allowing a payment.

To do that, an additional parameter prompt will be offered in an upcoming version.

/openid/authorize?...&prompt=login

This upcoming parameter will explicitly re-trigger authentication and verify the user using biometrics or local authentication. This can be used as proof for sensitive operations or to re-sign-in users explicitly after an expired session.

This can be invoked through the @passwordless-id/connect lib as follows.

passwordless.id({
    prompt: 'login'
})

On the opposite, prompt: 'none' (the default) would perform silent authentication if possible.

By default, the retrieved token/id_token is cached in sessionStorage. When you want to keep track of the session on your own, it is also recommended not to cache the token, to avoid accidentally re-using the cached token.

passwordless.id({
    cache: false,
    prompt: 'login'
})