Dispelling Token Auth myths
Published:
Recently a friend commented on how Django might do more to support SPA applications, notably by having built-in support for authentication using JWTs.
This is not a new question, and recently I've been discussing with someone on IRC about JWT support in Django that was not tied to Django REST Framework.
I have frequently helped people on IRC having issues supporting token based auth in their APIs. But all too often they can't explain why they're using token auth or what problems it solve for them, just that they read somewhere it was needed. (Or worse, their manager demanded it without justification.)
My focus in this post is the all too common misconception that when you use a Single Page App (SPA), or Mobile App, or API, you should or must use Token based authentication.
The reasons for using token based authentication are totally independent of your app being a SPA, Mobile, or an API.
Terminology
First, some terminology to make sure we all understand each other.
Authentication: Proving identity (who are you?)
Authorisation: Proving rights. (what are you permitted to see/do?)
Issuer: A service that generates and issues Tokens
Consumer Service: A service that accepts Tokens to prove Authorisation.
Client: The browser, SPA, Mobile App, or other application trying to make use of your service.
Common auth schemes
HTTP Basic Auth
In the early days, HTTP used "Basic" auth, which involved sending username +
password in almost cleartext (Base64 encoded, no encryption or signing) in the
Authorization
header in each request.
The server could indicate authentication was required by sending a header like
WWW-Authenticate: Basic realm="name"
. The presence of this header indicated
that Basic type auth is required for this URL, along with a hint for the user
as to what they're authenticating for.
The browser, after prompting the user for their credentials, would pass a
header Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l
. The value here is the
Base64 encoded value of the username and password joined with a ':'.
There are other "standard" types, listed in the IANA Authentication Scheme Registry
More details:
Session auth
Most web frameworks support a "session" system allowing the server to store
hidden details about a specific visitor. These details are mapped to the
visitor using a session cookie
, containing a key identifying their session
record.
Session authentication works by storing the authenticated users in the session. When a user logs in, their User ID is stored in the session.
On subsequent requests, the application can verify which user is logged in by checking this session value.
When a user logs out, their auth details are removed from the session. If the session expires (either purged by the system, or the cookie expires) the user is implicitly logged out.
The great benefit of using sessions and cookies like this is it requires no effort for the client to remain logged in. All that's needed is proper cookie handling, and it "just works".
What is Token Auth?
Token authentication comes in various flavours, but typically involves passing
some server-issued "token" in the Authorization
header, commonly using the
type of 'Bearer' (officially this is meant for Oauth2).
As browsers don't support this automatically, it requires your client (Javascript [SPA or otherwise], Mobile application, API client script) to set this header on every request.
What is a JWT?
JSON Web Tokens are a format used for providing a signed, structured token containing various "claims" about the bearer of the token, such as the right (authorisation) to operate as a specific user, in a given time frame, with a given subject (service).
They can be cryptographically signed to allow consuming services (e.g. APIs) to verify they were issued by a trusted third party.
One of their biggest benefits is being self-contained: since they can be cryptographically signed by the issuer, a consumer service can verify the token is valid without needing to contact the issuing service, or even reading their own database.
Relative Merits
So, when would you pick Token Auth over Session Auth?
Automated agents
One big strength of Token Auth is tokens don't have to directly represent users; they can delegate a subset of rights to an entity to use on behalf of a user. This is a large part of the Social Auth scene; allowing users to delegate limited authorisation without authentication to 3rd party services.
Typically this would require extra infrastructure in your app framework to map these assigned rights to each token.
This is where JWT can shine. A JWT can contain a list of 'claims' (e.g. who it's operating on behalf of, and what rights were delegated), which can be trusted without consulting the database.
3rd Party Authentication
If you have a need for some other service to provide authentication, token auth (especially with JWTs) makes tremendous sense.
Now your client can authenticate with the token issuer [be it another microservice in the same organisation, or a 3rd party service like an auth aggregator or "social auth" provider], pass the token provided to your service on each request, and your service can verify their authorization quickly and reliably.
Downsides
Manual headers
As noted, when you move to Token authentication, it becomes the responsibility of your Client to add the token header on every request. Typically in a modern SPA this is a minor overhead.
Revocation and Expiration
Since the issuer is no longer contacted, there is no way to forcibly cancel or retire an issued token.
Think about that for a minute: should any token be compromised, the server can not revoke it.
The standard solution to this is to have a short lifespan. Each JWT should
include an exp
claim; a timestamp indicating when it expires.
So what do clients do when their token expires? Typically when the token is issued, it comes along with a Refresh Token. This second token can be passed to the Issuer to request a fresh Authorization Token.
(As a side note, anyone who has experience with Kerberos may be seeing similarities here.)
For comparison, the sequence for using session auth might be:
- make authentication request
- make target request
Or if your process runs persistently:
- make target request
- get 401
- make authentication request
- repeat target request
Whereas when using a Token:
- make target request
- get 401
- make token refresh request
- repeat target request
The differences in workflow are minor.
What has this to do with SPA / Mobile App / APIs etc ?
I'm glad you asked.
Almost nothing. The two concerns are pretty much 100% orthogonal.
The real questions are:
- do I want to support 3rd party authenticators?
- do I need to support delegating rights?
Do you disagree? I'm happy to discuss this further...