Hydra

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

This feature is available with a Gloo 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 Gateway 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.

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, and that your Kubernetes context is set to the cluster you store your Gloo config in (typically the management cluster). If you have different names, make sure to update the sample configuration files in this guide.

Follow the getting started instructions to:

  1. Set up Gloo Gateway in a single cluster.

  2. Deploy sample apps.

  3. Configure an HTTP listener on your gateway and set up basic routing for the sample apps.

  4. Make sure that the external auth service is installed and running. If not, install the external auth service in your single or multicluster environment.

    kubectl get pods -A -l app=ext-auth-service
    

Install Hydra

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

  1. Add the Ory Helm repository to your cluster.

    helm repo add ory https://k8s.ory.sh/helm/charts
    helm 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 \
        --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 
    

    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.

    kubectl port-forward deploy/hydra-token-example 4445
    
  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.

    kubectl port-forward deploy/hydra-token-example 4444
    
  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 Gateway 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

You can do the following steps in a different order, depending on when you want the policy to take effect. For example, you might want the policy to always take effect as soon as the route is created. To do so, you can create the policy before you add the route to the route table.
  1. Create an external auth server to use for your policy.

    kubectl apply -f - <<EOF
    apiVersion: admin.gloo.solo.io/v2
    kind: ExtAuthServer
    metadata:
      name: ext-auth-server
      namespace: bookinfo
    spec:
      destinationServer:
        port:
          number: 8083
        ref:
          cluster: $CLUSTER_NAME
          name: ext-auth-service
          namespace: gloo-mesh-addons
    EOF
    
  2. Create an external auth policy that points to the introspection URL for access token validation.

    kubectl 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:
        server:
          name: ext-auth-server
          namespace: bookinfo
          cluster: $CLUSTER_NAME
        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 Use labels to configure which routes to apply the policy to. This example label matches the app and route from the example route table that you apply separately. If omitted and you do not have another selector such as applyToDestinations, 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 Gateway 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 Gateway's external auth pod that has access to Kubernetes DNS.
  3. 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 -vik --resolve www.example.com:80:${INGRESS_GW_IP} http://www.example.com:80/ratings/1
    
    curl -vik --resolve www.example.com:443:${INGRESS_GW_IP} https://www.example.com:443/ratings/1
    

  4. Send the request again, this time with an invalid token value. Again, the request fails with a 403 Forbidden error code.

    curl -vik --resolve www.example.com:80:${INGRESS_GW_IP} http://www.example.com:80/ratings/1 -H "Authorization: Bearer qwertyuio23456789"
    
    curl -vik --resolve www.example.com:443:${INGRESS_GW_IP} https://www.example.com:443/ratings/1 -H "Authorization: Bearer qwertyuio23456789"
    

  5. 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 -vik --resolve www.example.com:80:${INGRESS_GW_IP} http://www.example.com:80/ratings/1 -H "Authorization: Bearer $HYDRA_ACCESS_TOKEN"
    
    curl -vik --resolve www.example.com:443:${INGRESS_GW_IP} https://www.example.com:443/ratings/1 -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 optionally remove the resources that you set up as part of this guide.
helm uninstall hydra-token-example 
kubectl delete extauthserver -n bookinfo ext-auth-server
kubectl delete extauthpolicy -n bookinfo ratings-hydra