Skip to main content
OAuth tokens expire on a short clock. That is separate from how long a user should stay signed in. When a registered Ave app requests offline_access, Ave issues a refresh token so your app can rotate tokens without sending the user through consent again. AveSession gives you one supported lifecycle for that work: persist the token bundle, refresh before expiry, collapse duplicate refresh calls, broadcast changes across tabs, and save rotated refresh tokens atomically.

When to use it

  • You use a registered OAuth app and requested offline_access.
  • You need a fresh id_token for Convex, your API, or another auth client.
  • You want to avoid duplicated refresh calls, stale refresh tokens, and cold-start races.
  • You need framework adapters for Convex, Svelte, Next.js, or Expo.
Quick Ave does not issue refresh tokens. Move to a registered app when you need long-lived sessions. See Upgrading from Quick Ave.

Create a session

import { AveSession, createLocalStorageAdapter } from "@ave-id/sdk";
import { completeOAuthCallback } from "@ave-id/sdk/client";

const session = new AveSession({
  oauth: {
    clientId: process.env.AVE_CLIENT_ID!,
    redirectUri: "https://yourapp.com/callback",
  },
  storage: createLocalStorageAdapter(),
  devtools: process.env.NODE_ENV === "development",
});

await completeOAuthCallback(session, {
  clientId: process.env.AVE_CLIENT_ID!,
  redirectUri: "https://yourapp.com/callback",
});

await session.hydrate();
const idToken = await session.getValidIdToken();
completeOAuthCallback runs finishPkceLogin and setTokensFromResponse together. Use it on your OAuth callback route, then call hydrate() during app startup. If the redirect includes an E2EE #app_key, finishPkceLogin merges it into the token response and removes sensitive fragment parameters from the URL. session.getAppKeyBase64() returns the stored key after hydrate().
Set devtools: true only in development. It exposes window.__aveSessionDev with state, expiry helpers, and lastRefreshError.
getValidIdToken() waits for proactive refresh when the JWT is near expiry. For production refresh-token handling, prefer createMemoryStorage() or a backend-for-frontend instead of localStorage. See Quickstart for storage guidance.

Convex

import { ConvexReactClient } from "convex/react";
import { wireAveSessionToConvex } from "@ave-id/sdk/convex";

const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);

await session.hydrate();
wireAveSessionToConvex(convex, session);
Call session.setTokensFromResponse after login. wireAveSessionToConvex passes a function that calls getValidIdToken() so Convex always receives a valid token.

Next.js App Router

Use AveSessionProvider and useAveSession from @ave-id/sdk/next. If you use Convex, AveConvexBridge can replace a manual wireAveSessionToConvex call. See Next.js App Router + Ave Session.

Svelte

import { aveSessionToStore } from "@ave-id/sdk/svelte";

const sessionStore = aveSessionToStore(session);
// sessionStore.subscribe(...) — status + snapshot
Hydrate in onMount before relying on authenticated UI.

Expo

Native Expo has no Web Crypto for PKCE SHA-256. Configure expo-crypto once with configureAveSdkForExpo from @ave-id/sdk/expo-session, then use AveSession with createSecureStoreAdapter and completeExpoOAuthCallback. See Expo with AuthSession.
import * as ExpoCrypto from "expo-crypto";
import * as SecureStore from "expo-secure-store";
import * as WebBrowser from "expo-web-browser";
import {
  AveSession,
  completeExpoOAuthCallback,
  configureAveSdkForExpo,
  createSecureStoreAdapter,
  initExpoOAuthBrowserSession,
  onExpoAppForegroundRefresh,
  warmUpExpoAuthBrowser,
} from "@ave-id/sdk/expo-session";
import { AppState } from "react-native";

initExpoOAuthBrowserSession(WebBrowser);
configureAveSdkForExpo(ExpoCrypto);

const session = new AveSession({
  oauth: { clientId: "...", redirectUri: "myapp://callback" },
  storage: createSecureStoreAdapter(SecureStore),
  crossTabSync: false,
});

onExpoAppForegroundRefresh(AppState, session);

// Optional: in your login screen component
// useEffect(() => warmUpExpoAuthBrowser(WebBrowser), []);

Backend refresh

Pass customRefresh in AveSession options to POST to your own /api/session/refresh endpoint. That endpoint can read an HttpOnly cookie and return TokenResponse-shaped JSON. The client never stores the refresh token.

E2EE app key

For registered E2EE apps, Ave may append #app_key=... after consent. finishPkceLogin merges that value into the returned TokenResponse, and AveSession stores it as appKeyBase64 next to your OAuth tokens so you can use session.getAppKeyBase64() after hydrate(). You still own crypto policy. Import the key with Web Crypto according to End-to-end encryption. FedCM flows may set app_key on the token response directly; that value is merged the same way.

Dev tools

With devtools: true, window.__aveSessionDev exposes getState(), expiry helpers, and lastRefreshError in development builds.

Server APIs

Use getBearerToken and verifyAveIdTokenFromAuthHeader from @ave-id/sdk/server to turn Authorization: Bearer <id_token> into subject plus claims. See Postgres, SQLite, and JWT auth.
Last modified on May 1, 2026