Playground biometrics demo BioID home page

BWS API Authentication

All our APIs use the same authentication mechanism based on signed JSON Web Tokens (JWT, see RFC 7519). The signature, the so-called JSON Web Signature (JWS, see RFC 7515) ensures the integrity of the JWT, so it can be used to authenticate a client.

Prerequisites

If you do not have a BWS subscription yet, you can get yourself a BWS trial subscription at the BWS Portal, which will provide you with the necessary information for your BWS client, which is:

  • The BWS host URL: the URL of a host, you can use to call into the BWS API.
  • A client-ID: the client-ID uniquely identifies your client.
  • One or more encryption keys (also called client keys): you have to use one of the keys to sign your JWT for authentication. The keys provided by the BWS portal are currently 512 bit symmetric keys, which are fine to create JSON web signatures using a HMAC SHA-512 algorithm.

Generate JWT

The procedure of creating the JWT is identical for all APIs, regardless of whether it is the gRPC or REST API. Using your BWS client-ID and one of the encryption keys you can generate a JWT and sign it, e.g.:

JSON Web Tokens are supported by .NET using the package System.IdentityModel.Tokens.Jwt:

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;

static string GenerateToken(string clientId, string key, int expireMinutes = 5)
{
    var securityKey = new SymmetricSecurityKey(Convert.FromBase64String(key));
    var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha512);
    List<Claim> claims = new() { new Claim(JwtRegisteredClaimNames.Sub, clientId) };
    var now = DateTime.UtcNow;
    return new JwtSecurityTokenHandler()
        .CreateEncodedJwt(clientId, "BWS", new ClaimsIdentity(claims),
            now, now.AddMinutes(expireMinutes), now, credentials);
}

To generate a JSON Web Token, use one of the Libraries for Token Signing (e.g. io.jsonwebtoken).

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.util.Date;
import javax.crypto.SecretKey;
import java.util.Base64;

public static String generateToken(String clientId, String key, int expireMinutes)
{
    byte[] keyBytes = Base64.getDecoder().decode(key);
    SecretKey signingKey = Keys.hmacShaKeyFor(keyBytes);
    Date now = new Date();
    Date expiryDate = new Date(now.getTime() + expireMinutes * 60 * 1000);
    return Jwts.builder()
        .setSubject(clientId)
        .setIssuer(clientId)
        .setAudience("BWS")
        .setIssuedAt(now)
        .setExpiration(expiryDate)
        .signWith(signingKey, SignatureAlgorithm.HS512).compact();
}

To create JSON Web Tokens, use one of the Libraries for Token Signing, e.g.: python -m pip install pyjwt

import jwt
import datetime
import base64

def GenerateToken(clientId, key, expireMinutes):
    key_bytes = base64.b64decode(key)
    dt = datetime.datetime.now(tz=datetime.timezone.utc)
    dt_delta = datetime.timedelta(minutes=expireMinutes)
    payload = {"iss": clientId,
               'sub': clientId,
               'aud': 'BWS',
               'iat': dt,
               'exp': dt + dt_delta
               }
    header = {"alg": "HS512", "typ": "JWT"}
    return jwt.encode(payload=payload, key=key_bytes, algorithm="HS512", headers=header)

gRPC Client Authentication

Having the JWT created we can now go ahead and create a gRPC channel to call into the gRPC API at the BWS host. This code adds an JWT Bearer Authorization entry to the request header of each gRPC call, e.g.:

gRPC services are consumed by clients in .NET by using the gRPC .NET client library as available in the package Grpc.Net.Client:

using Grpc.Core;
using Grpc.Net.Client;

static GrpcChannel CreateAuthenticatedChannel(Uri host, string token)
{
    var credentials = CallCredentials.FromInterceptor((context, metadata) =>
    {
        metadata.Add("Authorization", $"Bearer {token}");
        return Task.CompletedTask;
    });
    return GrpcChannel.ForAddress(host, new GrpcChannelOptions
    {
        Credentials = ChannelCredentials.Create(ChannelCredentials.SecureSsl, credentials),
    });
}

Please refer to gRPC in Java for an introduction of how to use gRPC with Java.

import java.util.concurrent.Executor;
import io.grpc.CallCredentials;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;

public static ManagedChannel createAuthenticatedChannel(String host, String token)
{
    CallCredentials callCredentials = new CallCredentials() {
            @Override
            public void applyRequestMetadata(RequestInfo requestInfo, 
                Executor executor, MetadataApplier metadataApplier) {
                Metadata headers = new Metadata();
                headers.put(Metadata.Key.of("Authorization", 
                    Metadata.ASCII_STRING_MARSHALLER), "Bearer " + token);
                metadataApplier.apply(headers);
            }
            @Override
            public void thisUsesUnstableApi() {
                throw new UnsupportedOperationException(
                    "Unimplemented method 'thisUsesUnstableApi'");
            }
        };
        ClientInterceptor authInterceptor = new ClientInterceptor() {
            @Override
            public <ReqT, RespT> ClientCall <ReqT, RespT> interceptCall(
                MethodDescriptor <ReqT, RespT> method, CallOptions callOptions, Channel next) {
                CallOptions newCallOptions =
                    callOptions.withCallCredentials(callCredentials);
                return next.newCall(method, newCallOptions);
            }
        };
        ManagedChannel channel =
            ManagedChannelBuilder.forTarget(host)
                .useTransportSecurity()
                .intercept(authInterceptor)
                .build();
    return channel;
}

To use gRPC with Python, simply install the Python Package grpcio, e.g.: python -m pip install grpcio.

import grpc

def CreateAuthenticatedChannel(host, token):
    channel_credentials = grpc.ssl_channel_credentials()
    call_credentials = grpc.access_token_call_credentials(token)
    credentials = grpc.composite_channel_credentials(channel_credentials, call_credentials)
    return grpc.secure_channel(host, credentials)

HTTP Client Authentication

With the JWT we can also create a HTTP session to call into the RESTful JSON API at the BWS host by also adding a JWT Bearer Authorization entry to the request header, e.g.:

using System.Net.Http;
using System.Net.Http.Headers;

static HttpClient CreateAuthenticatedClient(Uri host, string token)
{
    HttpClient httpClient = new HttpClient { BaseAddress = host };
    httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    return httpClient;
}
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;

 // Get
static HttpResponse <String> sendAuthenticatedGetRequest(URI host, String token) throws IOException, InterruptedException
{
    HttpClient httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
    HttpRequest getRequest = HttpRequest.newBuilder()
        .uri(host)
        .header("Authorization", "Bearer " + token)
        .GET()
        .build();

    return httpClient.send(getRequest, HttpResponse.BodyHandlers.ofString());
}

 // Post
 static HttpResponse <String> sendAuthenticatedPostRequest(URI uri, String token, String jsonPayload) throws IOException, InterruptedException
 {
    HttpClient httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
    HttpRequest request = HttpRequest.newBuilder()
        .uri(uri)
        .header("Authorization", "Bearer " + token)
        .header("Content-Type", "application/json")
        .POST(BodyPublishers.ofString(jsonPayload))
        .build();

    return httpClient.send(request, HttpResponse.BodyHandlers.ofString());
}
# todo