{"id":16878808,"url":"https://github.com/peterj/jwts-for-services","last_synced_at":"2025-04-11T11:41:13.696Z","repository":{"id":199340346,"uuid":"699587902","full_name":"peterj/jwts-for-services","owner":"peterj","description":null,"archived":false,"fork":false,"pushed_at":"2023-10-17T21:13:09.000Z","size":362,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-25T08:01:46.722Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/peterj.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-02T23:34:45.000Z","updated_at":"2024-08-03T20:06:18.000Z","dependencies_parsed_at":"2023-10-17T04:33:33.048Z","dependency_job_id":"d6667984-8500-4a98-a4da-c3254f6e66b9","html_url":"https://github.com/peterj/jwts-for-services","commit_stats":null,"previous_names":["peterj/jwts-for-services"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterj%2Fjwts-for-services","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterj%2Fjwts-for-services/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterj%2Fjwts-for-services/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterj%2Fjwts-for-services/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/peterj","download_url":"https://codeload.github.com/peterj/jwts-for-services/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248385502,"owners_count":21094900,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-13T15:51:33.107Z","updated_at":"2025-04-11T11:41:13.664Z","avatar_url":"https://github.com/peterj.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JWTs for service-to-service communication\n\nThis repo contains sample apps to demonstrate how to use JWTs for service-to-service communication and how to use Istio with RequestAuthentication and AuthorizationPolicies. All apps are written in Node.js and use the `jsonwebtoken` library.\n\n\n# JWT scenarios\n\n## jwt-factory endpoints\n\n| Endpoint | Body | Description |\n| --- | --- |--- |\n| `GET /keys/:keyId` | - | Generates a new key pair that can be used to sign and verify JWTs with |\n| `POST /issueToken/:keyId?{expired=1}` | Body can contain the claims to be included in the token (note the `iss` (default: `https://jwt-factory/`), `sub` (`keyId`) claims can be provided, but that will overwrite the default values) | Issues a JWT token signed with the provide key referenced by the `keyId`. The token will contain the claims specified in the body. If the `expired` query parameter is set to 1, the token will be issued with an expiration date in the past (i.e. expired token) |\n| `POST /verify/:keyId` | Body contains the `token`, `aud`, and `sub` to verify | Verifies the token using the public key (referenced by `keyId`) and that the token contains the correct audience and subject claims |\n| `GET /utils/publicKey/:keyId` | - | Returns the base64 encoded public key |\n| `GET /utils/keys` | - | Returns the list of keyIds loaded by the instance |\n| `POST /utils/dump` | Dumps/saves all private and public keys to the `/keys` folder |\n\n## app endpoints\n\nNote that the same app can be running as `servicea`, `serviceb`, or `servicec`. Use the `npm run servicea`, `npm run serviceb`, or `npm run servicec` to start the app as the desired service.\n\nAll environment variables for individual services are stored in the `.env` files.\n\n| Endpoint | Description |\n| --- | --- |\n| `GET /scenario1{?expired=1}` | Runs the scenario one (see below). Use `expired=1` to have jwt-factory issue an expired token |\n| `GET /scenario2{?expired=1}` | Runs the scenario two (see below). Provide `expired=1` to have jwt-factory issue an expired token |\n| `GET /scenario3{?expired=1}` | Runs the scenario three (see below). Provide `expired=1` to have Service A issue an expired token |\n| `GET /scenario4{?expired=1}` | Runs the scenario four (see below). Provide `expired=1` to have Service A issue an expired token |\n| `GET /scenario1-server` | Runs as the response to `/scenario1` call on Service B. Note that this shouldn't be invoked directly |\n| `GET /scenario2-server` | Runs as the response to `/scenario2` call on Service B. Note that this shouldn't be invoked directly |\n| `GET /scenario3-server` | Runs as the response to `/scenario3` call on Service B. Note that this shouldn't be invoked directly |\n| `GET /scenario4-server}` | Runs as the rersponse to `/scenario3` call on Service B and Service C. Note that this shouldn't be invoked directly |\n\n\n## Build\n\nTo build the sample apps, go into `/app` and `/jwt-factory` folders and run `npm install`.\n\n## Prerequisites\n\nIn the first two scenarios, Tte keys are created and managed centrally by the `jwt-factory` (i.e. the individual services don't have access to the keys). Another scenario would be where each service manages its own keys and can sign and verify tokens, without calling to the `jwt-factory` (Scenario 3).\n\n1. Run the `jwt-factory` service:\n\n```shell\n# from the jwt-factory folder\nnpm install\nnpm run jwt-factory\n```\n\n1. Create the keys for Service A and Service B from the `jwt-factory` app:\n\n```shell\ncurl localhost:5555/keys/servicea\ncurl localhost:5555/keys/serviceb\n\n# Store the key pairs in the /keys folder (so next time you don't have to generate the keys)\ncurl -X POST localhost:5555/dump \n```\n\nFor Scenario 3, make sure you install [mkcert](https://github.com/FiloSottile/mkcert), so you can create the public/private key.\n\n## Scenario 1\n\nService A calls the `jwt-factory` to request a token to access Service B. Note that the private and public keys are held at the `jwt-factory` app and neither of the services knows the keys.\n\n\n1. Let's start Service A and Service B apps (from two different terminals):\n\n```shell\n# Run one command in each terminal window\nnpm install\n\nnpm run servicea\nnpm run serviceb\n```\n\n2. Send a request to Service A to start the scenario:\n\n```shell\ncurl localhost:3000/scenario1\n```\n\nHere's what happened:\n\n1. Service A made a call to the `jwt-factory` to request a token to access Service B (token with sub: servicea and aud: serviceb)\n\n2. Service A used the token in the Authorization header to call Service B\n\n3. Service B validated the token by making a call to the `jwt-factory` to verify the token was signed by Service A and it contains the correct audience and subject claims\n\n### Expired token\n\nTo simulate an expired token flow, we can add the `?expired=1` when calling `/scenario1` endpoint. The steps are exactly the same as before, the difference is that Service A will be issued an expired token (i.e. simulating the expiration).\n\nThe response will look like this:\n\n```shell\ncurl \"localhost:3000/scenario1?expired=123\"\n```\n\n```console\n{\"message\":\"invalid token\",\"valid\":false,\"error\":{\"name\":\"TokenExpiredError\", \"message\":\"jwt expired\",\"expiredAt\":\"2023-10-02T21:20:03.000Z\"},\"claims\":{}}\n```\n\n## Scenario 2\n\nIn this scenario service A requests JWT tokens for service B and service C. Service A then calls service B and service C using the tokens and returns the results.\n\n1. Let's start Service A, Service B and Service C apps (from three different terminals):\n\n```shell\nnpm run servicea\nnpm run serviceb\nnpm run servicec\n```\n\n2. Send a request to Service A to start the scenario:\n\n```shell\ncurl localhost:3000/scenario2\n```\n\nHere's what happened:\n\n1. Service A made a call to the `jwt-factory` to request a token to access Service B (token with `sub: servicea` and `aud: serviceb`)\n2. Service A made a call to the `jwt-factory` to request a token to access Service C (token with `sub: servicea` and `aud: servicec`)\n3. Service A used the token in the Authorization header to call Service B\n4. Service A used the token in the Authorization header to call Service C\n5. Service A returned the results from Service B and Service C\n\nThe output includes the responses from both services:\n\n```console\n{\"serviceb\":{\"valid\":true,\"claims\":{\"iss\":\"https://jwt-factory/\",\"sub\":\"servicea\",\"aud\":\"serviceb\",\"exp\":1696290884,\"iat\":1696287284}},\"servicec\":{\"valid\":true,\"claims\":{\"iss\":\"https://jwt-factory/\",\"sub\":\"servicea\",\"aud\":\"servicec\",\"exp\":1696290884,\"iat\":1696287284}}}\n```\n\nYou can add the `?expired=1` to the request to simulate the expiration of one of the tokens.\n## Scenario 3\n\nIn this scenario, Service A has it's own public/private key and uses it to sign the JWTs. Ideally, the JWT expiration should be measured in seconds and have the `aud` and `sub` claims set accordingly. Service B then uses service A's public key to verify the JWT.\n\nAt a minimum we also have to use a one-way TLS to protect the JWT.\n\nThe key rotation would include rotating the keys as Service A, but also updating the Service B with the public key (so it can verify tokens from A are signed with the correct key).\n\n1. Run both services from two separate terminals:\n\n```shell\n# Terminal 1\nnpm run servicea\n\n# Terminal 2\nnpm run serviceb\n```\n\n2. Create the public/private key for TLS (run from the `/app` folder):\n\n```shell\nmkcert -key-file key.pem -cert-file cert.pem localhost\n```\n\n3. Create the public/private key for signing JWT tokens (run from the `/app` folder):\n\n```shell\ncurl -X POST localhost:3000/gen-keys\n```\n\n\u003eThe above request will create a public and private key in the `/scenario-3-keys` folder. This is where both instances of the app will read the keys from (service A will read the private key to sign the JWTs, and service B will read the public key to verify the signature).\n\n\nTo run the scenario, you can send the request to `localhost:3000/scenario3`. Here's what happens:\n\nFrom Service A (client):\n\n1. Service A will load the private key from the `scenario-3-keys` folder.\n2. Service A creates and signs a JWT token (`sub: servicea`, `aud: serviceb`) with the private key.\n3. Service A sends a GET request with the JWT token in the headers to the `https://` endpoint (e.g. `https://localhost:8443/scenario3-server`).\n\nFrom Service B (server):\n\n1. Service B reads the token from the Authorization header.\n2. Service B reads the public key from the `scenario-3-keys` folder.\n3. Service B uses the JWT library to verify the JWT token was signed with the correct key, and that it includes the correct `sub` and `aud` claims.\n4. Service B responds with the JWT verification result.\n\nThe output from the call will look like this:\n\n```console\ncurl localhost:3000/scenario3\n{\"valid\":true,\"claims\":{\"iss\":\"https://service-issuer\",\"sub\":\"servicea\",\"aud\":\"serviceb\",\"exp\":1696879104,\"iat\":1696875504}}\n```\n\nYou can also check the logs from both services to see what was happening.\n\nTo test the scenario with an expired token, send the request like this: `curl localhost:3000/scenario3?expired=1` - this will issue an expired token, which will fail the request:\n\n```console\n{}\"valid\":false,\"error\":{\"name\":\"TokenExpiredError\",\"message\":\"jwt expired\",\"expiredAt\":\"2023-10-09T17:38:41.000Z\"},\"claims\":{}}\n```\n\n## Scenario 4\n\nThis scenario is similar to the previous one, but in this case Service A is signing the JWTs for two services - service B and service C. The flow is exactly the same, each receiving service has access to the Service A's public key to verify the issued JWTs.\n\n\u003eFor the sake of simplicity Service B and Service C are using the same TLS certificates (using the `localhost`), in reality you'd have separate TLS certs for each of the services.\n\n# Istio scenarios\n\nWhat follows are the scenarios using Istio service mesh and Gloo Gateway. Both scenarios show how to configure user authentication (using JWTs) and authorization policies that combine both user and service auth. In lieu of using JWTs for service-to-service communication, we're certificate and mutual TLS, which is enabled by default when you install Istio.\n\nThe following are the prerequisites for the scenarios:\n\n- Kubernetes cluster with Istio installed\n- `default` namespace labeled for Istio injection\n- Auth0 account with configured application and API\n\nNote that we're using `webuitest.com` as a domain name for the web UI. Make sure you update your `/etc/hosts` file to point the domain to the external IP of the ingress gateway.\n\nCreate the config map and secret with the Auth0 configuration:\n\n```shell\n# All these come from Auth0\nexport AUTH0_CLIENT_ID=\u003cfrom Auth0\u003e\nexport AUTH0_SECRET=\u003cfrom Auth0\u003e\nexport ISSUER_BASE_URL=\u003cfrom Auth0\u003e\nexport AUDIENCE=https://webui\n\n# Ingress GW external IP\nexport BASE_URL=http://webuitest.com\n```\n\n```shell\n# Create the secrets in a kubernetes Secret\nkubectl create secret generic webui-auth0 \\\n    --from-literal=AUTH0_CLIENT_ID=$AUTH0_CLIENT_ID \\\n    --from-literal=AUTH0_SECRET=$AUTH0_SECRET \\\n    --from-literal=RANDOM_SECRET=$(openssl rand -base64 32)\n\n# Create other values in a ConfigMap\nkubectl create configmap webui-config \\\n    --from-literal=ISSUER_BASE_URL=$ISSUER_BASE_URL \\\n    --from-literal=AUDIENCE=$AUDIENCE \\\n    --from-literal=BASE_URL=$BASE_URL\n```\n\nWe can now deploy the `web-ui` application with `kubectl apply -f web-ui/deploy.yaml` and deploy the `backend` as well with `kubectl apply -f backend/deploy.yaml`.\n\nNext, we can deploy the VirtualServices and the Gateway resource to expose the `webui` through the ingress gateway:\n\n```shell\nkubectl apply -f web-ui/istio.yaml\nkubectl apply -f backend/istio.yaml\n```\n\nAt this point we could navigate to `http://webuitest.com`, see the UI and results from both backend service and login to the UI.\n\nLet's continue with an allow nothing policy, that denies all requests between services:\n\n```yaml\napiVersion: security.istio.io/v1\nkind: AuthorizationPolicy\nmetadata:\n  name: allow-nothing\n  namespace: default\nspec:\n  {}\n```\n\nIf we reload the web page, we'll get the `RBAC: access denied` error. With the allow nothing policy, we'll get an access denied right away - we can't even see the UI, let alone log in.\n\nSo let's create an AuthorizationPolicy that allows calls from the Istio ingress gateway to the `webui`:\n\n```yaml\napiVersion: security.istio.io/v1beta1\nkind: AuthorizationPolicy\nmetadata:\n  name: allow-ingress-gateway\n  namespace: default\nspec:\n  selector:\n    matchLabels:\n      app: webui\n  action: ALLOW\n  rules:\n  - from:\n    - source:\n        principals: [\"cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account\"]\n```\n\nThis time, we can get to the UI, but calls to both backend services fail with 403 errors. This is still because of the allow nothing policy. Let's create another AuthorizationPolicy that allows calls from the `webui` to the `backend-api`:\n\n```yaml\napiVersion: security.istio.io/v1beta1\nkind: AuthorizationPolicy\nmetadata:\n  name: allow-webui-to-backend\n  namespace: default\nspec:\n  selector:\n    matchLabels:\n      app: backend-api\n  action: ALLOW\n  rules:\n    - from:\n      - source:\n          principals: [\"cluster.local/ns/default/sa/webui\"]\n```\n\nThis time, we can get the response back from the `backend-api` service.\n\nSo far, we've only configured authorization policy that allows or denies access based on which service is calling.\n\nSince we've implemented user authentication, let's see how we can add an authorization policy that allows access only to logged-in users.\n\nLet's start by creating an RequestAuthentication policy that requires a JWT token to be present in the request from the `web-ui` to the `backend`:\n\n```yaml\napiVersion: security.istio.io/v1beta1\nkind: RequestAuthentication\nmetadata:\n name: backend-api\nspec:\n  selector:\n    matchLabels:\n      app: backend-api\n  jwtRules:\n  - issuer: \"https://dev-ccdkn3an01hhlwu5.us.auth0.com/\"\n    jwksUri: \"https://dev-ccdkn3an01hhlwu5.us.auth0.com/.well-known/jwks.json\"\n```\n\nWith the above resource we're saying that we require all requests to the `backend-api` to have a JWT token that was issued and signed by the Auth0 tenant. The `jwksUri` is the public key that can be used to verify the JWT signature.\n\nIf we reload the page everything will still work and the reason for that is because we have to create an AuthorizationPolicy that makes use of the principal from the RequestAuthentication. We'll combine this with the allow-webui-to-backend policy we created earlier:\n\n```yaml\napiVersion: security.istio.io/v1beta1\nkind: AuthorizationPolicy\nmetadata:\n  name: allow-webui-to-backend\n  namespace: default\nspec:\n  selector:\n    matchLabels:\n      app: backend-api\n  action: ALLOW\n  rules:\n    - from:\n      - source:\n          principals: [\"cluster.local/ns/default/sa/webui\"]\n        source:\n          requestPrincipals: [\"*\"]\n```\n\nThe above AuthorizationPolicy applies to the `backend-api` and is saying that we're allowing (`action: ALLOW`) requests that have a valid principal set (i.e. the one that is issued and signed based on the rules we specified in the RequestAuthentication resource) AND the requests have to come from the `webui` service.\n\nThis time, it's not going to work - we'll get the same error in the UI as we did before. Additionally, if we check the logs from the sidecar proxy next to the `backend-api` pod, we'll see the 403 errors:\n\n```console\n\"GET /data HTTP/1.1\" 403 - rbac_access_denied_matched_policy[none] - \"-\" 0 19 0 - \"-\" \"undici\" \"a0553520-baf2-9207-820a-ab81246ea1d4\" \"backend-api\" \"-\" inbound|3000|| - 10.244.1.5:3000 10.244.1.7:49130 outbound_.80_._.backend-api.default.svc.cluster.local -\n```\n\nIf we login to the UI, we'll see that the calls to the `backend-api` are now successful. This is because the UI is now sending the JWT token in the request to the `backend-api` and the token is valid.\n\nAnother test we can do is to try and send a request directly from the `webui` pod to the `backend-api` pod; we'll get the same RBAC error as before because we didn't provide a valid JWT token.  Similarly, if we'd use a valid JWT token, but signed by a different issuer, we'd get the following error:\n\n```console\n\u003c HTTP/1.1 401 Unauthorized\n\u003c www-authenticate: Bearer realm=\"http://backend-api/data\", error=\"invalid_token\"\n```\n\n## Using Gloo Gateway\n\nIn this scenario, we'll use [Gloo Gateway](https://www.solo.io/products/gloo-gateway/) to expose the `webui` service and implement external auth flow. Previously, we've implemented the auth flow on the `webui` and the ingress gateway was just passing the request through. This time, we'll configure the gateway to redirect the user to Auth0 to login, and then back to the `webui` once authenticated. This way, the `webui` doesn't have to implement the auth flow at all as everything is done at the gateway level.\n\nYou can follow [these instructions](https://docs.solo.io/gloo-gateway/latest/getting_started/setup/) to install Gloo Gateway.\n\n```shell\nmeshctl install --profiles gloo-gateway-demo \\\n  --set common.cluster=mycluster \\\n  --set licensing.glooGatewayLicenseKey=\u003clicense-key-here\u003e\n```\n\nWe'll be using a DNS name `webuitest.com` so we have to create a self-signed certificate for the domain. Make sure you also update your `/etc/hosts` file to point the domain to the external IP of the ingress gateway:\n\n```shell\n./selfsigned-cert.sh\n```\n\nLet's bring the certificate and key to the cluster by creating a Kubernetes secret:\n\n```shell\nkubectl create secret generic gw-ssl-1-secret \\\n--from-file=tls.key=${SERVER_CERT_NAME}.key \\\n--from-file=tls.crt=${SERVER_CERT_NAME}.crt \\\n--dry-run=client -oyaml | kubectl apply -f- \\\n--namespace gloo-mesh-gateways\n```\n\nWe can now label the default namespace for injection and deploy the `webui` and `backend-api` services:\n\n```shell\nkubectl apply -f web-ui/deploy-gloo.yaml\nkubectl apply -f backend/deploy.yaml\n```\n\n\u003e Note the deployments are the same, the `-gloo.yaml` just doesn't include the references to Auth0 ConfigMap and Secret.\n\nTo expose the `webui` service through the gateway, we'll create a VirtualService and a RouteTable:\n\n```yaml\napiVersion: networking.gloo.solo.io/v2\nkind: VirtualGateway\nmetadata:\n  name: istio-ingressgateway\nspec:\n  listeners: \n  - allowedRouteTables:\n    - host: webuitest.com\n    http: {}\n    port:\n      number: 443\n    tls:\n      mode: SIMPLE\n      secretName: gw-ssl-1-secret\n  - allowedRouteTables:\n    - host: webuitest.com\n    http: {}\n    httpsRedirect: true\n    port:\n      number: 80\n  workloads:\n  - selector:\n      labels:\n        istio: ingressgateway\n      cluster: mycluster\n---\napiVersion: networking.gloo.solo.io/v2\nkind: RouteTable\nmetadata:\n  name: www-webuitest-com\nspec:\n  hosts:\n    - webuitest.com\n  # Selects the virtual gateway you previously created\n  virtualGateways:\n    - name: istio-ingressgateway\n      namespace: default\n  http:\n    - name: webui\n      matchers:\n      - uri:\n          prefix: /\n          ignoreCase: true\n      labels:\n        # We'll refer to this label later when creating the auth configuration\n        oauth: \"true\"\n      forwardTo:\n        destinations:\n          - ref:\n              name: webui\n              namespace: default\n            port:\n              number: 80\n```\n\n\u003eSetting a label on the route allows us to apply auth policies to specific routes.\n\nWe'll be using the external auth server that's provided by the Gloo Gateway. To do that, we create an ExtAuthServer resource that points to that auth service instance:\n\n```yaml\napiVersion: admin.gloo.solo.io/v2\nkind: ExtAuthServer\nmetadata:\n  name: webui\n  namespace: default\nspec:\n  destinationServer:\n    port:\n      number: 8083\n    ref:\n      cluster: mycluster\n      name: ext-auth-service\n      namespace: gloo-mesh-addons\n```\n\nWhat follows is the ExtAuthPolicy configuration - this is the same configuration we've done before, but at the `webui` application level (remember setting those environment variables).\n\nWe'll start by defining the `CLIENT_SECRET` and creating a secret with it:\n\n```shell\nexport CLIENT_SECRET=\u003cCLIENT_SECRET_HERE\u003e\n\nkubectl apply -f - \u003c\u003cEOF\napiVersion: v1\nkind: Secret\ntype: extauth.solo.io/oauth\nmetadata:\n  name: auth0-client-secret\n  namespace: default\ndata:\n  client-secret: $(echo -n ${CLIENT_SECRET} | base64)\nEOF\n```\n\nNow we can create the ExtAuthPolicy resource and apply it to the route labeled with `oauth: true`:\n\n```yaml\napiVersion: security.policy.gloo.solo.io/v2 \nkind: ExtAuthPolicy\nmetadata: \n  name: webui-auth0\n  namespace: default\nspec: \n  applyToRoutes:\n    - route:\n        labels:\n          oauth: \"true\"\n  config: \n    glooAuth: \n      configs: \n      - oauth2: \n          oidcAuthorizationCode: \n            appUrl: https://webuitest.com\n            callbackPath: /callback\n            clientId: \u003cINSERT AUTH0 CLIENT ID\u003e\n            # Reference to the secret we created earlier\n            clientSecretRef: \n              name: auth0-client-secret\n              namespace: default\n            issuerUrl: \u003cINSERT AUTH0 ISSUER URL\u003e\n            scopes:\n            - openid\n            - profile\n            logoutPath: /logout\n            # We're telling Gloo Gateway to put the id token into a header called `jwt`\n            # and to put the access token into a header called `my_access_token`\n            headers:\n              idTokenHeader: \"jwt\"\n              accessTokenHeader: \"my_access_token\"\n            # We can also tell Gloo Gateway to extract specific claims from the JWT\n            # and store them in headers.\n            identityToken:\n              claimsToHeaders:\n                - claim: name\n                  header: x-name\n                - claim: nickname\n                  header: x-nickname\n                - claim: picture\n                  header: x-picture\n    # Lastly, we're referencing the auth server we created earlier\n    server:\n      name: webui\n```\n\nIf you navigate to the URL (`https://webuitest.com`) you should be redirected to Auth0 to login, and after that back to the `webui`. The biggest difference in this scenario compared to the previous one is that Gloo Gateway is managing the user auth with Auth0. We don't have to implement the auth flow in the `webui` app at all.\n\nWe could now repeat the same steps we did previously, starting with enforcing the `allow-nothing` policy:\n\n```yaml\napiVersion: security.istio.io/v1\nkind: AuthorizationPolicy\nmetadata:\n  name: allow-nothing\n  namespace: default\nspec:\n  {}\n```\n\nNext, we can then create policies that explicitly allow calls from the ingress gateway ➡ `webui` and `webui` ➡ `backend-api`:\n\n```yaml\napiVersion: security.istio.io/v1beta1\nkind: AuthorizationPolicy\nmetadata:\n  name: allow-ingress-gateway\n  namespace: default\nspec:\n  selector:\n    matchLabels:\n      app: webui\n  action: ALLOW\n  rules:\n  - from:\n    - source:\n        # Note this is different than before, because we're using Gloo Gateway\n        principals: [\"cluster.local/ns/gloo-mesh-gateways/sa/istio-ingressgateway-1-18-3-service-account\"]\n---\napiVersion: security.istio.io/v1beta1\nkind: AuthorizationPolicy\nmetadata:\n  name: allow-webui-to-backend\n  namespace: default\nspec:\n  selector:\n    matchLabels:\n      app: backend-api\n  action: ALLOW\n  rules:\n    - from:\n      - source:\n          principals: [\"cluster.local/ns/default/sa/webui\"]\n        source:\n          requestPrincipals: [\"*\"]\n---\napiVersion: security.istio.io/v1beta1\nkind: RequestAuthentication\nmetadata:\n name: backend-api\nspec:\n  selector:\n    matchLabels:\n      app: backend-api\n  jwtRules:\n  - issuer: \"https://dev-ccdkn3an01hhlwu5.us.auth0.com/\"\n    jwksUri: \"https://dev-ccdkn3an01hhlwu5.us.auth0.com/.well-known/jwks.json\"\n```\n\nWe are also applying the request authentication policy; that way, even if the malicious agent gets inside the cluster, they won't be able to make calls to the `backend-api` without a valid JWT token.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeterj%2Fjwts-for-services","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpeterj%2Fjwts-for-services","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeterj%2Fjwts-for-services/lists"}