Deep DivesAPI

Authentication

How authentication works in the RawStack API.

The RawStack API uses a token-based authentication system built around short-lived JWT access tokens and longer-lived refresh tokens. The aim is to keep authenticated requests simple for clients while reducing the impact of token theft and making sessions easier to rotate, revoke, and monitor.

The API provides a single endpoint for obtaining tokens. Depending on the context of the request, tokens are delivered in two modes. In a browser context, the refresh token is delivered as an HttpOnly cookie. In all other contexts — such as mobile or server-to-server — both tokens are returned in the response body. This allows the API to support a wide range of client types while still following security best practices for each platform.

Overview

Users log in by POSTing credentials to the auth/tokens endpoint:

POST v1/auth/tokens
Content-Type: application/json
{
  "email": "bla@bla.bla",
  "password": "supersecret"
}

The API responds with two token types along with expiry information:

{
  "item": {
    "accessToken": "eyJhbG...",
    "ttlSeconds": 1500,
    "expiresAt": "2026-04-30T13:02:19.287Z",
    "refreshToken": "7628aec5-9a87-4de5-930b-3f8d4d90ffdb"
  }
}
  • the access token is for normal authenticated API requests
  • the refresh token is for obtaining new access tokens without forcing the user to sign in again

At a high level, the flow looks like this:

  1. A user signs in successfully.
  2. The client receives an access token and a refresh token.
  3. The client sends the access token with protected API requests.
  4. The API validates that token and resolves the authenticated user.
  5. When the access token expires, the client uses the refresh token to obtain a new token pair.
  6. If a refresh token is reused, revoked, or otherwise invalidated, the session can be terminated.

This split keeps day-to-day API requests lightweight while still allowing the system to manage long-lived sessions more safely.

Browser vs non-browser contexts

By setting an Auth-Context: browser header, the API knows to deliver the refresh token as an HttpOnly cookie instead of in the response body. This is a security best practice for web applications because it prevents client-side JavaScript from accessing the refresh token, reducing the risk of cross-site scripting (XSS) attacks.

In this mode the request and response are as follows:

POST v1/auth/tokens
Auth-Context: browser
Content-Type: application/json
{
  "email": "bla@bla.bla",
  "password": "supersecret"
}
{
  "item": {
    "accessToken": "eyJhbG...",
    "ttlSeconds": 1500,
    "expiresAt": "2026-04-30T13:02:19.287Z"
  }
}

The browser context is used in the admin application The web application is built as a BFF, so all communication with the API is server-side. Mobile apps use the default context and handle tokens directly in the response body.

JWT access tokens

Access tokens are signed JWTs that include the user ID and roles. Protected routes use JwtGuard to validate the token, enforce expiry, and make the authenticated user available to controllers and services.

These tokens are intentionally short-lived, typically around 15 minutes. That short lifespan limits the damage a leaked access token can cause because an attacker only has a small window in which it remains valid.

In normal use, the client sends the access token in the Authorization header when calling protected endpoints. Client-side storage depends on the platform and security model of the consuming application, but the key point is that access tokens are the credential used for regular authenticated requests.

Depending on configuration, Redis can also be used to keep token references or related session state. That makes server-side invalidation possible in scenarios such as logout, suspected compromise, or explicit revocation.

Refresh tokens

Refresh tokens are longer-lived credentials used only to obtain new access tokens. They are not intended to be sent with every API request.

The implementation follows standard security principles for modern token-based authentication:

  • Single-use rotation: each refresh token can be used once and is then replaced with a new one.
  • Reuse detection: if an already-used token appears again, the API can treat that as suspicious.
  • Chain invalidation: when reuse is detected, the wider refresh-token chain can be revoked.
  • Secure client storage: clients should store refresh tokens using the most secure platform-appropriate mechanism available, such as an HttpOnly cookie on the web or secure device storage on mobile.
  • Hashed persistence: the API stores hashed refresh-token values rather than raw token strings.

This design reduces the value of stolen refresh tokens and makes session compromise easier to detect and contain.

Password reset tokens

Password reset tokens are one-time-use tokens generated when a user requests a password reset. They are typically delivered by email and are designed to be short-lived and narrowly scoped to the reset flow.

They follow the same general security principles as refresh tokens:

  • they expire quickly
  • they can only be used once
  • they are stored securely on the server side, typically as hashed values
  • they are invalidated immediately after successful use

In the current design, successfully using a valid password reset token can also authenticate the user and begin a new refresh-token chain. That allows the user to move directly from recovery back into a normal authenticated session.

Token-based actions

The API also supports token-based actions delivered through secure links, typically by email. These actions encode a specific intent into a signed token so the API can safely verify what the user is trying to do.

Email verification is the clearest example. When a user creates an account, the system can send a link containing a token that represents the verification action. When that link is used, the API validates the token, decodes the action it represents, and performs the corresponding workflow.

POST v1/auth/action-requests
Content-Type: application/json
{
  "token": "eyJhbG..."
}

The API responds with the action result:

{
  "item": {
    "id": "25c7390a-143d-4333-a88b-ef21f4a7795b",
    "action": "EMAIL_VERIFICATION",
    "status": "PROCESSING"
  }
}

This pattern is useful because it keeps sensitive actions explicit, time-limited, and tamper-resistant.

For concrete endpoints and request shapes, refer to the API specification.

Why this approach works well

This authentication model balances usability and security:

  • short-lived access tokens keep authenticated requests simple
  • long-lived refresh tokens support smoother user sessions
  • rotation and reuse detection make token theft easier to detect
  • hashing long-lived tokens reduces the impact of a database leak
  • token-based email flows support verification and recovery without exposing permanent credentials

Taken together, this gives the API a practical authentication system that is easy for clients to consume and robust enough for real production workflows.