Skip to main content
Quick Ave and the standard OIDC flow use the same token families, JWKS endpoint, and OIDC discovery URL. Upgrading is mostly a matter of swapping a few function calls and registering an app in the developer portal.

What you gain

FeatureQuick AveStandard Ave
Refresh tokens✅ (request offline_access scope)
App branding on consent screen
Confidential client (client secret)
Connector delegation (app-to-app)
E2EE app-key delivery
Custom token TTLs
offline_access scope
Longer-lived sessions❌ (1 hour, no refresh)

What stays the same

  • Token families — id_token, access_token_jwt, and access_token keep the same roles
  • OIDC discovery: https://aveid.net/.well-known/openid-configuration
  • JWKS endpoint: https://aveid.net/.well-known/jwks.json
  • Claims (sub, iss, aud, name, email, etc.) — same meaning in both flows
  • Backend JWT validation shape — update the expected id_token audience from origin:https://yourapp.com to your registered clientId
  • Convex domain config — stays https://aveid.net

Upgrade steps

1

Register an app in the developer portal

Go to devs.aveid.net and create an OAuth app. You’ll receive:
  • Client ID (e.g. app_xxxx) — replaces your origin:https://yourapp.com clientId
  • Client secret — only if you choose a confidential client flow
Register the exact callback URL you plan to use (e.g. https://yourapp.com/callback).
You can keep using PKCE without a client secret — just register the app and get a clientId. The confidential client option is only needed if you want server-side token exchange with a secret.
2

Replace startQuickSignIn with startPkceLogin

// Before (Quick Ave)
import { startQuickSignIn } from "@ave-id/sdk/client";
await startQuickSignIn();

// After (Standard Ave with PKCE)
import { startPkceLogin } from "@ave-id/sdk/client";
await startPkceLogin({
  clientId: "app_xxxx",
  redirectUri: "https://yourapp.com/callback",
  scope: "openid profile email offline_access",
});
startPkceLogin works the same way as startQuickSignIn — it generates PKCE params, stores them in sessionStorage, and redirects to Ave.
3

Replace handleQuickCallback with finishPkceLogin

// Before (Quick Ave) — on your /ave/callback page
import { handleQuickCallback } from "@ave-id/sdk/client";
await handleQuickCallback();

// After (Standard Ave) — on your callback page
import { finishPkceLogin } from "@ave-id/sdk/client";

const tokens = await finishPkceLogin({
  clientId: "app_xxxx",
  redirectUri: "https://yourapp.com/callback",
});

// Store tokens in your own session management
// tokens.refresh_token is now available if you requested offline_access
Unlike handleQuickCallback, finishPkceLogin returns the full TokenResponse instead of a QuickIdentity — you manage your own session from here. State verification and token validation happen automatically, just like they did in Quick Ave.
4

Replace getQuickIdentity with your own session management

Quick Ave stored the identity in localStorage automatically. With Standard Ave, you decide how to store and serve the session:
// Before (Quick Ave)
import { getQuickIdentity } from "@ave-id/sdk/client";
const user = getQuickIdentity();
// user.token, user.idToken, user.displayName, etc.

// After (Standard Ave)
// Read from your own session store (cookie, context, database, etc.)
const { user, id_token, access_token, refresh_token } = tokens;
// user.id is the identity UUID
// Use refresh_token to get new tokens when they expire
5

Add refresh token handling

This is what you gain. With offline_access in your scope, you can silently refresh without re-authenticating the user:
import { refreshToken } from "@ave-id/sdk";

const newTokens = await refreshToken(
  { clientId: "app_xxxx", redirectUri: "https://yourapp.com/callback" },
  { refreshToken: storedRefreshToken }
);

// Always store the new refresh token immediately — they rotate on every use
storeRefreshToken(newTokens.refresh_token);

Updating Convex

If you’re using Convex, the only change is the applicationID in your auth.config.ts:
// Before (Quick Ave)
export default {
  providers: [{
    domain: "https://aveid.net",
    applicationID: "origin:https://yourapp.com",  // derived from your origin
  }],
};

// After (Standard Ave)
export default {
  providers: [{
    domain: "https://aveid.net",
    applicationID: "app_xxxx",  // your registered client ID
  }],
};
Everything else — the fetchAccessToken implementation, Convex function auth, the JWKS endpoint — stays the same.
The id_token from a Standard Ave app has aud: "app_xxxx" instead of aud: "origin:https://yourapp.com". Convex validates aud against applicationID, which is why you update applicationID when you upgrade.

Quick reference: function mapping

Quick AveStandard AveNotes
startQuickSignIn()startPkceLogin(config)Pass clientId + redirectUri
handleQuickCallback()exchangeCode(config, { code, codeVerifier })You manage the session
getQuickIdentity()Your own session storeUse the user field from TokenResponse
clearQuickIdentity()Clear your session + optionally redirectNo SDK call needed
startQuickSessionMonitor()Periodic refresh token checkUse refreshToken() on a timer
user.tokentokens.access_token_jwtAve API access JWT; not your app login token
user.idTokentokens.id_tokenSame OIDC token, aud changes to your clientId

If you still get logged out with offline_access

Quick Ave never gives a refresh token — sessions are bounded by access token TTL (~one hour by default). After upgrading to a registered app:
  • Request openid profile email offline_access and persist the full token response.
  • After every refresh response, save the new refresh_token immediately (tokens can rotate).
  • Avoid parallel refreshToken calls from different components — use a single session layer (Ave Session) so refresh is single-flight.
  • For Convex, do not pass a stale id_token string to setAuth — pass a function that returns a valid token (see Convex custom auth).
Last modified on May 23, 2026