The CIBA manager provides methods for implementing Client-Initiated Backchannel Authentication (CIBA) flows.
Overview
CIBA is an authentication flow that allows a client application to initiate authentication for a user through a backchannel, without requiring the user to interact directly with the client. This is useful for scenarios like:
- Push notifications for authentication
- Decoupled authentication flows
- Financial transaction approvals
Methods
Initiate
Initiates a CIBA authentication request.
func (c *CIBA) Initiate(
ctx context.Context,
body ciba.Request,
opts ...RequestOption,
) (*ciba.Response, error)
The context for the request
The CIBA request parameters
Request
login_hint
map[string]string
required
Contains format, iss (issuer), and sub (subject) for the user
The scope for the flow (e.g., “openid profile”)
A human-readable message shown to the user during authentication
The unique identifier of the target API
Client ID (uses default if not provided)
Client Secret (uses default if not provided)
Example
import (
"context"
"github.com/auth0/go-auth0/v2/authentication/ciba"
)
response, err := auth.CIBA.Initiate(
ctx,
ciba.Request{
LoginHint: map[string]string{
"format": "iss_sub",
"iss": "https://your-domain.auth0.com/",
"sub": "auth0|user123",
},
Scope: "openid profile email",
BindingMessage: "Approve login to your account",
Audience: "https://api.example.com",
},
)
if err != nil {
log.Fatalf("Failed to initiate CIBA: %v", err)
}
fmt.Printf("Auth Request ID: %s\n", response.AuthReqID)
fmt.Printf("Expires In: %d seconds\n", response.ExpiresIn)
fmt.Printf("Polling Interval: %d seconds\n", response.Interval)
Response
The authentication request identifier to use for polling
The number of seconds the auth_req_id is valid for
The minimum number of seconds to wait between polling requests
Complete CIBA Flow Example
The following example demonstrates a complete CIBA flow, including initiating the request and polling for the token:
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/auth0/go-auth0/v2/authentication"
"github.com/auth0/go-auth0/v2/authentication/ciba"
"github.com/auth0/go-auth0/v2/authentication/oauth"
)
func main() {
ctx := context.Background()
// Initialize the auth client
auth, err := authentication.New(
ctx,
"your-domain.auth0.com",
authentication.WithClientID("your-client-id"),
authentication.WithClientSecret("your-client-secret"),
)
if err != nil {
log.Fatal(err)
}
// Step 1: Initiate CIBA request
cibaResponse, err := auth.CIBA.Initiate(
ctx,
ciba.Request{
LoginHint: map[string]string{
"format": "iss_sub",
"iss": "https://your-domain.auth0.com/",
"sub": "auth0|user123",
},
Scope: "openid profile email",
BindingMessage: "Approve login to your account",
Audience: "https://api.example.com",
},
)
if err != nil {
log.Fatalf("Failed to initiate CIBA: %v", err)
}
fmt.Printf("CIBA initiated. Auth Request ID: %s\n", cibaResponse.AuthReqID)
fmt.Printf("Polling interval: %d seconds\n", cibaResponse.Interval)
// Step 2: Poll for token
// Note: In production, you would use the token endpoint with grant_type=urn:openid:params:grant-type:ciba
ticker := time.NewTicker(time.Duration(cibaResponse.Interval) * time.Second)
defer ticker.Stop()
timeout := time.After(time.Duration(cibaResponse.ExpiresIn) * time.Second)
for {
select {
case <-timeout:
log.Fatal("CIBA request timed out")
case <-ticker.C:
// Poll the token endpoint
tokens, err := auth.OAuth.LoginWithGrant(
ctx,
"urn:openid:params:grant-type:ciba",
map[string][]string{
"auth_req_id": {cibaResponse.AuthReqID},
},
oauth.IDTokenValidationOptions{},
)
if err != nil {
// Check if it's an authorization_pending error
if aerr, ok := err.(*authentication.Error); ok {
if aerr.Err == "authorization_pending" {
fmt.Println("Still waiting for user approval...")
continue
} else if aerr.Err == "slow_down" {
// Increase polling interval
ticker.Reset(time.Duration(cibaResponse.Interval+5) * time.Second)
continue
}
}
log.Fatalf("Failed to get token: %v", err)
}
// Success!
fmt.Println("\nAuthentication successful!")
fmt.Println("Access Token:", tokens.AccessToken)
fmt.Println("ID Token:", tokens.IDToken)
return
}
}
}
Understanding CIBA Parameters
Login Hint
The login_hint parameter identifies the user to authenticate. It must contain:
- format: The format of the hint (typically “iss_sub”)
- iss: The issuer identifier (your Auth0 domain)
- sub: The user’s subject identifier (user ID)
loginHint := map[string]string{
"format": "iss_sub",
"iss": "https://your-domain.auth0.com/",
"sub": "auth0|user123",
}
Binding Message
The binding message is displayed to the user during authentication. It should be a clear, human-readable message that explains what the user is approving.
bindingMessage := "Approve payment of $100 to Merchant XYZ"
Error Handling
response, err := auth.CIBA.Initiate(ctx, request)
if err != nil {
if aerr, ok := err.(*authentication.Error); ok {
switch aerr.StatusCode {
case 400:
fmt.Println("Bad request:", aerr.Message)
case 401:
fmt.Println("Unauthorized:", aerr.Message)
case 403:
fmt.Println("User not enrolled in CIBA:", aerr.Message)
default:
fmt.Printf("API error: %s\n", aerr.Message)
}
} else {
log.Fatalf("Request failed: %v", err)
}
}
Polling Errors
When polling for tokens, you may receive the following errors:
- authorization_pending: The user hasn’t approved the request yet. Continue polling.
- slow_down: You’re polling too frequently. Increase the polling interval by at least 5 seconds.
- expired_token: The auth_req_id has expired. You need to initiate a new CIBA request.
- access_denied: The user denied the authentication request.
if aerr, ok := err.(*authentication.Error); ok {
switch aerr.Err {
case "authorization_pending":
// Continue polling
continue
case "slow_down":
// Increase polling interval
ticker.Reset(time.Duration(interval+5) * time.Second)
continue
case "expired_token":
// Start a new CIBA flow
log.Fatal("Authentication request expired")
case "access_denied":
// User denied the request
log.Fatal("User denied authentication")
}
}
Best Practices
-
Respect Polling Intervals: Always wait at least the specified
interval between polling requests.
-
Handle Timeouts: Implement proper timeout handling based on the
expires_in value.
-
User Communication: Provide clear feedback to the user about the authentication request status.
-
Error Handling: Implement robust error handling for all possible error states.
-
Security: Always validate tokens received from the CIBA flow before trusting them.
See Also