siyuan-note/siyuan · error
OIDC login binding does not match
Error message
OIDC login binding does not match
What it means
The binding supplied at claim differs from the binding stored on the transaction, and the desktop/validate-without-binding exemption does not apply. The binding ties a state to the session/device that started it, so a mismatch blocks session hijack or replay of a state into another session.
Source
Thrown at kernel/model/oidc.go:755
if state == "" {
return nil, false, errors.New("OIDC state is missing")
}
oidcTransactions.Lock()
cleanupOIDCTransactionsLocked()
transaction := oidcTransactions.byState[state]
if transaction == nil {
oidcTransactions.Unlock()
return nil, false, errors.New("OIDC login transaction was not found or has expired")
}
if transaction.ConfigVersion != oidcConfigurationVersion(Conf.GetOIDC()) {
deleteOIDCTransactionLocked(state)
oidcTransactions.Unlock()
return nil, false, errors.New("OIDC configuration changed during login")
}
if !(allowDesktopWithoutBinding && (transaction.Flow == oidcFlowDesktop || transaction.Flow == oidcFlowValidate)) &&
(binding == "" || binding != transaction.Binding) {
oidcTransactions.Unlock()
return nil, false, errors.New("OIDC login binding does not match")
}
if !transaction.Claimed {
transaction.Claimed = true
copy := *transaction
oidcTransactions.Unlock()
return ©, false, nil
}
done := transaction.Done
oidcTransactions.Unlock()
select {
case <-ctx.Done():
return nil, false, fmt.Errorf("wait for OIDC login transaction failed: %w", ctx.Err())
case <-done:
}
oidcTransactions.Lock()
defer oidcTransactions.Unlock()View on GitHub (pinned to 251596fc0d)
Solutions
- Restart the flow in a single browser profile and keep cookies stable through the whole flow.
- Avoid clearing cookies between start and callback.
- Confirm the client sends the same binding from start through poll/callback.
Defensive patterns
Strategy: validation
Validate before calling
// Confirm the binding that will be presented at claim matches the one captured at start.
if startBinding != expectedBinding {
return errors.New("binding mismatch - restart the flow in one session")
} Try / catch
// On binding mismatch, restart the flow instead of retrying with a different session.
if err != nil && strings.Contains(err.Error(), "binding does not match") {
restartOIDCFlow(c)
} Prevention
- Keep cookies stable from start through callback (no clearing/rotating mid-flow).
- Run the entire flow in one browser profile/container.
- Do not share state URLs across sessions.
When it happens
Trigger: Poll or callback where the binding cookie was cleared or rotated between start and claim, or a different session attempts to consume a state.
Common situations: User cleared cookies mid-login; browser split across profiles/containers; cross-tab interference; an attempt to replay a captured state in another session.
Related errors
- OIDC nonce does not match
- Save OIDC login session failed
- OIDC issuer URL must use HTTPS unless it is a loopback addre
- OIDC login requires at least one claim rule when Allow all u
- remote access requires at least one authentication method
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/9d5e8d1d60a42c4d.
Report an issue: GitHub.