Basic JWT policy
Use JWT authentication to verify that incoming requests carry a token issued by a trusted provider before allowing them to reach your upstream services. This configuration lets you protect your APIs from unauthenticated access without adding authentication logic to each service. Enforce JWT authentication by creating a GatewayExtension with a JWT provider and referencing it from a TrafficPolicy.
Before you begin
-
Follow the Get started guide to install kgateway.
-
Follow the Sample app guide to create a gateway proxy with an HTTP listener and deploy the httpbin sample app.
-
Get the external address of the gateway and save it in an environment variable.
export INGRESS_GW_ADDRESS=$(kubectl get svc -n kgateway-system http -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo $INGRESS_GW_ADDRESSkubectl port-forward deployment/http -n kgateway-system 8080:8080
Set up JWT authentication
-
Create a GatewayExtension resource with a
jwtconfiguration. The GatewayExtension holds one or more JWT provider definitions, including the issuer and JWKS source that you want to use to validate incoming tokens. By keeping the provider configuration in a separate resource, the same GatewayExtension can be referenced from more than one TrafficPolicy.kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: GatewayExtension metadata: name: selfminted-jwt namespace: kgateway-system spec: jwt: providers: - name: selfminted issuer: solo.io jwks: local: inline: '{"keys":[{"kty":"RSA","kid":"solo-public-key-001","use":"sig","alg":"RS256","n":"AOfIaJMUm7564sWWNHaXt_hS8H0O1Ew59-nRqruMQosfQqa7tWne5lL3m9sMAkfa3Twx0LMN_7QqRDoztvV3Wa_JwbMzb9afWE-IfKIuDqkvog6s-xGIFNhtDGBTuL8YAQYtwCF7l49SMv-GqyLe-nO9yJW-6wIGoOqImZrCxjxXFzF6mTMOBpIODFj0LUZ54QQuDcD1Nue2LMLsUvGa7V1ZHsYuGvUqzvXFBXMmMS2OzGir9ckpUhrUeHDCGFpEM4IQnu-9U8TbAJxKE5Zp8Nikefr2ISIG2Hk1K2rBAc_HwoPeWAcAWUAR5tWHAxx-UXClSZQ9TMFK850gQGenUp8","e":"AQAB"}]}' EOFField Description spec.jwt.providersA list of JWT providers. If multiple providers are listed, a token that validates against any one of them is accepted (OR logic). nameAn arbitrary name for this provider entry. issuerThe expected value of the issclaim. Tokens with a different issuer are rejected. If omitted, theissfield is not checked.jwks.local.inlineAn inline JSON Web Key Set (JWKS) used to verify token signatures. To use a remote JWKS server instead, replace localwithremoteand provide aurlandbackendRef. -
Create the TrafficPolicy resource that points to the GatewayExtension that you created in the previous step. The following policy applies JWT authentication to all routes on the Gateway. Create the policy in the same namespace as the targeted resource.
kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: TrafficPolicy metadata: name: jwt-policy namespace: kgateway-system spec: targetRefs: - group: gateway.networking.k8s.io kind: Gateway name: http jwtAuth: extensionRef: name: selfminted-jwt EOFField Description targetRefsThe resource to enforce the policy on. When targeting a Gateway, the policy must be in the same namespace as the Gateway. To restrict JWT enforcement to a single route instead, change the kindfield to HTTPRoute and provide the name of the HTTPRoute resource that defines the routes that you want to route to. Make sure to create the policy in the same namespace as the HTTPRoute that you target.jwtAuth.extensionRefThe name of the GatewayExtension resource that holds the JWT provider configuration. The extension must be in the same namespace as the policy. -
Send a request without a JWT and verify that you get a
401 Unauthorizedresponse.curl -vik http://$INGRESS_GW_ADDRESS:8080/headers -H "host: www.example.com:8080"curl -vik localhost:8080/headers -H "host: www.example.com:8080"Example output:
< HTTP/1.1 401 Unauthorized Jwt is missing⚠️Got a
200 OKinstead? The controller silently ignores TrafficPolicy resources that target a resource in a different namespace. Verify that both resources were created in the correct namespace and that the controller accepted them.kubectl get TrafficPolicy jwt-policy -n kgateway-system -o yaml | grep -A10 status kubectl get gatewayextension selfminted-jwt -n kgateway-system -o yaml | grep -A10 statusBoth resources must show an
Acceptedcondition. If either has no status at all, the resource could be in the wrong namespace. -
Save a sample JWT token and send it in the
Authorizationheader. The token is signed by the same issuer and key that you configured in the GatewayExtension resource and can be successfully validated by the gateway proxy.export TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbG8tcHVibGljLWtleS0wMDEifQ.eyJpc3MiOiJzb2xvLmlvIiwib3JnIjoic29sby5pbyIsInN1YiI6ImFsaWNlIiwidGVhbSI6ImRldiIsImV4cCI6MjA3NDI3NDg4NCwibGxtcyI6eyJvcGVuYWkiOlsiZ3B0LTMuNS10dXJibyJdfX0.il5Rjsad65jpQR_pyRzBdEKFSj-ERmBf4K2VksvGvswWVv4n79lYERslr4KCECuiz9y_T-xUiQ9IkhW3YHzl5zo1kajhhIg7Nhnl1AvAqODbnF6wYpLRk0Npna_2T6lK3Yj54qQGi6vXG3IMRpo1_o2DrbdlKx2k_WFegCoQyyYazb4z3ZXfWvTiWqQDJA5wWcM3-jKzAWfNM8zgZWa-1BeAHDvpLcfWtuXEGSjkdCW0FQJOTjgIEqACnnXb2Jio0tWgelh9hDPILI-tvanj3iKCjpf3uF6g8QWSBNoVFfu7F1jJgj5Aj1sX8AV-CQVu2aQx3EHRZ1mL_3w3qSRWPwcurl -vik http://$INGRESS_GW_ADDRESS:8080/headers \ -H "host: www.example.com:8080" \ --header "Authorization: Bearer $TOKEN"curl -vik localhost:8080/headers \ -H "host: www.example.com:8080" \ --header "Authorization: Bearer $TOKEN"Verify that you get a
200 OKresponse.
(Optional) Forward JWT claims as request headers
You can extract claims from the verified JWT and forward them as headers to the upstream service by using the claimsToHeaders field in the GatewayExtension resource.
-
Update the GatewayExtension resource to define the claims that you want to add as headers to the request before it is forwarded upstream. The following example extracts the
teamandorgclaims from the verified JWT and forwards them to the upstream service as thex-teamandx-orgheaders.kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: GatewayExtension metadata: name: selfminted-jwt namespace: kgateway-system spec: jwt: providers: - name: selfminted issuer: solo.io claimsToHeaders: - name: team header: x-team - name: org header: x-org jwks: local: inline: '{"keys":[{"kty":"RSA","kid":"solo-public-key-001","use":"sig","alg":"RS256","n":"AOfIaJMUm7564sWWNHaXt_hS8H0O1Ew59-nRqruMQosfQqa7tWne5lL3m9sMAkfa3Twx0LMN_7QqRDoztvV3Wa_JwbMzb9afWE-IfKIuDqkvog6s-xGIFNhtDGBTuL8YAQYtwCF7l49SMv-GqyLe-nO9yJW-6wIGoOqImZrCxjxXFzF6mTMOBpIODFj0LUZ54QQuDcD1Nue2LMLsUvGa7V1ZHsYuGvUqzvXFBXMmMS2OzGir9ckpUhrUeHDCGFpEM4IQnu-9U8TbAJxKE5Zp8Nikefr2ISIG2Hk1K2rBAc_HwoPeWAcAWUAR5tWHAxx-UXClSZQ9TMFK850gQGenUp8","e":"AQAB"}]}' EOFField Description claimsToHeadersA list of JWT claims to extract and forward as request headers to the upstream service. nameThe name of the JWT claim to extract, for example team.headerThe HTTP header name to set with the extracted claim value, for example x-team. -
Send the request again with the JWT. Verify that the response includes the
X-TeamandX-Orgheaders, which the gateway extracted from the token’steamandorgclaims and forwarded to the upstream service.curl -vik http://$INGRESS_GW_ADDRESS:8080/headers \ -H "host: www.example.com:8080" \ --header "Authorization: Bearer $TOKEN"curl -vik localhost:8080/headers \ -H "host: www.example.com:8080" \ --header "Authorization: Bearer $TOKEN"Example output:
{ "headers": { ... "X-Org": [ "solo.io" ], "X-Team": [ "dev" ] } }
Cleanup
kubectl delete TrafficPolicy jwt-policy -n kgateway-system
kubectl delete gatewayextension selfminted-jwt -n kgateway-system