> **Building with AI coding agents?** Install the authstack plugin with one command. This equips your agent with accurate Scalekit implementation patterns.
>
> **Recommended**:
> ```bash
> npx @scalekit-inc/cli setup
> ```
>
> Global:
> ```bash
> npm install -g @scalekit-inc/cli
> scalekit setup
> ```
>
> Supports Claude Code, Cursor, GitHub Copilot, Codex + skills for 40+ agents.
> Skills: integrating-agentkit, implementing-saaskit, adding-mcp-oauth, implementing-modular-sso, implementing-scim-provisioning.
> [Full setup guide](https://docs.scalekit.com/dev-kit/build-with-ai/)

---

# Manage user sessions

Store tokens safely with proper cookie security, validate on every request, and refresh with rotation to keep sessions secure
Steps,
  Tabs,
  TabItem,
  Aside,
  Card,
  Badge,
} from '@astrojs/starlight/components';

User sessions determine how long users stay signed in to your application. After users successfully authenticate, you receive session tokens that manage their access. These tokens control session duration, multi-device access, and cross-product authentication within your company's ecosystem.

Store those tokens securely, validate them on every request, and refresh them before they expire. Session middleware writes one encrypted `sk_session` cookie and does that work for you. You can still store and refresh tokens yourself if you prefer.

##  Review the session management sequence

> Image: User session management flow diagram showing how access tokens and refresh tokens work together

1. ## Store the session

   After hosted login, session middleware writes one encrypted `sk_session` cookie. You do not set `accessToken` or `refreshToken` cookies.

   Add `COOKIE_ENCRYPTION_SECRET` to `.env`. Generate it with `openssl rand -base64 32`. Use the same value on every server.

   
     ### Node.js

Install `@scalekit-sdk/node` 2.12.0 or later. Mount `auth.router`. It registers `/login`, `/callback`, and `/logout`. The callback writes `sk_session`.

```ts title="server.ts" wrap
import express from 'express';
import { ScalekitAuth } from '@scalekit-sdk/node/express';

const auth = new ScalekitAuth({
  envUrl: process.env.SCALEKIT_ENVIRONMENT_URL,
  clientId: process.env.SCALEKIT_CLIENT_ID,
  clientSecret: process.env.SCALEKIT_CLIENT_SECRET,
  redirectUri: process.env.REDIRECT_URI,
  cookieEncryptionSecret: process.env.COOKIE_ENCRYPTION_SECRET,
});

const app = express();
app.use(auth.router);
```

     ### Next.js

Install `@scalekit-sdk/node` 2.12.0 or later. Create one `auth` instance. Export login, callback, and logout Route Handlers. The callback writes `sk_session`.

```ts title="lib/auth.ts" wrap
import { ScalekitClient } from '@scalekit-sdk/node';
import { ScalekitAuthNext } from '@scalekit-sdk/node/next';

const scalekit = new ScalekitClient(
  process.env.SCALEKIT_ENVIRONMENT_URL!,
  process.env.SCALEKIT_CLIENT_ID!,
  process.env.SCALEKIT_CLIENT_SECRET!
);

export const auth = new ScalekitAuthNext({
  client: scalekit,
  redirectUri: process.env.REDIRECT_URI!,
  cookieEncryptionSecret: process.env.COOKIE_ENCRYPTION_SECRET!,
});
```

```ts title="app/login/route.ts" wrap
import { auth } from '../../lib/auth';
export const GET = auth.createLoginHandler();
```

```ts title="app/callback/route.ts" wrap
import { auth } from '../../lib/auth';
export const GET = auth.createCallbackHandler();
```

```ts title="app/logout/route.ts" wrap
import { auth } from '../../lib/auth';
export const GET = auth.createLogoutHandler();
```

Pin `runtime: 'nodejs'` in `middleware.ts`, or pass [ScalekitEdgeClient](/saaskit/sdks/nextjs/#scalekitedgeclient) for Edge Runtime.

     
     
     ### Python

Install `scalekit-sdk-python` 2.17.0 or later with the Flask extra. `ScalekitAuth` registers `/login`, `/callback`, and `/logout` and writes `sk_session`.

```py title="app.py" wrap
import os
from flask import Flask
from scalekit.frameworks.flask import ScalekitAuth

app = Flask(__name__)
auth = ScalekitAuth(
    app,
    env_url=os.environ["SCALEKIT_ENVIRONMENT_URL"],
    client_id=os.environ["SCALEKIT_CLIENT_ID"],
    client_secret=os.environ["SCALEKIT_CLIENT_SECRET"],
    redirect_uri=os.environ["REDIRECT_URI"],
    cookie_encryption_secret=os.environ["COOKIE_ENCRYPTION_SECRET"],
    # HTTP localhost drops a Secure cookie, so the session never sticks.
    # A missing Secure flag on real HTTP lets the browser send sk_session in cleartext.
    cookie_secure=False,
)
```

     ### FastAPI

Install `scalekit-sdk-python` 2.17.0 or later with the FastAPI extra. Call `auth.install(app)` to register routes and write `sk_session`.

```py title="app.py" wrap
import os
from fastapi import FastAPI
from scalekit.frameworks.fastapi import ScalekitAuth

app = FastAPI()
auth = ScalekitAuth(
    env_url=os.environ["SCALEKIT_ENVIRONMENT_URL"],
    client_id=os.environ["SCALEKIT_CLIENT_ID"],
    client_secret=os.environ["SCALEKIT_CLIENT_SECRET"],
    redirect_uri=os.environ["REDIRECT_URI"],
    cookie_encryption_secret=os.environ["COOKIE_ENCRYPTION_SECRET"],
    # HTTP localhost drops a Secure cookie, so the session never sticks.
    # A missing Secure flag on real HTTP lets the browser send sk_session in cleartext.
    cookie_secure=False,
)
auth.install(app)
```

     
     
     ### Go

```go title="Gin" collapse={1-7}
import (
  "net/http"
  "os"
  "time"
  "github.com/gin-gonic/gin"
)

// Extract authentication data from the successful authentication response
accessToken := authResult.AccessToken
expiresIn := authResult.ExpiresIn
refreshToken := authResult.RefreshToken
user := authResult.User

// Encrypt tokens before storing to add an additional security layer
encryptedAccessToken := encrypt(accessToken)
encryptedRefreshToken := encrypt(refreshToken)

// Set SameSite mode for CSRF protection
c.SetSameSite(http.SameSiteStrictMode) // Prevents CSRF attacks

// Store encrypted access token in HttpOnly cookie
c.SetCookie(
  "accessToken",
  encryptedAccessToken,
  expiresIn-60, // Subtract 60s buffer for clock skew (seconds in Gin)
  "/",          // Available on all routes
  "",
  os.Getenv("GIN_MODE") == "release", // HTTPS-only in production
  true, // Prevents JavaScript access to mitigate XSS attacks
)

// Store encrypted refresh token in separate HttpOnly cookie
c.SetCookie(
  "refreshToken",
  encryptedRefreshToken,
  0,    // No expiry for refresh token cookie (session lifetime controlled server-side)
  "/",  // Available on all routes
  "",
  os.Getenv("GIN_MODE") == "release", // HTTPS-only in production
  true, // Prevents JavaScript access to mitigate XSS attacks
)
```

     ### Java

```java title="Spring" collapse={1-6}
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.env.Environment;
@Autowired
private Environment env;

// Extract authentication data from the successful authentication response
String accessToken = authResult.getAccessToken();
int expiresIn = authResult.getExpiresIn();
String refreshToken = authResult.getRefreshToken();
User user = authResult.getUser();

// Encrypt tokens before storing to add an additional security layer
String encryptedAccessToken = encrypt(accessToken);
String encryptedRefreshToken = encrypt(refreshToken);

// Store encrypted access token in HttpOnly cookie
Cookie accessTokenCookie = new Cookie("accessToken", encryptedAccessToken);
accessTokenCookie.setMaxAge(expiresIn - 60); // Subtract 60s buffer for clock skew (seconds in Spring)
accessTokenCookie.setHttpOnly(true); // Prevents JavaScript access to mitigate XSS attacks
accessTokenCookie.setSecure("production".equals(env.getActiveProfiles()[0])); // HTTPS-only in production
accessTokenCookie.setPath("/"); // Available on all routes
response.addCookie(accessTokenCookie);
response.setHeader("Set-Cookie",
  response.getHeader("Set-Cookie") + "; SameSite=Strict"); // Prevents CSRF attacks

// Store encrypted refresh token in separate HttpOnly cookie
Cookie refreshTokenCookie = new Cookie("refreshToken", encryptedRefreshToken);
refreshTokenCookie.setHttpOnly(true); // Prevents JavaScript access to mitigate XSS attacks
refreshTokenCookie.setSecure("production".equals(env.getActiveProfiles()[0])); // HTTPS-only in production
refreshTokenCookie.setPath("/"); // Available on all routes
response.addCookie(refreshTokenCookie);
```

   

2. ## Check the session before handling requests

   Session middleware refreshes `sk_session` and redirects to `/login` when the session is missing. A missing session returns **302**, not JSON **401**.

   
     ### Node.js

Guard a route with `auth.requiresAuth`. On success, `req.scalekitUser` is access-token claims. `sub` is always present.

```ts title="server.ts" wrap
app.get('/account', auth.requiresAuth, (req, res) => {
  res.json({ sub: req.scalekitUser?.sub });
});
```

     ### Next.js

Wrap a Route Handler with `withAuth`. Use `createMiddleware()` so every other path fails closed. List public paths in `publicRoutes`.

```ts title="app/account/route.ts" wrap
import { auth } from '../../lib/auth';

export const GET = auth.withAuth(async (request, { user }) => {
  return Response.json({ sub: user?.sub });
});
```

```ts title="middleware.ts" wrap
import { auth } from './lib/auth';

export default auth.createMiddleware({
  publicRoutes: ['/', '/pricing'],
});

export const config = {
  runtime: 'nodejs',
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};
```

     
     
     ### Python

Guard a view with `@auth.requires_auth`. `auth.current_user` is access-token claims.

```py title="app.py" wrap
@app.route("/account")
@auth.requires_auth
def account():
    return {"sub": auth.current_user["sub"]}
```

     ### FastAPI

Protect a route with `Depends(auth.requires_auth)`. The dependency is the user claims.

```py title="app.py" wrap
from fastapi import Depends

@app.get("/account")
async def account(user: dict = Depends(auth.requires_auth)):
    return {"sub": user["sub"]}
```

     
     
     ### Go

```go title="middleware/auth.go" collapse={1-5} "decrypt" "ValidateAccessToken" "RefreshAccessToken"
import (
  "net/http"
  "os"
  "github.com/gin-gonic/gin"
)
func VerifyToken() gin.HandlerFunc {
  return func(c *gin.Context) {
    // Extract encrypted tokens from request cookies
    accessToken, err := c.Cookie("accessToken")
    if err != nil || accessToken == "" {
      c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
      c.Abort()
      return
    }

    // Decrypt the access token before validation
    decryptedAccessToken := decrypt(accessToken)

    // Verify token validity using Scalekit's validation method
    isValid, err := scalekitClient.ValidateAccessToken(c.Request.Context(), decryptedAccessToken)

    if (err != nil || !isValid) {
      // Token expired - attempt transparent refresh
      refreshToken, err := c.Cookie("refreshToken")
      if err == nil && refreshToken != "" {
        decryptedRefreshToken := decrypt(refreshToken)
        authResult, err := scalekitClient.RefreshAccessToken(c.Request.Context(), decryptedRefreshToken)

        if err == nil {
          // Encrypt and store new tokens
          c.SetSameSite(http.SameSiteStrictMode)
          c.SetCookie(
            "accessToken",
            encrypt(authResult.AccessToken),
            authResult.ExpiresIn-60,
            "/",
            "",
            os.Getenv("GIN_MODE") == "release",
            true,
          )
          c.SetCookie(
            "refreshToken",
            encrypt(authResult.RefreshToken),
            0,
            "/",
            "",
            os.Getenv("GIN_MODE") == "release",
            true,
          )
          c.Next()
          return
        }
      }

      c.JSON(http.StatusUnauthorized, gin.H{"error": "Session expired. Please sign in again."})
      c.Abort()
      return
    }

    // Token is valid, proceed to the next handler in the chain
    c.Next()
  }
}
```

     ### Java

```java title="middleware/AuthInterceptor.java" collapse={1-5,22-28, 45-64}
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Cookie;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.core.env.Environment;

/**
 * Intercepts HTTP requests to verify authentication tokens.
 * Transparently refreshes expired tokens to maintain user sessions.
 */
@Component
public class AuthInterceptor implements HandlerInterceptor {
  @Autowired
  private Environment env;

  @Override
  public boolean preHandle(
    HttpServletRequest request,
    HttpServletResponse response,
    Object handler
  ) throws Exception {
    // Extract encrypted tokens from cookies
    String accessToken = getCookieValue(request, "accessToken");
    String refreshToken = getCookieValue(request, "refreshToken");

    if (accessToken == null) {
      response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
      response.getWriter().write("{\"error\": \"Authentication required\"}");
      return false;
    }

    try {
      // Decrypt the access token before validation
      String decryptedAccessToken = decrypt(accessToken);

      // Verify token validity using Scalekit's validation method
      boolean isValid = scalekitClient.validateAccessToken(decryptedAccessToken);

      if (!isValid && refreshToken != null) {
        // Token expired - refresh it transparently
        String decryptedRefreshToken = decrypt(refreshToken);
        AuthResult authResult = scalekitClient.authentication().refreshToken(decryptedRefreshToken);

        // Encrypt and store new tokens
        Cookie accessTokenCookie = new Cookie("accessToken", encrypt(authResult.getAccessToken()));
        accessTokenCookie.setMaxAge(authResult.getExpiresIn() - 60);
        accessTokenCookie.setHttpOnly(true);
        accessTokenCookie.setSecure("production".equals(env.getActiveProfiles()[0]));
        accessTokenCookie.setPath("/");
        response.addCookie(accessTokenCookie);

        Cookie refreshTokenCookie = new Cookie("refreshToken", encrypt(authResult.getRefreshToken()));
        refreshTokenCookie.setHttpOnly(true);
        refreshTokenCookie.setSecure("production".equals(env.getActiveProfiles()[0]));
        refreshTokenCookie.setPath("/");
        response.addCookie(refreshTokenCookie);
        response.setHeader("Set-Cookie", response.getHeader("Set-Cookie") + "; SameSite=Strict");

        return true;
      }

      if (!isValid) {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.getWriter().write("{\"error\": \"Session expired. Please sign in again.\"}");
        return false;
      }

      // Token is valid, allow request to proceed
      return true;
    } catch (Exception e) {
      response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
      response.getWriter().write("{\"error\": \"Authentication failed\"}");
      return false;
    }
  }

  private String getCookieValue(HttpServletRequest request, String cookieName) {
    Cookie[] cookies = request.getCookies();
    if (cookies != null) {
      for (Cookie cookie : cookies) {
        if (cookieName.equals(cookie.getName())) {
          return cookie.getValue();
        }
      }
    }
    return null;
  }
 }
 ```

   

   ## Build it yourself — store cookies and call validateAccessToken

Use this path when you do not use session middleware. Request `offline_access` so you receive a refresh token. Scalekit rotates refresh tokens. Store the new refresh token and discard the old one.

   Store the access token in an HttpOnly cookie. Store the refresh token in a separate HttpOnly cookie. Encrypt both values.

   ```javascript title="Express.js — set cookies"
   const { accessToken, expiresIn, refreshToken } = authResult;
   res.cookie('accessToken', encrypt(accessToken), {
     maxAge: (expiresIn - 60) * 1000,
     httpOnly: true,
     secure: process.env.NODE_ENV === 'production',
     sameSite: 'lax',
   });
   res.cookie('refreshToken', encrypt(refreshToken), {
     httpOnly: true,
     secure: process.env.NODE_ENV === 'production',
     sameSite: 'lax',
   });
   ```

   ```javascript title="Express.js — verify and refresh"
   async function verifyToken(req, res, next) {
     const { accessToken, refreshToken } = req.cookies;
     if (!accessToken) {
       return res.status(401).json({ error: 'Authentication required' });
     }
     const decryptedAccessToken = decrypt(accessToken);
     const isValid = await scalekit.validateAccessToken(decryptedAccessToken);
     if (!isValid && refreshToken) {
       const authResult = await scalekit.refreshAccessToken(decrypt(refreshToken));
       res.cookie('accessToken', encrypt(authResult.accessToken), {
         maxAge: (authResult.expiresIn - 60) * 1000,
         httpOnly: true,
         secure: process.env.NODE_ENV === 'production',
         sameSite: 'lax',
       });
       res.cookie('refreshToken', encrypt(authResult.refreshToken), {
         httpOnly: true,
         secure: process.env.NODE_ENV === 'production',
         sameSite: 'lax',
       });
       return next();
     }
     if (!isValid) {
       return res.status(401).json({ error: 'Session expired. Please sign in again.' });
     }
     next();
   }
   ```

   ```python title="Flask — verify and refresh"
   import os
   from functools import wraps
   from flask import request, jsonify, make_response

   def verify_token(f):
       @wraps(f)
       def decorated_function(*args, **kwargs):
           access_token = request.cookies.get("accessToken")
           refresh_token = request.cookies.get("refreshToken")
           if not access_token:
               return jsonify({"error": "Authentication required"}), 401
           is_valid = scalekit_client.validate_access_token(decrypt(access_token))
           if not is_valid and refresh_token:
               auth_result = scalekit_client.refresh_access_token(decrypt(refresh_token))
               response = make_response(f(*args, **kwargs))
               response.set_cookie(
                   "accessToken",
                   encrypt(auth_result.access_token),
                   max_age=auth_result.expires_in - 60,
                   httponly=True,
                   secure=os.environ.get("FLASK_ENV") == "production",
                   samesite="Lax",
               )
               response.set_cookie(
                   "refreshToken",
                   encrypt(auth_result.refresh_token),
                   httponly=True,
                   secure=os.environ.get("FLASK_ENV") == "production",
                   samesite="Lax",
               )
               return response
           if not is_valid:
               return jsonify({"error": "Session expired. Please sign in again."}), 401
           return f(*args, **kwargs)
       return decorated_function
   ```

   ## TypeScript: get typed claims from validateToken

Use a generic type parameter to get properly typed claims instead of `unknown`. Pass `JWTPayload` from `jose` for access tokens, or `IdTokenClaim` from `@scalekit-sdk/node` for ID tokens:

   ```typescript
   import type { JWTPayload } from 'jose';
   import type { IdTokenClaim } from '@scalekit-sdk/node';

   // Access token — typed as JWTPayload
   const claims = await scalekit.validateToken(accessToken);
   console.log(claims.sub); // user ID

   // ID token — typed with full user profile claims
   const idClaims = await scalekit.validateToken(idToken);
   console.log(idClaims.email);
   ```

3. ## Configure session security and duration

    Manage user session behavior directly from your Scalekit dashboard without modifying application code. Configure session durations and authentication frequency to balance security and user experience for your application.

    > Image: Scalekit Session Policy page with fields for absolute session timeout, idle session timeout, and access token lifetime

      In your Scalekit dashboard, the **Session settings** page lets you set these options:

    - **Absolute session timeout**: This is the maximum time a user can stay signed in, no matter what. After this time, they must log in again. For example, if you set it to 30 minutes, users will be logged out after 30 minutes, even if they are still using your app.

    - **Idle session timeout**: This is the time your app waits before logging out a user who is not active. If you turn this on, the session will end if the user does nothing for the set time. For example, if you set it to 10 minutes, and the user does not click or type for 10 minutes, they will be logged out.

    - **Access token lifetime**: This is how long an access token is valid. When it expires, your app needs to get a new token (using the refresh token) so the user can keep using the app without logging in again. For example, if you set it to 5 minutes, your app will need to refresh the token every 5 minutes.

    Shorter timeouts provide better security, while longer timeouts reduce authentication interruptions.

    > note: Need different session policies per customer?
>
> If specific enterprise customers require stricter session timeouts than your application defaults, you can override the absolute and idle session timeouts on a per-organization basis. See [Organization session policy](/authenticate/manage-organizations/organization-session-policy/) to configure custom policies for individual organizations.

4. ## Manage sessions remotely 

    Beyond client-side session management, Scalekit provides powerful APIs to manage user sessions remotely from your backend application. This enables you to build features like active session management in user account settings, security incident response, or administrative session control.

    These APIs are particularly useful for:
    - Displaying all active sessions in user account settings
    - Allowing users to revoke specific sessions from unfamiliar devices
    - Security incident response and suspicious session termination

    
      ### Node.js

```javascript title="Session Management SDK" wrap showLineNumbers=true
// Get details for a specific session
const sessionDetails = await scalekit.session.getSession('ses_1234567890123456');

// List all sessions for a user with optional filtering
const userSessions = await scalekit.session.getUserSessions('usr_1234567890123456', {
  pageSize: 10,
  filter: {
    status: ['ACTIVE'], // Filter for active sessions only
    startTime: new Date('2025-01-01T00:00:00Z'),
    endTime: new Date('2025-12-31T23:59:59Z')
  }
});

// Revoke a specific session (useful for "Sign out this device" functionality)
const revokedSession = await scalekit.session.revokeSession('ses_1234567890123456');

// Revoke all sessions for a user (useful for "Sign out all devices" functionality)
const revokedSessions = await scalekit.session.revokeAllUserSessions('usr_1234567890123456');
console.log(`Revoked sessions for user`);
```

      ### Python

```python title="Session Management SDK" wrap showLineNumbers=true
# Get details for a specific session
session_details = scalekit_client.sessions.get_session(session_id="ses_1234567890123456")

# List all sessions for a user with optional filtering
from google.protobuf.timestamp_pb2 import Timestamp
from datetime import datetime

start_time = Timestamp()
start_time.FromDatetime(datetime(2025, 1, 1))
end_time = Timestamp()
end_time.FromDatetime(datetime(2025, 12, 31))

filter_obj = scalekit_client.sessions.create_session_filter(
    status=["ACTIVE"], start_time=start_time, end_time=end_time
)
user_sessions = scalekit_client.sessions.get_user_sessions(
    user_id="usr_1234567890123456", page_size=10, filter=filter_obj
)

# Revoke a specific session (useful for "Sign out this device" functionality)
revoked_session = scalekit_client.sessions.revoke_session(session_id="ses_1234567890123456")

# Revoke all sessions for a user (useful for "Sign out all devices" functionality)
revoked_sessions = scalekit_client.sessions.revoke_all_user_sessions(user_id="usr_1234567890123456")
print(f"Revoked sessions for user")
```

      ### Go

```go title="Session Management SDK" wrap showLineNumbers=true
// Get details for a specific session
sessionDetails, err := scalekitClient.Session().GetSession(ctx, "ses_1234567890123456")
if err != nil {
    log.Fatal(err)
}

// List all sessions for a user with optional filtering
// import "time", sessionsv1 "...", "google.golang.org/protobuf/types/known/timestamppb"
startTime, _ := time.Parse(time.RFC3339, "2025-01-01T00:00:00Z")
endTime, _ := time.Parse(time.RFC3339, "2025-12-31T23:59:59Z")
filter := &sessionsv1.UserSessionFilter{
    Status:    []string{"ACTIVE"}, // Filter for active sessions only
    StartTime: timestamppb.New(startTime),
    EndTime:   timestamppb.New(endTime),
}
userSessions, err := scalekitClient.Session().GetUserSessions(ctx, "usr_1234567890123456", 10, "", filter)
if err != nil {
    log.Fatal(err)
}

// Revoke a specific session (useful for "Sign out this device" functionality)
revokedSession, err := scalekitClient.Session().RevokeSession(ctx, "ses_1234567890123456")
if err != nil {
    log.Fatal(err)
}

// Revoke all sessions for a user (useful for "Sign out all devices" functionality)
revokedSessions, err := scalekitClient.Session().RevokeAllUserSessions(ctx, "usr_1234567890123456")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Revoked sessions for user")
```

      ### Java

```java title="Session Management SDK" wrap showLineNumbers=true
// Get details for a specific session
SessionDetails sessionDetails = scalekitClient.sessions().getSession("ses_1234567890123456");

// List all sessions for a user with optional filtering
// import UserSessionFilter, Timestamp, Instant
UserSessionFilter filter = UserSessionFilter.newBuilder()
    .addStatus("ACTIVE")
    .setStartTime(Timestamp.newBuilder().setSeconds(Instant.parse("2025-01-01T00:00:00Z").getEpochSecond()).build())
    .setEndTime(Timestamp.newBuilder().setSeconds(Instant.parse("2025-12-31T23:59:59Z").getEpochSecond()).build())
    .build();
UserSessionDetails userSessions = scalekitClient.sessions().getUserSessions("usr_1234567890123456", 10, "", filter);

// Revoke a specific session (useful for "Sign out this device" functionality)
RevokeSessionResponse revokedSession = scalekitClient.sessions().revokeSession("ses_1234567890123456");

// Revoke all sessions for a user (useful for "Sign out all devices" functionality)
RevokeAllUserSessionsResponse revokedSessions = scalekitClient.sessions().revokeAllUserSessions("usr_1234567890123456");
System.out.println("Revoked sessions for user");
```

    

After you add the helper, open `/account` in the browser. A missing session goes to `/login`. After login, the browser returns to `/account`.

Session middleware validates `sk_session` on each request and refreshes it before expiry. If the refresh token is invalid, the helper sends the browser to `/login`.


---

## More Scalekit documentation

| Resource | What it contains | When to use it |
|----------|-----------------|----------------|
| [/llms.txt](/llms.txt) | Structured index with routing hints per product area | Start here — find which documentation set covers your topic before loading full content |
| [/llms-full.txt](/llms-full.txt) | Complete documentation for all Scalekit products in one file | Use when you need exhaustive context across multiple products or when the topic spans several areas |
| [sitemap-0.xml](https://docs.scalekit.com/sitemap-0.xml) | Full URL list of every documentation page | Use to discover specific page URLs you can fetch for targeted, page-level answers |
