Skip to main content

Refresh sessions

A refresh session lets a local account renew a short-lived portal access token without repeating its password and MFA at every expiry. Each successful renewal replaces the refresh credential. The session ends at its idle or absolute deadline, when revoked, or when the account's security state changes.

This guide targets caddy-security v1.3.0 / go-authcrunch v1.3.8. Portal refresh is separate from an upstream OAuth provider's refresh token and from AuthCrunch's downstream OIDC refresh grants.

Enable local refresh​

Start with a working local portal and users who have appropriate portal and app roles. Set AUTHCRUNCH_SIGNING_KEY to a private random signing secret and ensure the service can read and update /var/lib/authcrunch/users.json. Replace the example hostname and upstream with your deployment.

assets/conf/local/refresh/Caddyfile
# Renewable local sessions: caddy-security v1.3.0 / go-authcrunch v1.3.8.
{
security {
local identity store localdb {
realm local
path /var/lib/authcrunch/users.json
}

authentication portal myportal {
enable identity store localdb
crypto key sign-verify {env.AUTHCRUNCH_SIGNING_KEY}
token refresh {
realms local
public origin https://auth.example.com
base path /auth
access lifetime 300
idle timeout 1800
absolute timeout 28800
max sessions 10000
max rotations 1024
body transport disabled
}
ui {
links {
"Example app" /app
"My profile" /auth/profile/
}
}
}

authorization policy apppolicy {
set auth url https://auth.example.com/auth/
crypto key verify {env.AUTHCRUNCH_SIGNING_KEY}
allow roles app/member
}
}
}

auth.example.com {
route {
redir /auth /auth/ 308
authenticate /auth/* with myportal
@app path /app /app/*
route @app {
authorize with apppolicy
reverse_proxy 127.0.0.1:8080
}
}
}

The example does not create a privileged application account. Give intended users app/member; a portal role such as authp/user alone does not pass the app policy. Use static users or the administrative API to manage accounts.

realms lists attached local realm names, not store nicknames. Each must identify exactly one local store. Other realms remain access-only. Password/MFA completion supplies the evidence for a renewable session; API-key login does not.

The public origin is the exact HTTPS origin, including a nondefault port. The base path is the portal mount before any rewriting. Here the origin is https://auth.example.com and the mount is /auth. HTTP demo cookie settings do not make refresh work over insecure HTTP.

Choose lifetime and capacity​

All durations use integer seconds. Omitted or zero numeric settings select defaults. A present block defaults to enabled and requires realms, origin and base path; omission leaves refresh disabled. An empty block is invalid.

SettingDefaultEffect
access lifetime300Maximum lifetime of each renewable access token; the signing key and remaining absolute lifetime can shorten it
idle timeout1800Time allowed until the next successful renewal; ordinary app requests do not extend it
absolute timeout28800Session deadline from the original authentication; renewal never moves it
max sessions10000Live refresh families across selected realms and transports; new logins at capacity return 503
max rotations1024Successful renewals per family; the next attempt revokes it and requires login
body transportdisabledWhether explicitly selected native JSON credential transport is allowed

Access and idle lifetimes cannot exceed the absolute timeout; absolute timeout cannot exceed 30 days. Short access lifetimes require a client that actually renews. Capacity limits bound session storage; they are not request rate limits.

Refresh rotates a single-use credential against current account evidenceRefresh rotates a single-use credential against current account evidence

Refresh rotates a single-use credential against current account evidence. The browser client serializes renewal across tabs. The portal checks transport, current local identity evidence, deadlines, and replay history before committing a new credential. A spent credential cannot be retried safely after an uncertain response. Native body transport uses explicit JSON credentials rather than this browser cookie exchange.

Scroll horizontally when needed, or open the diagram for a larger view. Download Mermaid source.

Browser behavior​

The access cookie follows the configured app domain/path. The refresh cookie is always host-only, Secure, HttpOnly, SameSite=Lax, and scoped to the portal mount. Its default name is AUTHP_REFRESH_TOKEN; common cookie-prefix settings apply, and cookie name inside token refresh can override it. A __Host- refresh-cookie name requires a root mount.

The embedded portal client coordinates refresh and logout across tabs using Web Locks and local storage. It stores coordination metadata, not credentials. An expired portal visit can offer continuation; /auth/login?fresh=1 starts a fresh login. Browsers without the required coordination support must sign in again. Applications on other origins do not automatically acquire this client; plan their session integration explicitly.

Browser renewal is a same-origin POST to /auth/api/refresh_token:

const response = await fetch('/auth/api/refresh_token', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-Authcrunch-Refresh': '1',
},
body: '{}',
});

This illustrates the request shape, not a complete renewal coordinator. A client must serialize rotation across tabs and handle uncertain responses. Browser requests require the exact Origin, JSON, the refresh header and compatible Fetch Metadata. The response contains session/expiry metadata; credentials are delivered as cookies. /auth/api/refresh_session probes the family ID without rotating it and uses the same checks.

Native JSON clients​

Enable body transport enabled only for a client that needs it. Such a client sets "refresh_transport": "body" on its initial JSON login and every challenge continuation. It sends no Cookie, Origin, or Fetch Metadata headers. Completed login returns access and refresh credentials in JSON and sets no browser cookies.

Renew with this body, replacing the saved credentials atomically on success:

{"refresh_token": "CURRENT_PRIVATE_REFRESH_CREDENTIAL"}

POST it as JSON to /auth/api/refresh_token. The credential is opaque, not an access JWT to decode or send to an application. Cookie and body families cannot be interchanged. Enabling native transport does not enable arbitrary browser CORS. See the Portal API for the challenge protocol.

Replay, logout, and account changes​

A refresh credential is single-use. Reusing a spent credential revokes the family, including its current descendant. If a response is lost after the server rotates, retrying the old credential can therefore end the session. Require a fresh login after an uncertain exchange; do not create an automatic retry loop.

Browser GET /auth/logout displays confirmation when a refresh cookie exists. The protected POST /auth/api/logout revokes the family before deleting cookies. Native clients POST their current refresh credential to the same endpoint. Successful API logout returns {"logged_out": true}. A storage failure returns 503 and must not be presented as successful logout.

Password, role, factor, account-status, or challenge-policy changes invalidate the captured local identity evidence. Renewal checks current account state; it does not simply mint another token from stale claims. Existing signed access tokens still have their own validation and expiry behavior.

Without persistent runtime state, restart loses refresh families. Persistence retains their expiry and spent-credential history; it does not extend deadlines or support multiple active owners.

Diagnose a failed renewal​

StatusMeaning
400Malformed or ambiguous JSON/credentials, duplicate fields, query parameters, or invalid precondition
401Fresh authentication required: invalid, expired, revoked or replayed credential, or changed account evidence
403Origin, mount, or transport checks failed
404Refresh disabled or wrong endpoint/mount
405Wrong method; renewal uses POST
415Request is not JSON
503Temporary signing, backend, capacity, or storage failure

Check the configured origin and mount first. Confirm that cookies reach the portal, the realm participates, and the client selected the right transport. Use an isolated test account to verify renewal, expiry and logout; never replay a live credential as a diagnostic experiment.

Agentic Prompts​

Copy a prompt into your LLM to explore this topic. Each prompt prioritizes upstream repository guidance and code over this page, and asks for version-aware reasoning.

Separate three renewal models
Help me understand Refresh sessions.

Primary authorities (take precedence over website documentation):
https://github.com/greenpau/caddy-security/blob/main/AGENTS.md
https://github.com/greenpau/caddy-security/tree/main/.codex/skills
https://github.com/greenpau/go-authcrunch/blob/main/AGENTS.md
https://github.com/greenpau/go-authcrunch/tree/main/.codex/skills
Read each repository's root AGENTS.md first, then any scoped AGENTS.md that
applies to inspected paths. Read the relevant SKILL.md files and follow their
implementation and test references.

Relevant skills to locate: configuration-authentication,
refresh-token-implementation, refresh-token-transports,
refresh-token-identity.

Secondary reference:
https://docs.authcrunch.com/docs/authenticate/refresh-token

If a source is inaccessible, ask me to paste its relevant text. Identify the
versions your answer applies to; main may be newer than my release. Resolve
disagreements using code and tests, and flag unverified claims. Use synthetic
credentials and redacted examples; explain proposed checks before any changes.

Compare local portal refresh families, upstream OAuth refresh tokens, and
downstream OIDC refresh grants. Trace which local realms and authentication
evidence qualify for portal renewal. Explain access lifetime, idle timeout,
absolute deadline, and rotation limits using a timeline.
Compare browser and body transport
Help me understand Refresh sessions.

Primary authorities (take precedence over website documentation):
https://github.com/greenpau/caddy-security/blob/main/AGENTS.md
https://github.com/greenpau/caddy-security/tree/main/.codex/skills
https://github.com/greenpau/go-authcrunch/blob/main/AGENTS.md
https://github.com/greenpau/go-authcrunch/tree/main/.codex/skills
Read each repository's root AGENTS.md first, then any scoped AGENTS.md that
applies to inspected paths. Read the relevant SKILL.md files and follow their
implementation and test references.

Relevant skills to locate: configuration-authentication,
refresh-token-implementation, refresh-token-transports,
refresh-token-identity.

Secondary reference:
https://docs.authcrunch.com/docs/authenticate/refresh-token

If a source is inaccessible, ask me to paste its relevant text. Identify the
versions your answer applies to; main may be newer than my release. Resolve
disagreements using code and tests, and flag unverified claims. Use synthetic
credentials and redacted examples; explain proposed checks before any changes.

Explain cookies, exact Origin/mount, refresh header, and cross-tab
coordination for browsers, versus explicitly selected body transport for
native JSON login and every continuation. Show why enabling native transport
is not browser CORS and why families cannot change transports.
Diagnose failed renewal
Help me understand Refresh sessions.

Primary authorities (take precedence over website documentation):
https://github.com/greenpau/caddy-security/blob/main/AGENTS.md
https://github.com/greenpau/caddy-security/tree/main/.codex/skills
https://github.com/greenpau/go-authcrunch/blob/main/AGENTS.md
https://github.com/greenpau/go-authcrunch/tree/main/.codex/skills
Read each repository's root AGENTS.md first, then any scoped AGENTS.md that
applies to inspected paths. Read the relevant SKILL.md files and follow their
implementation and test references.

Relevant skills to locate: configuration-authentication,
refresh-token-implementation, refresh-token-transports,
refresh-token-identity.

Secondary reference:
https://docs.authcrunch.com/docs/authenticate/refresh-token

If a source is inaccessible, ask me to paste its relevant text. Identify the
versions your answer applies to; main may be newer than my release. Resolve
disagreements using code and tests, and flag unverified claims. Use synthetic
credentials and redacted examples; explain proposed checks before any changes.

Help me classify malformed JSON, wrong method/media type, disabled endpoint,
origin failure, expired/replayed credential, changed account evidence, and
temporary capacity/storage failures. Ask for redacted metadata and status.
Never replay a live refresh credential as an experiment.
Understand uncertain rotation
Help me understand Refresh sessions.

Primary authorities (take precedence over website documentation):
https://github.com/greenpau/caddy-security/blob/main/AGENTS.md
https://github.com/greenpau/caddy-security/tree/main/.codex/skills
https://github.com/greenpau/go-authcrunch/blob/main/AGENTS.md
https://github.com/greenpau/go-authcrunch/tree/main/.codex/skills
Read each repository's root AGENTS.md first, then any scoped AGENTS.md that
applies to inspected paths. Read the relevant SKILL.md files and follow their
implementation and test references.

Relevant skills to locate: configuration-authentication,
refresh-token-implementation, refresh-token-transports,
refresh-token-identity.

Secondary reference:
https://docs.authcrunch.com/docs/authenticate/refresh-token

If a source is inaccessible, ask me to paste its relevant text. Identify the
versions your answer applies to; main may be newer than my release. Resolve
disagreements using code and tests, and flag unverified claims. Use synthetic
credentials and redacted examples; explain proposed checks before any changes.

Walk through two tabs or a lost response during single-use rotation. Explain
spent-credential reuse, family revocation, atomic credential replacement, and
fresh login after uncertainty. Compare GET logout confirmation with protected
POST revocation and explain a failed-storage logout response.
Plan lifecycle tests
Help me understand Refresh sessions.

Primary authorities (take precedence over website documentation):
https://github.com/greenpau/caddy-security/blob/main/AGENTS.md
https://github.com/greenpau/caddy-security/tree/main/.codex/skills
https://github.com/greenpau/go-authcrunch/blob/main/AGENTS.md
https://github.com/greenpau/go-authcrunch/tree/main/.codex/skills
Read each repository's root AGENTS.md first, then any scoped AGENTS.md that
applies to inspected paths. Read the relevant SKILL.md files and follow their
implementation and test references.

Relevant skills to locate: configuration-authentication,
refresh-token-implementation, refresh-token-transports,
refresh-token-identity.

Secondary reference:
https://docs.authcrunch.com/docs/authenticate/refresh-token

If a source is inaccessible, ask me to paste its relevant text. Identify the
versions your answer applies to; main may be newer than my release. Resolve
disagreements using code and tests, and flag unverified claims. Use synthetic
credentials and redacted examples; explain proposed checks before any changes.

Design disposable-session cases for idle and absolute expiry, rotations,
password/role/factor changes, logout, restart with and without state, and a
native continuation missing its transport selection. State expected
observables without claiming existing signed access tokens are instantly
revoked.

Source Code References​

Start with the code search, then follow the parser, runtime, and tests relevant to this topic. These links target main; use GitHub's branch/tag selector to compare them with your installed release.

  1. Search this topic in both repositories — searches topic-specific symbols and paths across both codebases.
  2. caddy-security: caddyfile_authn_token_refresh.go — adapts the portal token-refresh block.
  3. caddy-security: caddyfile_authn_token_refresh_test.go — tests refresh configuration and adapter boundaries.
  4. go-authcrunch: pkg/authn/token_refresh_config.go — defines refresh origins, realms, lifetimes, and transport settings.
  5. go-authcrunch: pkg/authn/handle_api_refresh_token.go — validates browser/native refresh and logout request transport.
  6. go-authcrunch: pkg/authn/token_refresh_runtime.go — connects refresh rotation to current local account evidence.
  7. go-authcrunch: pkg/authn/token_refresh/manager.go — manages refresh families, rotation, expiry, and revocation.
  8. go-authcrunch: pkg/authn/token_refresh_browser_e2e_test.go — tests browser renewal, coordination, and logout journeys.