Wei-Shaw/sub2api · error

auth.oidc.callbackMissingToken

Error message

auth.oidc.callbackMissingToken

What it means

In frontend/src/views/auth/OidcCallbackView.vue:607, finalizeCompletion() processes the generic OIDC exchange completion. After handling the bind case, it requires isOAuthLoginCompletion(completion) (access_token present); otherwise it throws the localized 'auth.oidc.callbackMissingToken'. The error means the OIDC exchange endpoint returned a 2xx completion that is neither a bind nor a login result.

Source

Thrown at frontend/src/views/auth/OidcCallbackView.vue:607

    states.includes('bind_login_required') ||
    states.includes('bind_login') ||
    states.includes('adopt_existing_user_by_email') ||
    states.includes('existing_account_required') ||
    states.includes('existing_account_binding_required')
}

async function finalizeCompletion(completion: PendingOAuthExchangeResponse, redirect: string) {
  if (getOAuthCompletionKind(completion) === 'bind') {
    const bindRedirect = sanitizeRedirectPath(completion.redirect || '/profile')
    clearPendingAuthSession()
    clearAllAffiliateReferralCodes()
    appStore.showSuccess(bindSuccessMessage)
    await router.replace(bindRedirect)
    return
  }

  if (!isOAuthLoginCompletion(completion)) {
    throw new Error(t('auth.oidc.callbackMissingToken'))
  }

  persistOAuthTokenContext(completion)
  await authStore.setToken(completion.access_token)
  clearAllAffiliateReferralCodes()
  appStore.showSuccess(t('auth.loginSuccess'))
  await router.replace(redirect)
}

async function finalizePendingAccountResponse(completion: PendingOidcCompletion) {
  applyAdoptionSuggestionState(completion)
  const redirect = sanitizeRedirectPath(completion.redirect || redirectTo.value)

  if (completion.error === 'invitation_required') {
    pendingAccountAction.value = 'none'
    needsInvitation.value = true
    needsAdoptionConfirmation.value = false
    isProcessing.value = false

View on GitHub (pinned to 073e92d171)

Solutions

  1. Check the exchange POST response body in devtools to see the actual completion fields.
  2. Validate OIDC provider config (issuer, client_id/secret, redirect_uri) on the backend so token minting succeeds.
  3. Ensure the pre-auth session cookie survives the IdP redirect (cookie SameSite, domain).
  4. Add an explicit third branch: unknown completion -> friendly error + redirect to login instead of an uncaught throw.

Example fix

// before
if (!isOAuthLoginCompletion(completion)) {
  throw new Error(t('auth.oidc.callbackMissingToken'))
}

// after
if (!isOAuthLoginCompletion(completion)) {
  if (isPendingCompletion(completion)) { await handlePending(completion); return }
  appStore.showError(t('auth.oidc.callbackMissingToken'))
  await router.replace('/login')
  return
}
Defensive patterns

Strategy: type-guard

Type guard

function isOidcLoginCompletion(c: PendingOAuthExchangeResponse): c is PendingOAuthExchangeResponse & { access_token: string } {
  return getOAuthCompletionKind(c) !== 'bind' && typeof (c as any).access_token === 'string' && (c as any).access_token.length > 0;
}

Try / catch

try { await finalizeCompletion(completion, redirect); }
catch (e) {
  if (e.message === t('auth.oidc.callbackMissingToken')) {
    showError('Sign-in could not complete — please retry');
    await router.replace('/login'); return;
  }
  throw e;
}

Prevention

When it happens

Trigger: OIDC authorization-code exchange returns an incomplete completion: session/state mismatch after the IdP redirect (cookie lost), backend token exchange with the IdP failed but was wrapped as success without a token, or the response schema changed (token under a different field name).

Common situations: SameSite=Lax/Strict cookies dropped on the IdP cross-site redirect; backend OIDC provider config wrong (client_secret/issuer) so no token is minted; frontend/backend version mismatch; callback processed twice via refresh.

Related errors


AI-assisted analysis of Wei-Shaw/sub2api@073e92d171 (2026-08-15). Data as JSON: /api/errors/80a2a1fb31640342. Report an issue: GitHub.