Securing API Requests With Access Tokens

Learn how to secure your backend APIs using Wristband access tokens.

For backend APIs, a common approach for authenticating incoming requests is to use an access token. Access tokens are strings passed in the Authorization request header using the Bearer authentication scheme. Using access tokens for authentication has the following benefits:

  1. Stateless Authentication: Access tokens don't require persisting state on the server. All information needed to authenticate the request is encapsulated in the token itself. To authenticate a request, you can simply add the token to the Authorization request header.
  2. Cross-Domain Support: Unlike session cookies, which have restrictions regarding how they can be sent for cross-site requests, access tokens can be sent freely between different domains (assuming CORS is enabled on the APIs). This makes them ideal for APIs that might need to be accessed from third-party clients.
  3. User-Agent Agnostic: Access tokens can be easily sent from any user agent, including browsers, native applications, and servers. This is in contrast to session cookies, which require the use of a browser to send the cookie from the client to the server.
  4. Flexible Formatting: Since access tokens are just strings, they can be formatted in any manner. Typically, the access token will be formatted as a JWT, which can include tamper-proof claims regarding the authenticated subject.
⚠️

Access Tokens Are Not a Replacement for Session Cookies

While access tokens are great for authenticating APIs, they are not a replacement for session cookies. Session cookies should still be used to maintain the user's authenticated state between the frontend running in the browser and the backend. Listed below are reasons why session cookies are beneficial for authenticating requests between the browser and the backend.

  1. Improved Security: Without session cookies, you'd have to store the access token within the browser, for example, in localStorage. Once tokens are stored within the browser, they can be accessed by malicious JavaScript, making them susceptible to XSS attacks. Session cookies, if configured correctly, are impervious to XSS attacks.
  2. Automatically Included in Browser Requests: Session cookies are automatically included in every request made by the browser; whereas with access tokens, you need to explicitly add them to theAuthorization header of each request.
  3. On-Demand Revocation: Since access tokens are stateless, they are not easily revoked. Typically, an expiration time is encoded into the access token, and it becomes invalid only when this expiration time is reached. Server-side sessions, on the other hand, can be revoked immediately by deleting the server-side state.

Now that we've reviewed why access tokens are useful for authenticating backend API requests, let's dive into the implementation details. In the sections below, we'll explain how to validate access tokens issued by Wristband at your API endpoints.

Access Token Validation

To authenticate requests to your backend APIs using Wristband's access tokens, you'll need to add a JWT validation middleware that performs the following actions:

  1. Extracts the access token from the Authorization header.
  2. Verifies the signature of the access token.
  3. Ensure the access token's claims are valid.

Extract Access Token From Authorization Header

The first thing your JWT validation middleware will need to do is extract the access token from the Authorization header. The Authorization header should be using the Bearer authentication scheme as shown below. The access token is the string following the word Bearer.

Authorization: Bearer eyJraWQiOiJxdG90dDV0NGZ2YXU3bHAzems1ZGl6YnBqdSIsInR5cCI6ImF0K0pXVCIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJwYmlhZnN1dDdqZzUzanZqanAzbzR1Z3VmdSIsImlkcF9uYW1lIjoid3Jpc3RiYW5kIiwidmFuX2RvbSI6Imludm9iMmJuZGNoZXp5bHRvaXMzLXdyaXN0YmFuZC5kZXYud3Jpc3RiYW5kLmRldiIsImFtciI6WyJwd2QiXSwiaXNzIjoiaHR0cHM6Ly9pbnZvYjJibmRjaGV6eWx0b2lzMy13cmlzdGJhbmQuZGV2LndyaXN0YmFuZC5kZXYiLCJjbGllbnRfaWQiOiJ1dXZ4Mmx6ZHp2aGhqY2E1cGU3bmVqd3dkeSIsInRudF9pZCI6IjZxbjNtb3Z4M2ZndGxvaDJxanV0NXM0bmhhIiwic2NvcGUiOiJvcGVuaWQgb2ZmbGluZV9hY2Nlc3MgZW1haWwiLCJhdXRoX3RpbWUiOjE3NTI0MzgzNDgsInN1Yl9raW5kIjoidXNlciIsImV4cCI6MTc1MjQ0MDE0OCwiYXBwX2lkIjoidmw2amRvYnh5NWdhZmtyM2R2enRidnpydWUiLCJpYXQiOjE3NTI0MzgzNDgsImp0aSI6IjJ5emtjamRrMjVjbDdlM3V1Z2d6ZW82a2ZhIn0.dU-AmJsdcIKLM0aZx_ipBj2UG_KOp3jiqG79cTqudS4OoCmZiFWYaECSDf2uKE81aEhXZkYUC4uwnhXTMw67YbjVaVxfuSOvU0OaLfHAXCixXkK3nVdFlJzD3SXEyC-1QwxH9uEFt5M1iF3fZpFG7kUWb5FNyOhX14AJ693mDUdvj6Tj0I1gUQVm4mQ_Yu1z4iUeQcGiEiNJwbu4Izk1taROzZmDc33cgpFDxVjAIsPk3HRPPAtYyaEcluqHzSXBt-Q4Mx0hr06bS3MSrByD9d-clE-27deywE3mqbA70ijDIxVV0WCvZ8PPvwnAufHk891gCTiFATDgWUi43eMdKA

Signature Verification

Wristband access tokens are structured as JWTs; as such, they are composed of three parts: a header, a payload, and a signature. The different parts of the JWT are delimited by dots (.) as follows: <header>.<payload>.<signature>. Each section of the JWT is a JSON object, base64 URL-encoded, except for the signature, which is a base64 URL-encoded cryptographic hash.

To verify the token signature, you'll first need to decode the JWT header, which should have the following structure:

{
  "kid": "qtott5t4fvau7lp3zk5dizbpju",
  "typ": "at+JWT",
  "alg": "RS256"
}

The typfield should be at+JWT which asserts that the token is an access token. For Wristband-issued access tokens, the alg field should always be RS256, indicating that the token was signed using RSA with a SHA-256 hash. The kid field provides the ID of the public key that can be used to verify the RSA signature.

After decoding the JWT header, use the kid value to lookup the public signing key from Wristband's JWKS Endpoint. The key can be cached in memory to avoid making repeated calls to the JWKS endpoint for each request.

Next, use the public signing key to verify the signature of the JWT. The signature can be computed as follows:

RSA_DECRYPT(public_key, SHA256("<header>.<payload>"))

If the computed signature matches the signature from the JWT, then you can be sure that the JWT is valid and hasn't been tampered with.

Claims Validation

After verifying the access token's signature, the next step is to validate the following token claims from the payload:

  • Issuer (iss): The issuer should have the format: https://<your-application-vanity-domain. For example, if your application vanity domain was yourapp-yourorg.us.wristband.dev then the issuer should be https://yourapp-yourorg.us.wristband.dev.
  • Expiration (exp): Verify that the current time is less than the expiration time.

If the above claim validation checks are successful, you can be certain that the access token is valid and continue processing the request. Conversely, if any of the access token validation checks fail, then the request should not be processed, and a 401 response should be returned.



Did this page help you?