With a PortalGroup, you can set up membership information of specific claims that must be present in the user’s JWT from the OIDC provider before the user gets access to an ApiProduct.

About OIDC

A PortalGroup specifies the claims that must be present in a JWT ID token to allow a user to access specific private APIs and usage plans, and to perform actions, such as to create API keys for these APIs. Therefore, PortalGroups create an additional layer of security for your portal alongside external OIDC authentication.

To configure authorization for private APIs, you must set up authentication with an OIDC provider first. After successful authentication, the OIDC provider returns an ID token and refresh token. The ID token contains the claims that are used to match the user to a PortalGroup. After a matching PortalGroup is found, access to the private APIs and usage plans is granted.

The following image illustrates the authorization flow when trying to create an API key for a private API. Note that this flow assumes that the user is successfully authenticated with an OIDC provider and received an ID token.

Figure: Authorization for private API products with PortalGroups
Figure: Authorization for private API products with PortalGroups
Figure: Authorization for private API products with PortalGroups
Figure: Authorization for private API products with PortalGroups

Set up authorization with PortalGroups

To control access to private API products in your developer portal, you set up PortalGroups and specify the conditions that must be met to successfully authorize a user to see the private APIs.

  1. Set up an OIDC provider for the developer portal. For example, see Steps 1 and 2 in the Keycloak guide. This step is required to receive the ID token from your OIDC provider and identify the claims that you want to use for your PortalGroup.

  2. Identify the claims in the ID token that you want to match on. For example steps to create a custom claim, see Step 3 in the Keycloak guide. Only if a user presents a token with the right claims, the user is matched with the PortalGroup and is granted access to the private APIs and usage plans that you define in the PortalGroup. The ID token is returned by the OIDC provider during authentication. Depending on the OIDC provider that you use, you can customize the claims that are being returned in the ID token. However, some OIDC providers might not allow you to create custom claims.

    Your ID token might have a claim structure that is similar to the following:

      {
      "exp": 1681133278,
      "iat": 1681133218,
      "auth_time": 1681133218,
      "jti": "3e410301-d9a2-463f-a127-df78a94f87db",
      "iss": "http://34.xxx.xxx.xxx:8080/auth/realms/master",
      "aud": "4464fbac-ab29-4c6a-945b-14e4685c0ad4",
      "sub": "83a467a1-8f4e-4dd6-8fd1-34832ba0a84e",
      "typ": "ID",
      "azp": "4464fbac-ab29-4c6a-945b-14e4685c0ad4",
      "session_state": "aac7790f-036c-4367-b728-1f1d9dc355fe",
      "at_hash": "2nFLjyWQA2pEANc8zuhwTw",
      "acr": "1",
      "email_verified": false,
      "preferred_username": "user1",
      "email": "user1@example.com",
      "X-groups": "users",
    }
      
  3. Create a PortalGroup to allow access to the petstore API product for a user that presents an ID token with the X-groups: users claim.

      kubectl apply -f- <<EOF
    apiVersion: portal.gloo.solo.io/v1
    kind: PortalGroup
    metadata:
      name: petstore-group
      namespace: gloo-system
    spec:
      membership:
      - claimGroup:
        - key: X-groups
          value: users
      apiProducts:
      - name: 'tracks-svc-api-product'
        namespace: gloo-system
    EOF
      
    SettingDescription
    membershipThe claims that determine whether a user belongs to the PortalGroup. Users must have a JWT ID token with claims that match all the claims in the membership list.
    claimGroupSet the claims in the JWT ID token that must be present to successfully include the user in this PortalGroup. Note that the claims that you specify must exist in the ID token that your OIDC provider returns. You have the option to specify one or multiple claim sets. In this example, the ID token must include the X-group: users claim.
    apiProductsSelect the ApiProducts that the user can view in the developer portal after the user is matched with this PortalGroup. In this example, the PortalGroup gives members access to only the Tracks ApiProduct in the gloo-system namespace.
  4. Verify that the claim is set for the selected ApiProducts in the PortalConfig. If not, you might need to update the ApiProduct selector.

      kubectl get portalconfigs -A -o yaml | grep claimGroups -A3
      

    Example output:

      claimGroups:
    - claims:
      - key: X-groups
        value: users
      
  5. Send a request to the /v1/api-products endpoint to list the ApiProducts that you have access to. Note that in your CLI output, only the Tracks API is listed. The private Pet Store API is hidden because no ID token with the right claims is presented in the curl request.

    Example output: Notice that only the Pet Store ApiProduct is returned.

      {
      "apiKeyEnabled": false,
      "apiProductMetadata": {
        "imageURL": "https://raw.githubusercontent.com/solo-io/gloo/8d9df95602c506067d49db9b1447b4003b19a070/docs/content/img/portal/petstore-food-bowl-collar.png"
      },
      "id": "22faf351-1e1b-47a6-9b7c-865d056e2960",
      "name": "Pet Store",
      "oauthEnabled": false,
      "versionsCount": 1
    }
      
  6. Get an ID token for your user from your OIDC provider.

    Keycloak example:

      export USER1_TOKEN=$(curl -Ssm 10 --fail-with-body \
      -d "client_id=${KEYCLOAK_CLIENT}" \
      -d "client_secret=${KEYCLOAK_SECRET}" \
      -d "username=user1" \
      -d "password=password" \
      -d "grant_type=password" \
      "$KEYCLOAK_URL/realms/master/protocol/openid-connect/token" |
      jq -r .access_token)
      
  7. Send another request to the /v1/api-products endpoint. This time, you provide the ID token that you retrieved in the previous step in the Cookie request header. In your CLI output, verify that you also see the Tracks ApiProduct, because now, you are successfully authenticated and authorized to see this private API.

    Example output:

      [
      {
        "apiKeyEnabled": false,
        "apiProductMetadata": {
          "imageURL": "https://raw.githubusercontent.com/solo-io/gloo-mesh-use-cases/fbf090f7f6e9dd14c1b14d7c274b8611c9b0443e/gloo-gateway/portal/astronauts-image.jpeg"
        },
        "id": "5f659216-af3a-4368-90b5-41144f733885",
        "name": "Catstronauts Course Tracks",
        "oauthEnabled": false,
        "versionsCount": 1
      },
      {
        "apiKeyEnabled": false,
        "apiProductMetadata": {
          "imageURL": "https://raw.githubusercontent.com/solo-io/gloo/8d9df95602c506067d49db9b1447b4003b19a070/docs/content/img/portal/petstore-food-bowl-collar.png"
        },
        "id": "22faf351-1e1b-47a6-9b7c-865d056e2960",
        "name": "Pet Store",
        "oauthEnabled": false,
        "versionsCount": 1
      }
    ]