Multiple JWT providers

Allow JWT credentials from multiple providers. Then, users can include any valid JWT from any provider to access your secured services. You might use multiple providers when you want to extract different claims for different issuers, such as subdomains on your website. For more information about JWTs, see the JWT overview and API docs.

If you import or export resources across workspaces, your policies might not apply. For more information, see Import and export policies.

Before you begin

This guide assumes that you use the same names for components like clusters, workspaces, and namespaces as in the getting started. If you have different names, make sure to update the sample configuration files in this guide.
  1. Complete the multicluster getting started guide to set up the following testing environment.
    • Three clusters along with environment variables for the clusters and their Kubernetes contexts.
    • The Gloo Platform CLI, meshctl, along with other CLI tools such as kubectl and istioctl.
    • The Gloo management server in the management cluster, and the Gloo agents in the workload clusters.
    • Istio installed in the workload clusters.
    • A simple Gloo workspace setup.
  2. Install Bookinfo and other sample apps.

Configure JWT policies

For multiple JWT providers, you can add the providers to the same JWT policy as shown in the following example. You can also create separate JWT policies for each provider. You can apply a JWT policy at the destination or route level, but you cannot apply multiple JWT policies to the same destination or to the same route in a route table. If you try to apply multiple policies, only the policy that you created first takes effect. In general, you apply policies to routes to protect ingress traffic through the gateway, and to destinations to protect traffic within the service mesh. For more information, see Applying policies.

The following example is for a basic JWT policy with multiple providers that use a local JWT issuer and inline public key. The policy does the following:

Review the following table to understand this configuration. For more information, see the API docs.

Setting Description
applyToDestinations Configure which destinations to apply the policy to, by using labels. Destinations can be a Kubernetes service, VirtualDestination, or ExternalService. If you do not specify any destinations or routes, the policy applies to all destinations in the workspace by default. If you do not specify any destinations but you do specify a route, the policy applies to the route but to no destinations.
phase Set when to apply the JWT filter in the request chain, either before (preAuthz) or after (postAuthz) authorization to have access to the JWT token. You can also set the priority if you have multiple policies in the same phase. The lowest numbered priority is run first. For more information, see Phase considerations. This example sets no priority, so the default value of zero is used.
providers Set up multiple JWT providers for the policy. You can name the providers to help you map the provider when viewing logs to debug. However, the provider name does not affect the policy's behavior and cannot be used by other resources to select the policy. In this example, two providers are set up, with the names dev-example and login-example.
claimsToHeaders Optionally set the claims from the JWT payload that you want to extract and add as headers to the request before the request is forwarded to the upstream destination. This example extracts the same two org and email claims and adds them as headers for both providers. The login-example also has an additional scope claim.
claimsToHeaders.append Enter a boolean value to add a claim's value if the header exists in the request. Use true to append the claim's value to the header, and false to overwrite any existing value in the header.
claimsToHeaders.claim Enter the name of the claim in the JWT payload to get the value for the header.
claimsToHeaders.header Enter the request header that the value of the claim is copied to.
issuer Optionally, set the JWT issuer, usually as a subdomain of a URL or email address. If set, the iss field in the JWT token must match this field, or else the request is denied. If unset, the iss field in the JWT token is not checked. In this example, the issuer is set to a unique URL per provider, dev.example.com or docs.example.com.
keepToken This value is set to true so that the JWT is kept in the request after verification. This way, other policies can use the JWT information as needed.
local Provide the PEM-formatted public key to verify the JWT token. In this example, the public key is written inline to the policy for testing purposes. For production scenarios, you can set a remote reference to your JSON Web Key Set (JWKS) server instead of this local setting.

Try out JWT policies

For quick testing, you can use sample keys with dev-example and login-example JWTs. For more details about the sample JWT, see the GitHub readme.

  1. Apply the JWT policy. For more information about this policy, see the configuration example.

    kubectl apply -f https://gist.githubusercontent.com/artberger/85ac03f284a341faf292fec52dd59227/raw/f27c085d60558e02edc4e01b80069337861e1ee1/jwt-policy-multi.yaml
    
  2. Send a request to the httpbin app without any authentication. Notice that your request is denied with a 401 error.

    Use the kubectl debug command to create an ephemeral curl container in the deployment. This way, the curl container inherits any permissions from the app that you want to test. If you don't run Kubernetes 1.23 or later, you can deploy a separate curl pod or manually add the curl container as shown in the other tab.

    kubectl --context ${REMOTE_CONTEXT1} -n bookinfo debug -i pods/$(kubectl get pod --context ${REMOTE_CONTEXT1} -l app=reviews -A -o jsonpath='{.items[0].metadata.name}') --image=curlimages/curl -- curl -v http://httpbin.httpbin:8000/get
    

    If the output has an error about EphemeralContainers, see Ephemeral containers don’t work when testing Bookinfo.

    Create a temporary curl pod in the bookinfo namespace, so that you can test the app setup. You can also use this method in Kubernetes 1.23 or later, but an ephemeral container might be simpler, as shown in the other tab.

    1. Create the curl pod.
      kubectl run -it -n httpbin --context $REMOTE_CONTEXT1 curl \
        --image=curlimages/curl:7.73.0 --rm  -- sh
      
    2. Send a request to the httpbin app.
      curl -v http://httpbin:8000/get
      

    Example output:

    HTTP/1.1 401 Unauthorized
    www-authenticate: Bearer realm="http://httpbin.httpbin:8000/get"
    ...
    Jwt is missing
    
  3. Get a sample JWT that is preconfigured to meet the validation requirements that you set in the JWT policy for the dev-example provider.

    TOKEN=$(curl https://raw.githubusercontent.com/solo-io/gloo-mesh-use-cases/main/gloo-gateway/jwt/dev-example.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode
    

    Example output:

    {"iss":"https://dev.example.com","exp":4804324736,"iat":1648651136,"org":"internal","email":"dev1@solo.io","group":"engineering","scope":"is:developer%  
    
  4. Try the request to the httpbin app again, this time with your dev-example token. Notice that your request is now accepted!

    Use the kubectl debug command to create an ephemeral curl container in the deployment. This way, the curl container inherits any permissions from the app that you want to test. If you don't run Kubernetes 1.23 or later, you can deploy a separate curl pod or manually add the curl container as shown in the other tab.

    kubectl --context ${REMOTE_CONTEXT1} -n bookinfo debug -i pods/$(kubectl get pod --context ${REMOTE_CONTEXT1} -l app=reviews -A -o jsonpath='{.items[0].metadata.name}') --image=curlimages/curl -- curl -H "X-Auth: Bearer ${TOKEN}" -v http://httpbin.httpbin:8000/get
    

    Create a temporary curl pod in the bookinfo namespace, so that you can test the app setup. You can also use this method in Kubernetes 1.23 or later, but an ephemeral container might be simpler, as shown in the other tab.

    1. Create the curl pod.
      kubectl run -it -n httpbin --context $REMOTE_CONTEXT1 curl \
        --image=curlimages/curl:7.73.0 --rm  -- sh
      
    2. Send a request to the httpbin app.
      curl -H "X-Auth: Bearer ${TOKEN}" -v http://httpbin:8000/get
      

    In the example output you get back a 200 response. You also can see the X-Email and X-Org headers that you appended in the claimsToHeaders section of the policy.

    HTTP/1.1 200 OK 
    ...
    {
      "args": {}, 
      "headers": {
        "Accept": [
          "*/*"
        ],
        "Host": [
          "httpbin.httpbin:8000"
        ],
        "X-Email": [
          "dev1@solo.io"
        ],
        "X-Org": [
          "internal"
        ],
        ...
       
  5. Get another sample JWT that is preconfigured to meet the validation requirements that you set in the JWT policy for the login-example provider.

    TOKEN_LOGIN=$(curl https://raw.githubusercontent.com/solo-io/gloo-mesh-use-cases/main/gloo-gateway/jwt/login-example.jwt -s) && echo "$TOKEN_LOGIN" | cut -d '.' -f2 - | base64 --decode
    

    Example output:

    {"iss":"https://login.example.com","exp":4804324736,"iat":1648651136,"org":"external","email":"user2@example.com","group":"user","scope":"is:reader%  
    
  6. Try the request to the httpbin app again, this time with your login-example token. This request is also accepted, but the output differs from the request that used the dev-example token.

    Use the kubectl debug command to create an ephemeral curl container in the deployment. This way, the curl container inherits any permissions from the app that you want to test. If you don't run Kubernetes 1.23 or later, you can deploy a separate curl pod or manually add the curl container as shown in the other tab.

    kubectl --context ${REMOTE_CONTEXT1} -n bookinfo debug -i pods/$(kubectl get pod --context ${REMOTE_CONTEXT1} -l app=reviews -A -o jsonpath='{.items[0].metadata.name}') --image=curlimages/curl -- curl -H "X-Auth: Bearer ${TOKEN_LOGIN}" -v http://httpbin.httpbin:8000/get
    

    Create a temporary curl pod in the bookinfo namespace, so that you can test the app setup. You can also use this method in Kubernetes 1.23 or later, but an ephemeral container might be simpler, as shown in the other tab.

    1. Create the curl pod.
      kubectl run -it -n httpbin --context $REMOTE_CONTEXT1 curl \
        --image=curlimages/curl:7.73.0 --rm  -- sh
      
    2. Send a request to the httpbin app.
      curl -H "X-Auth: Bearer ${TOKEN_LOGIN}" -v http://httpbin:8000/get
      
    In the example output you get back a 200 response. You also can see the X-Email, X-Org, and X-Scope headers that you appended in the claimsToHeaders section of the policy.

    HTTP/1.1 200 OK 
    ...
    {
      "args": {}, 
      "headers": {
        "Accept": [
          "*/*"
        ],
        "Host": [
          "httpbin.httpbin:8000"
        ],
        "X-Email": [
          "dev1@solo.io"
        ],
        "X-Org": [
          "internal"
        ],
        "X-Scope": [
          "is:reader"
        ],
        ...
        
  7. Optional: Clean up the resources that you created.

    kubectl delete jwtpolicy -n httpbin jwt-policy