@ondewo/ondewo-csi-client-js
v5.6.2
Published
ONDEWO Communication System Integration (CSI) Client library for Js
Readme
Overview
@ondewo/csi-client-js is a compiled version of the ONDEWO CSI API using the ONDEWO PROTO COMPILER. Here you can find the CSI API documentation.
ONDEWO APIs use Protocol Buffers version 3 (proto3) as their Interface Definition Language (IDL) to define the API interface and the structure of the payload messages. The same interface definition is used for gRPC versions of the API in all languages.
Setup
Using NPM:
npm i --save @ondewo/ondewo-csi-client-jsUsing GitHub:
git clone https://github.com/ondewo/ondewo-csi-client-js.git ## Clone repository
cd ondewo-csi-client-js ## Change into repo-directoy
make setup_developer_environment_locally ## Install dependenciesPackage structure
npm
├── api
│ ├── ondewo_csi_api.js
│ ├── ondewo_csi_api.min.js
│ └── ondewo_csi_api.min.js.map
├── auth
│ ├── grpcWebEndpoint.js
│ └── offlineTokenProvider.js
├── LICENSE
├── package.json
└── README.mdAuthentication (Keycloak bearer)
Auth is bearer-only. Every gRPC-web call must carry a short-lived Keycloak bearer access token in the Authorization metadata header.
Obtain and auto-refresh a token with the offline-token helper shipped in auth/offlineTokenProvider.js. Its login() performs a one-time ROPC login (grant_type=password, scope=offline_access) against the public SDK client ondewo-nlu-cai-sdk-public (no client secret) and refreshes the access token in the background until it lapses.
const { login } = require('@ondewo/ondewo-csi-client-js/auth/offlineTokenProvider');
const provider = await login({
keycloakUrl: 'https://localhost:8443/auth',
realm: 'ondewo-ccai-platform',
clientId: 'ondewo-nlu-cai-sdk-public',
username: '[email protected]',
password: 'super-secret'
});
const client = new ConversationsPromiseClient('https://localhost:8443', null, null);
const metadata = { Authorization: provider.getAuthorizationHeader() };
const response = await client.getS2sPipeline(request, metadata);
provider.stop(); // stop the background refresh loop when doneSee examples/getS2sPipelineExample.js for a complete, unit-tested example.
TLS, mutual TLS and certificates
This package talks gRPC-web (HTTP/1.1 or HTTP/2 through a gRPC-web proxy such as Envoy) from a browser. TLS is therefore the browser's job: the browser negotiates TLS (never SSL), verifies the server certificate against its own trust store, and, for mutual TLS, presents a client certificate from its own certificate store. JavaScript can neither hand the browser a CA certificate nor a client certificate or private key, so this SDK accepts none of them.
The generated clients (e.g. ConversationsPromiseClient) take the endpoint URL as their first argument. Build it with
buildGrpcWebEndpoint from auth/grpcWebEndpoint.js, which picks the scheme from useSecureChannel and enforces the
rules below:
const { buildGrpcWebEndpoint } = require('@ondewo/ondewo-csi-client-js/auth/grpcWebEndpoint');
const endpoint = buildGrpcWebEndpoint({ host: 'ondewo.example.com', port: 443 }); // 'https://ondewo.example.com:443'
const client = new ConversationsPromiseClient(endpoint, null, null);| Mode | Options | What the browser does |
|--------------------------------|-------------------------------------|-----------------------------------------------------------------------|
| Plaintext (not for production) | useSecureChannel: false | http://; console.warn names host:port |
| TLS with system roots | useSecureChannel: true (default) | https://; the server certificate must chain to a trusted CA |
| TLS with a custom CA | useSecureChannel: true (default) | install the CA in the OS / browser trust store (not possible from JS) |
| Mutual TLS | useSecureChannel: true (default) | offers a client certificate from its certificate store when asked |
For mutual TLS the proxy requests the client certificate and the browser offers one from its certificate store (the user picks it, or a managed-device policy selects it). Alternatively the proxy terminates the browser's TLS and uses mutual TLS towards the ONDEWO server.
Rules buildGrpcWebEndpoint enforces:
grpcCert,grpcClientCertandgrpcClientKey(the field names the other ONDEWO SDKs use for PEM content) are refused with an error when non-empty: a private key must never be shipped to a browser, and a CA or client certificate passed from JavaScript would be silently ignored. Empty strings (or leaving them out) mean plain TLS. This also covers half a pair (only a certificate or only a key) anduseSecureChannel: falsewith an identity.useSecureChannelmust be a boolean (the string'false'is an error, not "secure").hostis a bare host name or IP address: no scheme, no port (passport), no path. A bare IPv6 literal is bracketed (::1becomeshttps://[::1]:8443);[::1]is kept. Withoutportthe scheme's default port is used.- Error messages name the option, never its value.
Node.js: the generated bundle sends its requests through the browser's XMLHttpRequest, which Node does not have,
so this package cannot make gRPC calls from Node (only auth/offlineTokenProvider.js runs there). For Node, and for
mutual TLS from code with PEM files, use ondewo-csi-client-nodejs, which takes
grpcCert / grpcClientCert / grpcClientKey as described in its README.
Channel options: gRPC-web has no keepalive, ping, reconnect-backoff or message-size options; the browser manages the connection. The keepalive / backoff defaults of the native ONDEWO SDKs do not apply here.
Mutual TLS at the proxy (Envoy)
The gRPC-web proxy decides whether a client certificate is required. A minimal Envoy downstream TLS context:
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
require_client_certificate: true
common_tls_context:
tls_certificates:
- certificate_chain: { filename: /etc/envoy/server.pem }
private_key: { filename: /etc/envoy/server.key }
validation_context:
trusted_ca: { filename: /etc/envoy/ca.pem }A test PKI with openssl
A CA, a server certificate with SANs, and a client certificate with the clientAuth extended key usage, packed as
PKCS#12 for import into the browser / OS certificate store. For tests only: the keys are unencrypted.
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes -days 365 \
-subj "/CN=Test CA" -keyout ca.key -out ca.pem
printf 'subjectAltName=DNS:localhost,IP:127.0.0.1,IP:::1\nextendedKeyUsage=serverAuth\n' > server.ext
openssl req -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \
-subj "/CN=localhost" -keyout server.key -out server.csr
openssl x509 -req -in server.csr -CA ca.pem -CAkey ca.key -CAcreateserial -days 365 \
-extfile server.ext -out server.pem
printf 'extendedKeyUsage=clientAuth\n' > client.ext
openssl req -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \
-subj "/CN=my-client" -keyout client.key -out client.csr
openssl x509 -req -in client.csr -CA ca.pem -CAkey ca.key -CAcreateserial -days 365 \
-extfile client.ext -out client.pem
openssl pkcs12 -export -in client.pem -inkey client.key -certfile ca.pem -out client.p12 # asks for an export password
chmod 600 *.key client.p12
openssl verify -CAfile ca.pem server.pem client.pemImport ca.pem into the trust store and client.p12 into the certificate store of the browser / OS (Firefox keeps its
own stores: Settings, Privacy & Security, Certificates). Envoy uses server.pem / server.key and trusts ca.pem for
its clients.
TLS security notes
- Never bundle a private key, a PKCS#12 file or its password into a web application: everything shipped to a browser is public.
- The Keycloak tokens are secrets too:
JSON.stringify(provider)andconsole.log(provider)of the token provider render both tokens as***REDACTED***(getAuthorizationHeader()still returns the real one). Do not log theAuthorizationmetadata yourself. - A page served over
httpscannot call anhttpendpoint (mixed content); keepuseSecureChannel: trueoutside local development.
TLS troubleshooting
gRPC-web reports a failed TLS handshake as a network error (status UNAVAILABLE / code 14, or UNKNOWN / code 2 with
"Http response at 400 or 500 level"); the cause is in the browser's developer tools (Network / Console tab):
net::ERR_CERT_AUTHORITY_INVALID(Chrome) /SEC_ERROR_UNKNOWN_ISSUER(Firefox): the server certificate is not signed by a CA the browser trusts. Install the CA in the trust store, or use a publicly trusted certificate.net::ERR_CERT_COMMON_NAME_INVALID: the host in the URL is not in the certificate's subject alternative names. Connect by a name that is, or add the name / IP to the certificate.net::ERR_BAD_SSL_CLIENT_AUTH_CERT/SSL_ERROR_HANDSHAKE_FAILURE_ALERT: the proxy requires a client certificate and the browser offered none, or one the proxy'strusted_cadoes not accept (signed by another CA, or missing theclientAuthextended key usage).- "Mixed Content" /
net::ERR_SSL_PROTOCOL_ERROR: anhttpspage callinghttp, oruseSecureChannel: trueagainst a plaintext port. - CORS errors are not TLS errors: the proxy must answer the gRPC-web preflight (
Access-Control-Allow-Origin,-Headersincludingauthorization,x-grpc-web,content-type).
