Bearer tokens are a very popular API authentication scheme. In this post, we’ll see what they are, how they work, how to sign/verify/encrypt/decrypt a token works, and common cryptographic algorithms to use. Let’s do it!
Table of contents
Open Table of contents
- Bearer Token
- What to store in a Bearer token?
- Signing a token
- Encrypting a token
- Cryptographic algorithms
- Request/response flows using Bearer tokens
- Bearer token generation libraries
- Bearer token open-standards
A bearer token is a signed and optionally encrypted string usually sent between client <-> server communication. The server generates a token for a successful login request. Some examples of a login request can be:
After a successful login, the client uses the bearer token to authenticate subsequent calls to the server. The server uses the token to verify and decode the data it contains.
The Bearer token is usually sent from the client to the server in the HTTP
Authorization header as
Example: exchanging email/password credentials for a Bearer token
What to store in a Bearer token?
You would typically store information to identify the bearer of the token, the user’s ID, for example.
Beware that the information you store in the token string might be readable by anyone who has the token! As we’ll see in the following sections, there is a difference between signing a token, and encrypting a token.
Signing a token
A digital signature is a mathematical scheme for verifying the authenticity of digital messages or documents. A valid digital signature on a message gives a recipient confidence that the message came from a sender known to the recipient. — Wikipedia.
In other words, signing a token allows us to validate that our server created the incoming token from the client. Only our server can issue such signature, and by using the same secret, the server can verify the incoming token’s signature. Signing a token is used to protect against tampering.
Example: a user of our application exchanges his credentials (email/password) for a Bearer token. The server signs the token, and returns it to the user. If an attacker modifies the token’s contents and signs it with a different secret, the server won’t recognize the signature (content + secret don’t match), thus unauthorizing the request. If the attacker knows the secret and the signing algorithm, he could generate a valid signature bypassing our security.
Depending on the token generation mechanism used, the content of the token might be readable by anyone. You should never store sensitive information. If you need to store sensitive information, encrypt the token!
Encrypting a token
Encryption is the process of encoding information. This process converts the original representation of the information, known as plaintext, into an alternative form known as ciphertext. Ideally, only authorized parties can decipher a ciphertext back to plaintext and access the original information. — Wikipedia.
Encrypting a token ensures that malicious parties cannot read the content. It’s helpful if you need to store sensitive information from the user.
Encryption will increase the server’s time to generate and validate the token. You should use what best fits your use case. In my opinion, you should always sign and encrypt the token. It’s better to be on the safe side!
The security of the token, for both signing and encryption, will depend on the algorithms you use. We’ll look at the most common (and secure) algorithms used in the industry.
Symmetric-key algorithms use the same secret key to sign/verify/encrypt/decrypt the token. These algorithms are commonly used if you only need to verify/decrypt tokens in one place. A common use case is when you have a single backend. Most use cases fit here.
Asymmetric-key algorithms use different keys to sign/verify/encrypt/decrypt the token. They use a private key for signing/encryption and a public key for verify/decryption.
Suppose you must sign/verify/encrypt/decrypt the token in different places, for example, multiple services/backends. Having a single secret key can compromise all your infrastructure as you need to share the secret key between all the involved services. An asymmetric key is preferred for this use case, as each service can expose its public key independently. Services then can use this public key to verify that a legit service issued the token. If a public-private key pair is compromised, you only need to rotate that key pair to patch your infrastructure.
Asymmetric-key algorithms don’t come for free. You need a public-key infrastructure to manage public-key exchange between signers and verifiers.
Although asymmetric-key algorithms are commonly used for digital signatures, they can also be used for encryption. Here is a list of the most popular asymmetric-key algorithms.
Popular protocols relying on asymmetric-key algorithms:
Github uses GPG to verify your commit signatures. You sign your commits using your private key. Github uses your public key to verify your commits’ signatures.
Read more about symmetric cryptography and asymmetric cryptography.
Request/response flows using Bearer tokens
Example: Requesting to update my username to the Server
Example: Request my username and billing info to the server
This example is hypothetical to showcase asymmetric-key digital signatures through Bearer tokens.
The backend has a services architecture using a single GraphQL schema through schema-stitching. The services are publicly exposed to the internet, so they need to verify every request is from known services.
- Gateway: Stitches all services GraphQL schemas into a unified GraphQL schema.
- Auth Service: Exposes a REST endpoint to exchange a cookie for a bearer token signed by auth’s private-key.
- User Service: Exposes a GraphQL schema about the user’s information.
- Billing service: Exposes a GraphQL schema about the user’s billing information.
- Stripe Service: Exposes REST endpoints about the user’s billing information in Stripe.
Gateway acts as a public-key registry for services. Any service can request other services’ public keys.
The Billing service aggregates different billing providers, one being Stripe.
The Billing service needs to request the user’s information to Stripe service.
The Stripe service can only be called from the Billing service.
Each of the services registers its public key in Gateway.
Whenever a service wants to call another service, it generates a Bearer token using its private key. The Bearer tokens generated are short-lived (5 seconds).
The called service will verify the Bearer token by requesting the caller’s public key to Gateway.
I won’t put the “invalid token” options in the diagram to exemplify the Bearer token verification flow.
Bearer token generation libraries
As we’ve seen, a token is just a string. This string can either be signed and/or encrypted. There are different libraries to facilitate the generation of these tokens using the previous industry-standard cryptographic algorithms listed that give you secure implementations. Try not to implement these algorithms manually for your token validation mechanism.
There will be different libraries depending on the language/web framework you use, for example:
Bearer token open-standards
There are different industry standards that you can use for your Bearer tokens. Which one to use? As always, it depends on your use case. Each solution has trade-offs you need to consider when choosing one that suits you.
Do you know another one that belongs here, or do you have any other recommendations?
Let me know so anyone reading this can benefit!