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.
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 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)
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)
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