> ## Documentation Index
> Fetch the complete documentation index at: https://docs.streambird.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Session Token vs JWT

Understand the differences between session tokens and JWTs to choose the right approach for your application.

MoonKey provides two types of session credentials after successful authentication: **session tokens** and **session JWTs**. Both represent the same authenticated session but serve different use cases and have distinct characteristics.

When a user successfully authenticates through any method (email OTP, OAuth, wallet signature, etc.), MoonKey returns both credentials. The MoonKey SDK automatically stores these in your browser's IndexedDB, and you can choose which one to use based on your application's needs.

## Overview

Each session contains:

* User information and identifiers
* Authentication factors used (email, OAuth provider, wallet, etc.)
* Device and browser information
* Session metadata (creation time, expiration, etc.)

**Session Token:** A standard opaque token that requires API verification on every use.

**Session JWT:** A cryptographically signed JSON Web Token that can be verified independently.

## Session Token

A session token is an opaque, unique string that doesn't contain any user or session information. It must be verified through the MoonKey API to retrieve session details.

### Format

```
session_vy9YGpubKjVn98cw1nT25Msj7jaIpHBinUfD45KLdAOgn9NqEuE4qGHOEchEG5Ue
```

### Characteristics

* **Opaque:** The token itself contains no user information
* **Requires verification:** Must call `/sessions/verify` API on every request
* **Easily revocable:** Can be instantly invalidated via `/sessions/delete` API
* **No expiration in token:** Expiration is managed server-side

### When to use session tokens

Session tokens are ideal for applications that prioritize security and real-time session control:

#### High-security applications

Since session tokens can be instantly revoked, there's no risk of using a token after the session has been invalidated. With JWTs, even if the underlying session is revoked, the JWT might still be considered valid until its 5-minute expiration.

#### Privacy-focused applications

Session tokens don't expose any user information if inspected. Unlike JWTs (which can be decoded to reveal `user_id`, `session_id`, and other claims), session tokens remain completely opaque.

#### Real-time session management

Applications that need immediate session revocation (e.g., admin panels, financial apps) benefit from the server-side verification model where session state is always current.

### Verification example

```javascript theme={null}
// Session token must be verified via API
const response = await fetch('https://api.moonkey.fun/v1/auth/sessions/verify', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    session_token: sessionToken
  })
});

const { session, user } = await response.json();
```

### Advantages

✅ Instant revocation - Session is checked server-side on every request\
✅ Privacy - No user information exposed in the token\
✅ Simplicity - No JWT verification logic needed\
✅ Security - Server maintains complete control over session state

### Disadvantages

❌ Performance - Requires an API call for every verification\
❌ Availability - Dependent on MoonKey API being reachable\
❌ Latency - Adds network round-trip time to each request

## Session JWT

A session JWT is a JSON Web Token that contains the full session object, cryptographically signed using the RS256 algorithm. It can be verified independently without calling the MoonKey API.

### Format

```
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidXNlcl8xMjMiLCJzZXNzaW9uX2lkIjoic2Vzc2lvbl94eXoiLCJleHAiOjE3MDQwNjcyMDB9.signature
```

### Characteristics

* **Self-contained:** Contains full session and user information
* **Cryptographically signed:** Uses RS256 (RSA Signature with SHA-256)
* **Independently verifiable:** Can be verified using public keys from JWKS endpoint
* **Short-lived:** Expires after 5 minutes (300 seconds)

### How JWT verification works

MoonKey uses **RS256** (RSA Signature with SHA-256), an asymmetric signing algorithm:

1. **Signing:** MoonKey signs JWTs with a private key
2. **Verification:** You verify JWTs using a public key
3. **Public keys:** Available at `https://api.moonkey.fun/v1/auth/jwks/{app_id}`

This means anyone with the public key can verify the JWT's authenticity without needing the private key or calling the MoonKey API.

<Info>
  MoonKey leverages the [JSON Web Key (JWK) spec](https://www.rfc-editor.org/rfc/rfc7517) to represent the cryptographic keys used for signing tokens. Learn more about accessing your public keys in the [GetJWKsByApp API reference](/api-reference/sessions/getjwksbyapp).
</Info>

### JWT expiration and refresh

Each JWT expires after **5 minutes** (specified in the `exp` claim). However, you can refresh the JWT by calling the `/sessions/verify` API:

```javascript theme={null}
// JWT has expired (past 5 minutes) but underlying session is still valid
const response = await fetch('https://api.moonkey.fun/v1/auth/sessions/verify', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    session_jwt: expiredJWT
  })
});

const { session_jwt: newJWT, session, user } = await response.json();
// Returns a fresh JWT with a new 5-minute expiration
```

**Important:** If the underlying session has been revoked or expired, even a valid (not-yet-expired) JWT will fail verification when checked against the MoonKey API.

### When to use session JWTs

Session JWTs are ideal for performance-critical and distributed systems:

#### Performance-critical applications

Since JWTs can be verified locally without an API call, you can eliminate network latency from your authentication flow. This is especially valuable for high-traffic applications.

#### Distributed systems

When your backend services are distributed across multiple servers or regions, JWTs allow each service to verify sessions independently without coordinating with a central authentication service.

#### External integrations

If you're integrating with external systems that rely on JWT standards (e.g., third-party APIs, microservices), session JWTs provide a standardized authentication mechanism.

#### Offline or low-connectivity scenarios

Applications that may operate with intermittent internet connectivity can verify JWTs locally without depending on API availability.

### Verification example

```javascript theme={null}
import { createRemoteJWKSet, jwtVerify } from 'jose';

// Fetch public keys from MoonKey
const JWKS = createRemoteJWKSet(
  new URL(`https://api.moonkey.fun/v1/auth/jwks/${appId}`)
);

// Verify JWT locally without API call
try {
  const { payload } = await jwtVerify(sessionJWT, JWKS, {
    algorithms: ['RS256']
  });
  
  // JWT is valid, payload contains session data
  const userId = payload.user_id;
  const sessionId = payload.session_id;
  // ... use session data
} catch (error) {
  // JWT is invalid, expired, or signature doesn't match
  console.error('JWT verification failed:', error);
}
```

### Advantages

✅ Performance - No API call needed for verification\
✅ Offline capability - Can verify without internet connection\
✅ Standards-based - Compatible with JWT ecosystem and libraries\
✅ Distributed - Each service can verify independently

### Disadvantages

❌ Delayed revocation - Valid JWTs remain usable until 5-minute expiration\
❌ Information exposure - JWT payload can be decoded (though not modified)\
❌ Complexity - Requires JWT verification logic and key management\
❌ Size - JWTs are larger than session tokens

## Comparison table

| Feature             | Session Token       | Session JWT                            |
| ------------------- | ------------------- | -------------------------------------- |
| **Verification**    | API call required   | Local verification with public key     |
| **Revocation**      | Instant             | Up to 5-minute delay                   |
| **Performance**     | Slower (API call)   | Faster (no API call)                   |
| **Privacy**         | Opaque token        | User info visible (but not modifiable) |
| **Expiration**      | Server-side managed | 5-minute token expiration              |
| **Size**            | Small (\~80 chars)  | Large (\~500+ chars)                   |
| **Offline support** | No                  | Yes (during 5-min window)              |
| **Complexity**      | Simple              | Requires JWT verification setup        |
| **Standards**       | MoonKey-specific    | JWT standard (RFC 7519)                |

## Choosing the right approach

### Use Session Tokens if:

* Security and instant revocation are top priorities
* You need real-time session control
* Privacy is important (no user data exposure)
* Simplicity is preferred over performance
* Your application is low to moderate traffic

### Use Session JWTs if:

* Performance is critical (high-traffic applications)
* You have distributed services that need to verify sessions
* You're integrating with JWT-based external systems
* Offline or low-connectivity operation is needed
* You can accept a 5-minute revocation delay

### Hybrid approach

You can also use both:

```javascript theme={null}
// Store both credentials
const { session_token, session_jwt } = authResponse;

// Use JWT for normal requests (fast)
const isValidJWT = await verifyJWTLocally(session_jwt);

// Use session token for sensitive operations (secure)
const { session, user } = await verifySessionToken(session_token);
```

This approach gives you performance benefits for most requests while maintaining security for critical operations.

## Best practices

### General

* **Store securely:** The MoonKey SDK uses IndexedDB for secure client-side storage
* **Never log tokens:** Don't include session tokens or JWTs in application logs
* **Use HTTPS:** Always transmit credentials over secure connections
* **Clear on logout:** Remove both tokens when users sign out

### For session tokens

* **Verify on every request:** Always check validity before processing requests
* **Handle expiration gracefully:** Redirect users to login when sessions expire
* **Cache minimally:** Session tokens should be verified fresh on each request

### For session JWTs

* **Refresh proactively:** Refresh JWTs before the 5-minute expiration
* **Validate claims:** Check `exp`, `iat`, and other claims during verification
* **Use reputable libraries:** Use well-maintained JWT libraries like `jose` or `jsonwebtoken`
* **Cache public keys:** Cache JWKS responses to avoid fetching on every verification
* **Re-verify for sensitive actions:** Use the API to double-check session status for critical operations

## Implementation examples

### Using both with fallback

```javascript theme={null}
async function verifySession(sessionToken, sessionJWT) {
  // Try JWT first (fast)
  try {
    const jwtPayload = await verifyJWTLocally(sessionJWT);
    
    // Check if JWT is about to expire
    const expiresAt = jwtPayload.exp;
    const now = Math.floor(Date.now() / 1000);
    const timeRemaining = expiresAt - now;
    
    if (timeRemaining < 60) {
      // Less than 1 minute remaining, refresh via API
      const refreshed = await refreshSessionViaAPI(sessionToken);
      return refreshed;
    }
    
    return jwtPayload;
  } catch (error) {
    // JWT verification failed or expired, fallback to API
    return await verifySessionViaAPI(sessionToken);
  }
}
```

### Automatic JWT refresh

```javascript theme={null}
class SessionManager {
  constructor(sessionToken, sessionJWT) {
    this.sessionToken = sessionToken;
    this.sessionJWT = sessionJWT;
    this.refreshInterval = null;
  }

  startAutoRefresh() {
    // Refresh JWT every 4 minutes (before 5-min expiration)
    this.refreshInterval = setInterval(async () => {
      try {
        const response = await this.refreshJWT();
        this.sessionJWT = response.session_jwt;
      } catch (error) {
        // Session expired or invalid
        this.stopAutoRefresh();
        this.handleSessionExpired();
      }
    }, 4 * 60 * 1000); // 4 minutes
  }

  stopAutoRefresh() {
    if (this.refreshInterval) {
      clearInterval(this.refreshInterval);
    }
  }

  async refreshJWT() {
    const response = await fetch('https://api.moonkey.fun/v1/auth/sessions/verify', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        session_jwt: this.sessionJWT
      })
    });
    return response.json();
  }

  handleSessionExpired() {
    // Clear session and redirect to login
    localStorage.clear();
    window.location.href = '/login';
  }
}
```

## Related resources

<CardGroup cols={2}>
  <Card title="Session Management Guide" icon="list" href="/get-started/sessions/session-management">
    Implement session management with the REST API
  </Card>

  <Card title="Verify Session API" icon="shield-check" href="/api-reference/sessions/verify-session">
    API reference for verifying sessions
  </Card>

  <Card title="Get JWKS" icon="key" href="/api-reference/sessions/getjwksbyapp">
    Retrieve public keys for JWT verification
  </Card>

  <Card title="Sessions Overview" icon="clock" href="/get-started/sessions/overview">
    Learn the basics of MoonKey sessions
  </Card>
</CardGroup>
