All Products
Search
Document Center

Alibaba Cloud Service Mesh:Authenticate and authorize HTTP requests with JWT

Last Updated:Mar 11, 2026

Service Mesh (ASM) supports JSON Web Token (JWT) authentication to verify the source of HTTP requests. By validating the access token in a request header, ASM confirms that the request comes from a trusted issuer and allows only authorized requests to reach your services.

JWT authentication in ASM uses two Istio security resources that work together:

  • RequestAuthentication validates the token. If a request carries a JWT, this resource checks the token against the configured issuer and JSON Web Key Set (JWKS). Requests with invalid tokens are rejected. However, requests with _no token at all are still accepted_ -- they simply carry no authenticated identity.

  • AuthorizationPolicy enforces access control. This resource specifies who can access the service based on the authenticated identity. To require a valid JWT on every request, pair a RequestAuthentication with an AuthorizationPolicy.

Important

A RequestAuthentication alone does not block unauthenticated requests. To reject requests that carry no token, create a companion AuthorizationPolicy.

Prerequisites

Before you begin, make sure that you have:

Authentication methods

ASM supports two authentication methods:

MethodProtocolScope
Transmission authenticationMutual TLS (mTLS)Service-to-service communication
Source authenticationJWTClient-to-service requests

JWT is a standard that uses representative claims to secure information transmission between two parties. The following steps walk through source authentication with JWT. For more information about JWT, see jwt.io.

Step 1: Deploy sample services

Create a namespace and deploy two sample services -- httpbin (the target service) and sleep (the client) -- to test JWT authentication.

  1. Create a namespace named foo with the label istio-injection=enabled to enable sidecar injection. For more information, see Manage global namespaces.

  2. Create the following YAML files.

    httpbin.yaml

    apiVersion: v1
    kind: ServiceAccount
    metadata:
     name: httpbin
    ---
    apiVersion: v1
    kind: Service
    metadata:
     name: httpbin
     labels:
     app: httpbin
     service: httpbin
    spec:
     ports:
     - name: http
     port: 8000
     targetPort: 80
     selector:
     app: httpbin
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
     name: httpbin
    spec:
     replicas: 1
     selector:
     matchLabels:
     app: httpbin
     version: v1
     template:
     metadata:
     labels:
     app: httpbin
     version: v1
     spec:
     serviceAccountName: httpbin
     containers:
     - image: docker.io/kennethreitz/httpbin
     imagePullPolicy: IfNotPresent
     name: httpbin
     ports:
     - containerPort: 80

    sleep.yaml

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: sleep
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: sleep
      labels:
        app: sleep
        service: sleep
    spec:
      ports:
      - port: 80
        name: http
      selector:
        app: sleep
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: sleep
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: sleep
      template:
        metadata:
          labels:
            app: sleep
        spec:
          terminationGracePeriodSeconds: 0
          serviceAccountName: sleep
          containers:
          - name: sleep
            image: curlimages/curl
            command: ["/bin/sleep", "3650d"]
            imagePullPolicy: IfNotPresent
            volumeMounts:
            - mountPath: /etc/sleep/tls
              name: secret-volume
          volumes:
          - name: secret-volume
            secret:
              secretName: sleep-secret
              optional: true
    ---
  3. Connect to the ACK cluster with kubectl and deploy both services in the foo namespace. For more information, see Obtain the kubeconfig file of a cluster and use kubectl to connect to the cluster.

       kubectl apply -f httpbin.yaml -n foo
       kubectl apply -f sleep.yaml -n foo
  4. Verify that sleep can reach httpbin without any authentication policy in place. Expected output: A 200 OK response confirms that the services are running and reachable.

       kubectl exec -it deploy/sleep -n foo -- curl -I httpbin.foo.svc.cluster.local:8000
       HTTP/1.1 200 OK
       server: envoy
       date: Thu, 21 Dec 2023 07:39:55 GMT
       content-type: text/html; charset=utf-8
       content-length: 9593
       access-control-allow-origin: *
       access-control-allow-credentials: true
       x-envoy-upstream-service-time: 14

Step 2: Create a RequestAuthentication policy

A RequestAuthentication resource defines how ASM validates JWTs for a specific workload. After you apply this policy, requests with invalid tokens are rejected with 401 Unauthorized. Requests with a valid token or no token at all are still accepted.

  1. Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.

  2. On the Mesh Management page, click the name of your ASM instance. In the left-side navigation pane, choose Mesh Security Center > RequestAuthentication. Click Create from YAML.

  3. Select foo from the Namespace drop-down list, enter the following YAML, and click Create. This policy applies to the httpbin service. The jwtRules field specifies that any JWT in the request must have testing@secure.istio.io as its issuer (iss claim). The jwks field provides the public key used to verify the token signature. For more information about JWKS, see RFC 7517: JSON Web Key.

    Important

    In production, use jwksUri instead of inline jwks to point to a remote JWKS endpoint. This enables automatic key rotation without redeploying the policy.

       apiVersion: "security.istio.io/v1beta1"
       kind: "RequestAuthentication"
       metadata:
         name: "jwt-example"
         namespace: foo
       spec:
         selector:
           matchLabels:
             app: httpbin
         jwtRules:
         - issuer: "testing@secure.istio.io"
           jwks: '{ "keys":[ {"e":"AQAB","kid":"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-envvQ","kty":"RSA","n":"xAE7eB6qugXyCAG3yhh7pkDkT65pHymX-P7KfIupjf59vsdo91bSP9C8H07pSAGQO1MV_xFj9VswgsCg4R6otmg5PV2He95lZdHtOcU5DXIg_pbhLdKXbi66GlVeK6ABZOUW3WYtnNHD-91gVuoeJT_DwtGGcp4ignkgXfkiEm4sw-4sfb4qdt5oLbyVpmW6x9cfa7vs2WTfURiCrBoUqgBo_-4WTiULmmHSGZHOjzwa8WtrtOQGsAFjIbno85jp6MnGGGZPYZbDAa_b3y5u-YpW7ypZrvD8BgtKVjgtQgZhLAGezMt0ua3DRrWnKqTZ0BJ_EyxOGuHJrLsn00fnMQ"}]}'
  4. Verify the policy by sending requests from the sleep Pod. The following sample token is used throughout this tutorial: Decode the token payload to see its claims: Output: To decode tokens visually, go to jwt.io. Now test with different tokens from the sleep Pod: Note that requests without a token still succeed. The next step addresses this.

    ScenarioExpected response
    Valid token200 OK
    Invalid token401 Unauthorized
    No token200 OK (no identity, but accepted)
       TOKEN='eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg'
       echo $TOKEN | cut -d '.' -f2 - | base64 --decode -
       {"exp":4685989700,"foo":"bar","iat":1532389700,"iss":"testing@secure.istio.io","sub":"testing@secure.istio.io"}
       # Open a shell in the sleep Pod
       kubectl exec -it deploy/sleep -n foo -- sh
    
       # Request with a valid token -- expect 200 OK
       curl -I -H "Authorization: Bearer $TOKEN" httpbin.foo.svc.cluster.local:8000
    
       # Request with an invalid token -- expect 401 Unauthorized
       curl -I -H "Authorization: Bearer invalidtoken" httpbin.foo.svc.cluster.local:8000

Step 3: Create an AuthorizationPolicy

An AuthorizationPolicy restricts access to the httpbin service so that only requests with a valid, verified JWT are allowed. After you apply this policy, requests without a token are rejected with 403 Forbidden.

  1. Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.

  2. Click the name of your ASM instance. In the left-side navigation pane, choose Mesh Security Center > AuthorizationPolicy. Click Create from YAML.

  3. Select foo from the Namespace drop-down list, enter the following YAML, and click Create. The requestPrincipals field uses the format <iss>/<sub>. This policy allows requests only when the decoded JWT has iss: testing@secure.istio.io and sub: testing@secure.istio.io.

       apiVersion: security.istio.io/v1beta1
       kind: AuthorizationPolicy
       metadata:
         name: require-jwt
         namespace: foo
       spec:
         selector:
           matchLabels:
             app: httpbin
         action: ALLOW
         rules:
         - from:
           - source:
              requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"]
  4. Verify the policy from the sleep Pod: Unlike Step 2, requests without a token now return 403 Forbidden because the AuthorizationPolicy requires a matching principal.

    ScenarioExpected responseReason
    Valid token200 OKToken matches the requestPrincipals rule
    Invalid token403 ForbiddenAuthorization policy denies the request
    No token403 ForbiddenNo authenticated identity to match the ALLOW rule
       kubectl exec -it deploy/sleep -n foo -- sh
    
       # Valid token -- expect 200 OK
       TOKEN='eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg'
       curl -I -H "Authorization: Bearer $TOKEN" httpbin.foo.svc.cluster.local:8000
    
       # Invalid token -- expect 403 Forbidden
       curl -I -H "Authorization: Bearer invalidtoken" httpbin.foo.svc.cluster.local:8000

Step 4: Restrict access by JWT claims

Refine the authorization policy to check specific JWT claims beyond iss and sub. In this example, the policy requires the groups claim to contain group1.

  1. Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.

  2. Click the name of your ASM instance. In the left-side navigation pane, choose Mesh Security Center > AuthorizationPolicy.

  3. Find the require-jwt policy and click YAML in the Actions column.

  4. In the Edit dialog box, update the YAML to include a when condition and click OK. The when condition checks the groups claim in the decoded JWT. A request is allowed only if:

    • iss is testing@secure.istio.io

    • sub is testing@secure.istio.io

    • groups contains group1

       apiVersion: security.istio.io/v1beta1
       kind: AuthorizationPolicy
       metadata:
         name: require-jwt
         namespace: foo
       spec:
         selector:
           matchLabels:
             app: httpbin
         action: ALLOW
         rules:
         - from:
           - source:
              requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"]
           when:
           - key: request.auth.claims[groups]
             values: ["group1"]
  5. Test with a token that includes the groups claim. Sample token with group claims: Decode to inspect the claims: Output: Test from the sleep Pod:

    ScenarioExpected responseReason
    Token with group1200 OKAll conditions met
    Valid token without groups claim403 ForbiddenMissing required claim
       TOKEN_GROUP='eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjM1MzczOTExMDQsImdyb3VwcyI6WyJncm91cDEiLCJncm91cDIiXSwiaWF0IjoxNTM3MzkxMTA0LCJpc3MiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyIsInNjb3BlIjpbInNjb3BlMSIsInNjb3BlMiJdLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.EdJnEZSH6X8hcyEii7c8H5lnhgjB5dwo07M5oheC8Xz8mOllyg--AHCFWHybM48reunF--oGaG6IXVngCEpVF0_P5DwsUoBgpPmK1JOaKN6_pe9sh0ZwTtdgK_RP01PuI7kUdbOTlkuUi2AO-qUyOm7Art2POzo36DLQlUXv8Ad7NBOqfQaKjE9ndaPWT7aexUsBHxmgiGbz1SyLH879f7uHYPbPKlpHU6P9S-DaKnGLaEchnoKnov7ajhrEhGXAQRukhDPKUHO9L30oPIr5IJllEQfHYtt6IZvlNUGeLUcif3wpry1R5tBXRicx2sXMQ7LyuDremDbcNy_iE76Upg'
       echo "$TOKEN_GROUP" | cut -d '.' -f2 - | base64 --decode - | jq
       {
         "exp": 3537391104,
         "groups": [
           "group1",
           "group2"
         ],
         "iat": 1537391104,
         "iss": "testing@secure.istio.io",
         "scope": [
           "scope1",
           "scope2"
         ],
         "sub": "testing@secure.istio.io"
       }
       kubectl exec -it deploy/sleep -n foo -- sh
    
       # Token with group1 -- expect 200 OK
       curl -I -H "Authorization: Bearer $TOKEN_GROUP" httpbin.foo.svc.cluster.local:8000
    
       # Original token (no groups claim) -- expect 403 Forbidden
       curl -I -H "Authorization: Bearer $TOKEN" httpbin.foo.svc.cluster.local:8000

What's next

For more information about JWT algorithms supported by ASM, using jwksUri for remote key fetching, and configuring specific paths to bypass JWT authentication, see FAQ about JWT.