Access token OAuth with Hydra

Let users authenticate with your app by using an access token from ORY Hydra.

This feature is available with a Gloo Mesh Gateway license only.

You might control access to your APIs with access tokens. Users can authenticate and authorize access through an OpenID Connect (OIDC) compliant authentication system, such as ORY Hydra. The authentication system issues an access token. Users add the access token in their requests to your app.

Gloo Mesh accepts the request and validates the token by using the authentication system's introspection endpoint. If the access token is authorized to perform the requested action, the request is returned successfully. Otherwise, the request is denied.

Before you begin

  1. Complete the demo setup to install Gloo Mesh, Istio, and Bookinfo in your cluster.

  2. Create the Gloo Mesh resources for this policy in the management and workload clusters.

    The following files are examples only for testing purposes. Your actual setup might vary. You can use the files as a reference for creating your own tests.

    1. Download the following Gloo Mesh resources:

    2. Apply the files to your management cluster.

      kubectl apply -f kubernetes-cluster_gloo-mesh_cluster-1.yaml --context ${MGMT_CONTEXT}
      kubectl apply -f kubernetes-cluster_gloo-mesh_cluster-2.yaml --context ${MGMT_CONTEXT}
      kubectl apply -f workspace_gloo-mesh_anything.yaml --context ${MGMT_CONTEXT}
      
    1. Download the following Gloo Mesh resources:

    2. Apply the files to your workload cluster.

      kubectl apply -f virtual-gateway_bookinfo_north-south-gw.yaml --context ${REMOTE_CONTEXT1}
      kubectl apply -f workspace-settings_bookinfo_anything.yaml --context ${REMOTE_CONTEXT1}
      
  3. Send a request to verify that you can reach the ratings app without authorization. By default Gloo Mesh allows any request on routes that do not specify authentication.

    curl -v ${INGRESS_GW_IP}/ratings/1 -H "Host: www.example.com"
    

    Example output:

    {"id":1,"ratings":{"Reviewer1":5,"Reviewer2":4}}
    

Install Hydra

To set up authentication, you need an OpenID Connect (OIDC) provider to use with Gloo Mesh. For demonstration purposes, you can deploy ORY Hydra in your demo cluster.

  1. Add the Ory Helm repository to your cluster.

    helm --kube-context ${REMOTE_CONTEXT1} repo add ory https://k8s.ory.sh/helm/charts
    helm --kube-context ${REMOTE_CONTEXT1} repo update
    
  2. Deploy the Hydra Helm chart.

    This example uses an in-memory database and disables SSL for demonstration purposes. Do not use this setup for production.

    helm install --kube-context ${REMOTE_CONTEXT1} \
        --set 'hydra.config.secrets.system={$(LC_ALL=C tr -dc 'A-Za-z0-9' < /dev/urandom | base64 | head -c 32)}' \
        --set 'hydra.config.dsn=memory' \
        --set 'hydra.config.urls.self.issuer=http://public.hydra.localhost/' \
        --set 'hydra.config.urls.login=http://example-idp.localhost/login' \
        --set 'hydra.config.urls.consent=http://example-idp.localhost/consent' \
        --set 'hydra.config.urls.logout=http://example-idp.localhost/logout' \
        --set 'ingress.public.enabled=true' \
        --set 'ingress.admin.enabled=true' \
        --set 'hydra.dangerousForceHttp=true' \
        --set 'image.tag=latest-sqlite' \
        hydra-token-example \
        ory/hydra
    
  3. Verify that the Hydra pods are running. In the service ports, note that admin endpoint runs on port 4445 and the public endpoint runs on port 4444. You use the admin endpoint to create a client ID and password and to validate the token. You use the public endpoint to generate an access token.

    kubectl get all -l app.kubernetes.io/name=hydra -n default --context ${REMOTE_CONTEXT1} 
    

    Example output:

    NAME                                      READY   STATUS    RESTARTS   AGE
    pod/hydra-token-example-cdcf57f87-zpq9q   1/1     Running   0          111s
    
    NAME                                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
    service/hydra-token-example-admin    ClusterIP   10.108.3.142    <none>        4445/TCP   111s
    service/hydra-token-example-public   ClusterIP   10.108.12.219   <none>        4444/TCP   111s
    
    NAME                                  READY   UP-TO-DATE   AVAILABLE   AGE
    deployment.apps/hydra-token-example   1/1     1            1           111s
    
    NAME                                            DESIRED   CURRENT   READY   AGE
    replicaset.apps/hydra-token-example-cdcf57f87   1         1         1       111s
    

Create the OIDC credentials

Now that you have an OIDC provider running, you can create the credentials.

  1. Enable port-forwarding for the administrative endpoint on port 4445. You can store the port-forward process ID as an environment variable so that you can end the process later.

    kubectl --context ${REMOTE_CONTEXT1} port-forward deploy/hydra-token-example 4445 &
    portForwardPid1=$!
    
  2. In a new tab in your terminal, send a request to the admin endpoint to create a client ID and secret.

    curl -X POST http://127.0.0.1:4445/clients \
      -H 'Content-Type: application/json' \
      -H 'Accept: application/json' \
      -d '{"client_id": "my-client", "client_secret": "secret", "grant_types": ["client_credentials"]}'
    

    Example output:

    {"client_id":"my-client","client_name":"","client_secret":"secret","redirect_uris":null,"grant_types":["client_credentials"],"response_types":null,"scope":"offline_access offline openid","audience":[],"owner":"","policy_uri":"","allowed_cors_origins":[],"tos_uri":"","client_uri":"","logo_uri":"","contacts":null,"client_secret_expires_at":0,"subject_type":"public","jwks":{},"token_endpoint_auth_method":"client_secret_basic","userinfo_signed_response_alg":"none","created_at":"2022-05-24T21:36:11Z","updated_at":"2022-05-24T21:36:10.78725Z","metadata":{}}
    
  3. Enable port-forwarding for the public endpoint on port 4444. You can store the port-forward process ID as an environment variable so that you can end the process later.

    kubectl --context ${REMOTE_CONTEXT1} port-forward deploy/hydra-token-example 4444 &
    portForwardPid2=$!
    
  4. In a new tab in your terminal, send a request to the public endpoint to create an access token. The access token uses the client ID and secret that you created in the previous step.

    curl -X POST http://127.0.0.1:4444/oauth2/token \
      -H 'Content-Type: application/x-www-form-urlencoded' \
      -H 'Accept: application/json' \
      -u my-client:secret \
      -d 'grant_type=client_credentials' | jq .access_token -r
    
  5. Set the access token as an environment variable.

    #SET VARIABLE
    export HYDRA_ACCESS_TOKEN=<_EB6xI-hnCryL_D...>
    
  6. Check that the access token works by using the introspection path of the admin endpoint. The introspection path is the same path that Gloo Mesh uses to check the validity of access tokens.

    curl -X POST http://127.0.0.1:4445/oauth2/introspect \
      -H 'Content-Type: application/x-www-form-urlencoded' \
      -H 'Accept: application/json' \
      -d "token=$HYDRA_ACCESS_TOKEN" | jq
    

    Example output:

    {
      "active": true,
      "client_id": "my-client",
      "sub": "my-client",
      "exp": 1653432108,
      "iat": 1653428508,
      "nbf": 1653428508,
      "aud": [],
      "iss": "http://public.hydra.localhost/",
      "token_type": "Bearer",
      "token_use": "access_token"
    }
    

Configure an OAuth policy

  1. Create an external auth policy that points to the introspection URL for access token validation.

    kubectl --context ${REMOTE_CONTEXT1} apply -f -<<EOF
    apiVersion: security.policy.gloo.solo.io/v2
    kind: ExtAuthPolicy
    metadata:
      name: ratings-hydra
      namespace: bookinfo
    spec:
      applyToRoutes:
      - route:
          labels:
            route: ratings
      config:
        glooAuth:
          configs:
          - oauth2:
              accessTokenValidation:
                introspectionUrl: http://hydra-token-example-admin.default:4445/oauth2/introspect
    EOF
    

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

    Setting Description
    applyToRoutes Configure which routes to apply the policy to, by using labels. The label matches the app and the route from the route table. If omitted, the policy applies to all routes in the workspace.
    accessTokenValidation Configure the details to validate the access token. If the token is missing or invalid, Gloo Mesh denies the request. In this example, the introspection URL is set to the OIDC provider value, the Hydra admin port. You can use the internal hostname of the Hydra admin service, because the request comes from Gloo Mesh's external auth pod that has access to Kubernetes DNS.
  2. Send a request to the ratings app without providing an access token. Because you enabled OAuth, the request fails with a 403 Forbidden HTTP error code.

    curl -v ${INGRESS_GW_IP}/ratings/1 -H "Host: www.example.com" 
    
  3. Send the request again, this time with an invalid token value. Again, the request fails with a 403 Forbidden error code.

    curl -v ${INGRESS_GW_IP}/ratings/1 -H "Host: www.example.com" -H "Authorization: Bearer qwertyuio23456789"
    
  4. Send the request once more, this time with the access token that you previously created. The request succeeds with a 200 HTTP response and the body of the Bookinfo homepage.

    curl -v ${INGRESS_GW_IP}/ratings/1 -H "Host: www.example.com" -H "Authorization: Bearer $HYDRA_ACCESS_TOKEN"
    

Good job, you used an access token to authenticate your request! As you plan your production scenario, keep in mind a few things about access tokens. Your app likely sends a request to get its own access token, instead of you providing one directly in the header of a request. Typically, you scope the access token to certain API actions. Also, access tokens expire. Check the default authorization and expiry settings for Hydra or your identity provider when you set up your apps to authenticate with access tokens.

Cleanup

You can clean up the resources that you created in this guide by running the following commands.

kill $portForwardPid1
kill $portForwardPid2
kill $portForwardPid3
helm uninstall hydra-token-example --kube-context ${REMOTE_CONTEXT1}
kubectl delete --context ${REMOTE_CONTEXT1} extauthpolicy -n bookinfo ratings-hydra