# How Credential Session Support Handles Token Refresh in OpenHuman

> Discover how OpenHuman's credential session support manages token refresh. Learn how it retrieves access tokens and delegates OAuth refresh to AuthService for seamless updates.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: how-to-guide
- Published: 2026-05-12

---

**The session support module retrieves the current access token from the session profile but delegates the actual OAuth refresh to AuthService, which exchanges the refresh token and updates the stored credentials.**

The `tinyhumansai/openhuman` repository manages authentication through credential profiles, using a dedicated session profile—identified by the constant `APP_SESSION_PROVIDER`—to supply bearer tokens for RPC and UI layers. When tokens expire, the system employs a strict separation of concerns: lightweight read operations reside in [`session_support.rs`](https://github.com/tinyhumansai/openhuman/blob/main/session_support.rs) while the complex token renewal logic lives in `AuthService`.

## Understanding the Session Architecture

### The Session Profile and TokenSet

Authentication data in OpenHuman is organized into credential profiles defined in [`src/openhuman/credentials/profiles.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/credentials/profiles.rs). The session profile stores an `AuthProfile` containing a `TokenSet` struct that holds the **access token**, **refresh token**, and associated metadata. This profile serves as the single source of truth for active authentication state, consumed by both the core RPC layer and the user interface.

### Separation of Concerns: Reader vs. Refresher

The `session_support` module at [`src/openhuman/credentials/session_support.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/credentials/session_support.rs) operates strictly as a read-only view. According to the OpenHuman source code, the `get_session_token` function queries the session profile but never modifies it. This design keeps session handling lightweight and stateless, delegating all mutable OAuth operations—including token refresh—to `AuthService` in [`src/openhuman/credentials/auth_service.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/credentials/auth_service.rs).

## The Token Refresh Cycle

### Step 1: Retrieving the Stored Access Token

The session support helper extracts the current token through the `get_session_token` function (lines 118–124 in [`src/openhuman/credentials/session_support.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/credentials/session_support.rs)):

```rust
// Returns Ok(Some(token)) or Ok(None) if profile missing
Ok(profile.and_then(|entry| entry.token))

```

This simply returns whatever access token is currently stored in the profile, or `None` if the session profile does not exist.

### Step 2: Detecting Expiration via 401 Responses

The session support module does not validate expiration timestamps or automatically trigger refreshes. Instead, callers such as RPC handlers detect expired tokens when they receive **401 Unauthorized** responses from upstream services. The detection happens outside `session_support`, keeping the module focused solely on credential retrieval.

### Step 3: Performing the OAuth Refresh

When expiration is detected, `AuthService` executes the refresh flow. The service reads the stored `refresh_token` from the profile's `TokenSet`, calls the OAuth provider's token endpoint, and receives a new `access_token`. This logic is implemented in methods like `refresh_oauth_token`, which handles network communication, response parsing, and error recovery independently of the session support code.

### Step 4: Persisting and Subsequent Reads

After obtaining fresh credentials, `AuthService::store_provider_token` updates the session profile with the new access token and optional new refresh token. Because `get_session_token` performs a simple read of `profile.token`, subsequent calls automatically return the refreshed credentials without requiring changes to the caller's logic.

## Code Implementation in OpenHuman

The following pattern demonstrates how RPC handlers coordinate with these components:

```rust
use openhuman_core::openhuman::credentials::session_support::get_session_token;

// Initial token retrieval
let token = match get_session_token(&config)? {
    Some(t) => t,
    None => return Err("session JWT required".into()),
};

// If the downstream call returns 401, trigger refresh
auth_service.refresh_oauth_token(&config)?;  // Exchanges refresh_token

// After successful refresh, retrieve updated token
let new_token = get_session_token(&config)?
    .ok_or("token unavailable after refresh")?;

```

Higher-level operations in [`src/openhuman/credentials/ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/credentials/ops.rs) such as `auth_get_session_token_json` ultimately wrap `get_session_token`, providing JSON interfaces for the UI and RPC layers while maintaining the same underlying refresh semantics.

## Key Files and Responsibilities

- **[`src/openhuman/credentials/session_support.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/credentials/session_support.rs)**: Implements `get_session_token` for read-only access to the session profile.
- **[`src/openhuman/credentials/profiles.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/credentials/profiles.rs)**: Defines `AuthProfile`, `AuthProfileKind`, and `TokenSet` structures.
- **[`src/openhuman/credentials/auth_service.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/credentials/auth_service.rs)**: Implements `refresh_oauth_token` and `store_provider_token` for OAuth flows and credential persistence.
- **[`src/openhuman/credentials/ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/credentials/ops.rs)**: Exposes high-level operations like `auth_get_session_token_json` that callers use to retrieve session tokens.

## Summary

- The **session support** module functions as a read-only accessor, returning the current token from the session profile without performing validation or refresh.
- **Token refresh** is handled exclusively by **AuthService**, which exchanges the `refresh_token` from the `TokenSet` with the OAuth provider.
- After refresh, `AuthService` persists the new access token to the session profile, ensuring subsequent `get_session_token` calls return fresh credentials.
- This architecture keeps credential retrieval lightweight while centralizing complex OAuth mutation logic in a dedicated service.

## Frequently Asked Questions

### Does [`session_support.rs`](https://github.com/tinyhumansai/openhuman/blob/main/session_support.rs) automatically refresh expired tokens?

No. The module only reads the stored access token from the session profile. It does not check expiration timestamps or initiate OAuth flows. Token refresh must be explicitly triggered by callers (such as RPC handlers) through `AuthService`.

### What triggers the token refresh process in OpenHuman?

When an RPC handler or UI component receives a **401 Unauthorized** response indicating an expired access token, it invokes `AuthService::refresh_oauth_token`. This method performs the actual OAuth refresh using the stored `refresh_token` from the credential profile.

### Where is the refresh token stored in the credential system?

The refresh token is stored within the `TokenSet` struct inside the session `AuthProfile`, defined in [`src/openhuman/credentials/profiles.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/credentials/profiles.rs). The `TokenSet` contains both the access token and refresh token, along with metadata required for the OAuth flow.

### How does the RPC layer obtain tokens after a refresh?

The RPC layer calls `get_session_token` from [`session_support.rs`](https://github.com/tinyhumansai/openhuman/blob/main/session_support.rs) (or the higher-level `auth_get_session_token_json` from [`ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/ops.rs)) whenever it needs a bearer token. Because `AuthService` updates the underlying session profile during refresh, these read operations automatically return the new access token without requiring the RPC layer to track refresh state.