Beta

Create a tenant

A new tenant starts with its own users, OAuth settings, audit history and logs. You are its first Tenant Admin.

BTL Admin

OAUTH 2.0 · LAB

Authenticate the order service with a client certificate

Run a small partner CA, issue a client authentication certificate, and plan tls_client_auth and self_signed_tls_client_auth for a print-orders client. Today, see the tenant ignore the certificate.

PlannedUses your lab tenant

The lesson

New to the labs? Start with the lab toolkit and the shared cast and names every lab uses.

Planned. The core of this lab waits on platform features that are not built yet. The planned walkthrough shows exactly how it will run; Do today is a real exercise you can do now.

Setup

  1. Make sure lab-print-orders exists in OAuth > Clients (Machine to machine preset, grant client_credentials, scope prints.create), and that the Flow policy allows client_credentials. Run export ORDERS_ID=<its client_id>.

  2. Build the partner CA and the order service's certificate. This runs today, on your machine only.

openssl req -x509 -new -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes -keyout lab-ca.key -out lab-ca.crt -days 90 \
  -subj "/O=Lab Print Partners/CN=Lab Partner CA 1" -addext "basicConstraints=critical,CA:TRUE" -addext "keyUsage=critical,keyCertSign,cRLSign"
openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes -keyout orders.key -out orders.csr \
  -subj "/O=Lab Printer/CN=orders.printer.example"
printf 'basicConstraints=critical,CA:FALSE\nkeyUsage=critical,digitalSignature\nextendedKeyUsage=clientAuth\nsubjectAltName=DNS:orders.printer.example\n' > client-ext.cnf
openssl x509 -req -in orders.csr -CA lab-ca.crt -CAkey lab-ca.key -CAcreateserial -out orders.crt -days 30 -extfile client-ext.cnf
  1. Planned (G27): in OAuth > Client certificate authorities, upload lab-ca.crt as a trust anchor for client certificates in this tenant only.

  2. Planned (G8): create a temporary client lab-tmp-mtls-orders with the Machine to machine preset, scope prints.create, authentication method tls_client_auth, expected subject value SAN DNS orders.printer.example. Run export MTLS_ID=<its client_id>.

Planned walkthrough

  1. Read the mutual TLS alias from metadata.

curl -s "$ISSUER/.well-known/oauth-authorization-server" | jq '{token_endpoint_auth_methods_supported, mtls_endpoint_aliases}'
MTLS_TOKEN=$(curl -s "$ISSUER/.well-known/oauth-authorization-server" | jq -r .mtls_endpoint_aliases.token_endpoint)

Expect tls_client_auth and self_signed_tls_client_auth in the list, and a token endpoint on a separate host.

Why it matters: "Separate addresses for mutual TLS". Only the alias host asks for certificates, so browsers on the sign-in pages are never prompted.

  1. Request a token with the certificate and no credential in the request.

curl -s --cert orders.crt --key orders.key "$MTLS_TOKEN" -d grant_type=client_credentials -d client_id=$MTLS_ID -d scope=prints.create | jq .

Why it matters: "A certificate on both sides". Possession of the key is proved in the handshake, and client_id tells the tenant which registration to compare the certificate with.

  1. Open Audit: oauth.token succeeded for lab-tmp-mtls-orders with the method tls_client_auth and the matched subject value, never the certificate itself.

Why it matters: "Matching the certificate to a client". The tenant records which registration value matched.

  1. Switch the client to self_signed_tls_client_auth. Create a self-signed certificate from the same key and register it in the client's key set.

openssl req -x509 -new -key orders.key -out self.crt -days 30 -subj "/CN=orders.printer.example"

Repeat step 2 with --cert self.crt.

Why it matters: no CA vouches for this certificate; the registration itself is the trust.

  1. Fill in the lesson's comparison table from what you just changed: who vouches, what the registration holds, and what a renewal needs.

Do today

  1. Inspect what you built in Setup step 2.

openssl x509 -in orders.crt -noout -subject -issuer -ext subjectAltName,extendedKeyUsage,basicConstraints
openssl verify -CAfile lab-ca.crt -purpose sslclient orders.crt

Confirm the lesson's three details: a private issuer, "TLS Web Client Authentication", and the SAN orders.printer.example. The chain check prints orders.crt: OK.

  1. See that the tenant's host never asks for a client certificate.

openssl s_client -connect ${ISSUER#https://}:443 -servername ${ISSUER#https://} </dev/null 2>/dev/null | grep -i "client certificate"

It prints No client certificate CA names sent.

  1. Present the certificate to the token endpoint anyway, with only client_id in the body, for lab-print-orders.

curl -s --cert orders.crt --key orders.key "$ISSUER/oauth/token" -d grant_type=client_credentials -d client_id=$ORDERS_ID -d scope=prints.create | jq .

The answer is 401 invalid_client, and Audit shows oauth.token rejected unsupported_auth_method for lab-print-orders. The certificate was never requested, so the tenant saw a confidential client with no credential.

  1. In OAuth > Metadata, try to add mtls_endpoint_aliases as an extension field. The tenant refuses to save it: advertising an alias it does not run would send clients' token requests somewhere else. Leave the metadata unchanged.

Break it

These run once the gaps close.

  1. Call the alias with no --cert. Expect 401 invalid_client, the same answer as any failed client authentication.

  2. Issue a second certificate from your CA with SAN reports.printer.example and use it for lab-tmp-mtls-orders. Expect 401 invalid_client: the subject does not match the registration.

Check your work

Today, Audit shows oauth.token rejected unsupported_auth_method for lab-print-orders, and the metadata change was refused. Once the gaps close, Audit shows oauth.token succeeded with tls_client_auth and with self_signed_tls_client_auth, and the two Break it refusals.

Cleanup

  1. Once the gaps close, delete lab-tmp-mtls-orders and the uploaded CA.

  2. Keep lab-ca.crt, lab-ca.key, orders.key, orders.csr, orders.crt and client-ext.cnf for the next two labs, then delete them.

Missing infrastructure

  • G8: the tls_client_auth and self_signed_tls_client_auth methods with per-client expected subject settings.

  • G27: tenant trust anchors for client certificates and X.509 chain validation.

  • G57: a per-tenant alias host where the edge requests a client certificate and passes it, or its SHA-256 thumbprint and verification result, to the tenant. Whether per-tenant private CAs can be checked at the edge or must be checked by the tenant needs investigation before design.

  • Once these exist, the Planned walkthrough runs as written.

Back to all labs

We value your privacy

We use cookies and similar technologies to enhance your browsing experience, and analytics to understand our traffic. By clicking "Allow All", you consent to optional analytics. Cookie Policy

The Lab