Client Initiated Backchannel Authentication (CIBA)

Client Initiated Backchannel Authentication (CIBA)

Authway supports Client Initiated Backchannel Authentication (also known as CIBA) as part of the Enterprise Edition.

In most OpenID Connect scenarios the user access a client (application) on the same device as they will sign-in with. CIBA is used when the user needs to sign-in on another device than the client is running on. Here are some scenarios where this can be useful:

  • A call center agent wants to authenticate the caller.
  • A bank teller wants to authenticate the customer in a bank branch.
  • A user wants to authorize a payment they are making at a point of sale terminal.

Authway Support

There are documentation of what Authway supports regarding CIBA in the endpoint documentation.

Note: The use of CIBA ping mode requires that the endpoint to ping is pre-registered on the client.

Scenario: Authenticate a caller with Swedish BankId

When a user makes a phone call to customer service there are many reasons to identify the caller, so that customer service knows who they’re talking too.

Collect the callers social security number with 12 digits and pass it as login hint.

var personalIdentityNumber = "191212121212";
var bindingMessage = "Please identify yourself for better help in our customer service.";

var req = new BackchannelAuthenticationRequest()
{
    Address = "https://environment-company.irmciam.se/connect/ciba",
    ClientId = "ciba-client-id-from-portal",
    ClientSecret = "secret-from-portal",
    Scope = "openid",
    LoginHint = personalIdentityNumber,
    BindingMessage = bindingMessage
};

var client = new HttpClient();
var response = await client.RequestBackchannelAuthenticationAsync(req);

if (response.IsError) throw new Exception(response.Error);

Console.WriteLine($"Login Hint                  : {personalIdentityNumber}");
Console.WriteLine($"Binding Message             : {bindingMessage}");
Console.WriteLine($"Authentication Request Id   : {response.AuthenticationRequestId}");
Console.WriteLine($"Expires In                  : {response.ExpiresIn}");
Console.WriteLine($"Interval                    : {response.Interval}");
Console.WriteLine();

Console.WriteLine($"\nPress enter to start polling the token endpoint.");
Console.ReadLine();

The person won’t be notified, but instead Authway expects you to let the user know that they should start their BankId. When the user does so, the binding message is displayed in the app.

Meanwhile the user is performing the authentication the sales terminal will poll the token endpoint.

var client = new HttpClient();

while (true)
{
    var response = await client.RequestBackchannelAuthenticationTokenAsync(new BackchannelAuthenticationTokenRequest
    {
        Address = "https://environment-company.irmciam.se/connect/token",
        ClientId = "ciba-client-id-from-portal",
        ClientSecret = "secret-from-portal",
        AuthenticationRequestId = authorizeResponse.AuthenticationRequestId
    });

    if (response.IsError)
    {
        if (response.Error == OidcConstants.TokenErrors.AuthorizationPending || response.Error == OidcConstants.TokenErrors.SlowDown)
        {
            Console.WriteLine($"{response.Error}...waiting.");
            await Task.Delay(authorizeResponse.Interval.Value * 1000);
        }
        else
        {
            throw new Exception(response.Error);
        }
    }
    else
    {
        var accessToken = response.AccessToken;
        var identityToken = response.IdentityToken;

        //TODO: Verify the identity token against the order
    }
}

If the user does not fullfil the identification in the BankId app or if the service is unable to match or auto-provision a user an access denied error will be returned.

Scenario: Authenticate the person picking up an order

When a person comes to a store for picking up an order that have been ordered online, the teller wants to make sure that the person is actually the same as the one who placed the order. The sale terminal therefor initiates a backchannel authentication by calling the CIBA endpoint.

(The sample uses Duende.IdentityModel to simplify the code.)

var username = "[email protected]";
var bindingMessage = "Please identify yourself to pick up your order at the Store.";

var req = new BackchannelAuthenticationRequest()
{
    Address = "https://environment-company.irmciam.se/connect/ciba",
    ClientId = "ciba-client-id-from-portal",
    ClientSecret = "secret-from-portal",
    Scope = "openid",
    LoginHint = username,
    BindingMessage = bindingMessage,
    RequestedExpiry = 200
};

var client = new HttpClient();
var response = await client.RequestBackchannelAuthenticationAsync(req);

if (response.IsError) throw new Exception(response.Error);

Console.WriteLine($"Login Hint                  : {username}");
Console.WriteLine($"Binding Message             : {bindingMessage}");
Console.WriteLine($"Authentication Request Id   : {response.AuthenticationRequestId}");
Console.WriteLine($"Expires In                  : {response.ExpiresIn}");
Console.WriteLine($"Interval                    : {response.Interval}");
Console.WriteLine();

Console.WriteLine($"\nPress enter to start polling the token endpoint.");
Console.ReadLine();

The person can be notified by an e-mail or the sales terminal could generate a QR code that will take the user to the https://environment-company.irmciam.se/idenitty/ciba?requestId={response.AuthenticationRequestId} endpoint. The endpoint will display short information to the person and ask him/her to accept (or deny) the authentication request.

Meanwhile the user is performing the authentication the sales terminal will poll the token endpoint.

var client = new HttpClient();

while (true)
{
    var response = await client.RequestBackchannelAuthenticationTokenAsync(new BackchannelAuthenticationTokenRequest
    {
        Address = "https://environment-company.irmciam.se/connect/token",
        ClientId = "ciba-client-id-from-portal",
        ClientSecret = "secret-from-portal",
        AuthenticationRequestId = authorizeResponse.AuthenticationRequestId
    });

    if (response.IsError)
    {
        if (response.Error == OidcConstants.TokenErrors.AuthorizationPending || response.Error == OidcConstants.TokenErrors.SlowDown)
        {
            Console.WriteLine($"{response.Error}...waiting.");
            await Task.Delay(authorizeResponse.Interval.Value * 1000);
        }
        else
        {
            throw new Exception(response.Error);
        }
    }
    else
    {
        var accessToken = response.AccessToken;
        var identityToken = response.IdentityToken;

        //TODO: Verify the identity token against the order
    }
}