Protect a function with a token
Require an OpenID Connect access token on a function's HTTP calls, what the platform checks, and how to call and test a protected function.
Requires: functions/write
A function with authentication on accepts an HTTP call only when it carries a valid access token
from the identity provider you name. Changing the setting needs functions/write. Timer
functions are not affected: they have no public route.
Turn it on
- Open the function and its Configuration tab, section Authentication.
- Switch on Enable JWT Authentication.
- Issuer URL — the issuer exactly as it appears in the tokens'
issclaim. For the platform's own identity provider that ishttps://auth.example.com/realms/stackship, with no slash at the end. - Audience — optional. When you set it, a token's
audclaim must contain it. Tokens from the platform's identity provider carrystackship-kubernetes-client. - Choose Save. The function is redeployed with the new setting.
Warning
With authentication on and Issuer URL empty, every call is refused with
401: the function fails closed rather than serving anyone.
What is checked
The namespace's proxy checks every call before it reaches the function, and the function's runtime checks it again. A token is accepted when:
- it is sent in the header
Authorization: Bearer <token>; - its signature verifies with one of the issuer's keys, which the platform finds through
<issuer>/.well-known/openid-configuration— so the issuer must be reachable from the cluster; - it is signed with an asymmetric algorithm (RSA, RSA-PSS or ECDSA with SHA-256, SHA-384 or SHA-512); unsigned and HMAC-signed tokens are refused;
- its
issequals the Issuer URL and it has anexpthat has not passed, with 60 seconds of leeway; annbf, when present, must have passed too; - its
audcontains the Audience, when one is set.
Any other call is answered with 401. The token only proves who is calling; what the caller may
do is up to your code.
Important
The proxy does not check tokens on paths that begin with
/_api,/healthzor/readyz. A function whose route begins with one of them, such as/healthzone, is checked only by its runtime, and an image of your own only if it checks the token itself. Workloads in the boundary can also call a function directly inside the cluster, without passing the proxy. Functions built from the portal check the token in both places; an image of your own must check it itself — see What the image must do.
Call a protected function
A script or pipeline can sign in as a service account — see Authenticate — and send its token to the function:
TOKEN=$(curl -s https://auth.example.com/realms/stackship/protocol/openid-connect/token \
-d grant_type=client_credentials \
-d client_id="$CLIENT_ID" \
-d client_secret="$CLIENT_SECRET" | jq -r .access_token)
curl -H "Authorization: Bearer $TOKEN" "https://my-namespace.fn.apps.example.com/hello"The same call without the header answers 401, which is a quick way to check that the function
is protected.
Who is calling
When a call is accepted, the proxy passes the caller on to the function in request headers:
| Header | From the claim |
|---|---|
X-Stackship-User-Id |
sub |
X-Stackship-User-Email |
email |
X-Stackship-User-Groups |
groups, separated by commas |
A header is left out when the token has no such claim. The proxy removes these headers from every
incoming request first, on every function, so a caller cannot set them itself on a call through
the proxy. A call made directly inside the cluster does not pass the proxy — see
What is checked. In a Node.js handler, ctx.user holds the claims of the token
that the runtime itself verified.
Namespace defaults
The namespace's Configuration → Authentication has Force Authentication, Default Auth Enabled, Issuer URL and Audience. None of them affects the namespace's functions today:
- Force Authentication is not saved.
- The other settings are saved, but every function carries its own authentication setting, which is off when the function is created.
Turn authentication on for each function that needs it.