Self-Service User Registration with Gloo Portal and Okta

Gloo Portal allows users to catalog, manage and securely publish running APIs to onboard developers both inside and outside your organization. And of course, all of this is delivered with a cloud-native architecture that fits hand-in-glove with your Kubernetes and Istio deployments.

Gloo Portal users often ask if there is a way to configure self-service registration for new portal users.  Imagine a use case like this:  We have APIs that we’d like to publish externally in a way that is freely accessible for new users.  But we don’t want anonymous users or bots pounding away at our servers.  We’d like to require users to register to use our service, and we’d like to keep the administrative burden to a minimum.  Is there some way we can make the registration process self-service?

The short answer to this question is YES.  This post will explore one approach to achieve this using the popular identity management platform Okta.  Okta supports the OpenID Connect (OIDC) standard and allows users to configure their application for self-service registration.  (Okta certainly isn’t the only IdP option for configuring self-registration with Gloo Edge, but it is a popular option among Solo customers.  It seems very likely that Keycloak among others could support this use case as well.)

In this post, we’ll walk step-by-step through the process of building out a Petstore Portal with public APIs that allow users to register themselves.  We’ll walk through the configuration of both the Portal itself and our Okta application, and then we’ll test it out in our Portal sandbox.

Prerequisites

You’ll need a Kubernetes cluster and associated tools, plus an instance of Gloo Edge Enterprise to complete this guide.

We use Google Kubernetes Engine (GKE) with Kubernetes v1.18.16 to test this guide, although any recent version with any Kubernetes provider should suffice.  If you prefer to work locally instead of a remote cluster, we’ve have good success with Kind.  If you go the Kind route, use these instructions for your installation of Gloo Edge.

We use Gloo Edge Enterprise v1.9.0 as our API gateway.  Use this guide if you need to install Gloo Edge Enterprise.  If you don’t already have access to the Enterprise bits of Gloo Edge, you can request a free trial here.

Gloo Portal is available as an element of the Gloo Edge Enterprise subscription.  But since not all customers use it, it is packaged as a separate installation from the core Edge API Gateway.  We are using Portal v1.1.1 for this exercise.  Follow this guide to install Portal with Gloo Edge as its deployment target.

Note that Gloo Portal also supports deployment to Istio ingress gateways in addition to Gloo Edge.  However, in this blog post we’ll focus strictly on deployment to Gloo Edge.

Establish the Initial Portal

We will begin by establishing the initial version of the Petstore Portal, which will allow anonymous access to its endpoints with no authentication required.

Deploy the Petstore App

First, let’s deploy the Petstore application to our Kubernetes cluster.

% kubectl apply -n default -f \
  https://raw.githubusercontent.com/solo-io/gloo/v1.9.0/example/petstore/petstore.yaml
% kubectl -n default rollout status deployment petstore
deployment "petstore" successfully rolled out

Configure the Portal Components

If you have not used Gloo Portal at all in the past, you might benefit from walking through these two detailed step-by-step guides to creating the Kubernetes custom resources that comprise your first basic Portal.

However, if you’ve built basic Gloo Portal configurations in the past, you can skip the two Getting Started guides and arrive at the same place using the command below.  It will build all of the Portal components from the guides above, including the following Kubernetes custom resources:

  • an APIDoc to wrap the OpenAPI interface of the Petstore application,
  • an APIProduct to apply policies to those endpoints and package them for distribution,
  • an Environment to declare which APIProducts will be deployed to which compute environments, and
  • a Portal to manage the specifics of how the generated web user interface will be presented to users.
kubectl apply -f \
  https://raw.githubusercontent.com/jameshbarton/solo-resources/main/portal-self-reg/0-apidoc-prod-envt-portal.yaml

Configure DNS Rules

If you used the single kubectl command above to create the Portal artifacts, then you likely do not have the necessary network configuration in place.  In order to visit the Portal being served at our domain petstore.example.com, we’ll need to make sure a DNS rule exists that will resolve that domain to that address.

There are many ways to set up DNS rules for the domains defined in our Environment and Portals. In production environments, you will probably want to use your Cloud Provider’s DNS solution.

For the purposes of this setup, modifying your local /etc/hosts file with an entry to manually resolve the Environment and Portal domains will be sufficient.

Let’s add entries for the api.example.com and petstore.example.com domains, substituting INGRESS_HOST with your IP address for these hostnames.  If you need support in determining the INGRESS_HOST for your environment, review this section in Part 1 of the Portal Getting Started Guide.

cat <<EOF | sudo tee -a /etc/hosts

# Added for the Solo.io Gloo Portal Guides
${INGRESS_HOST} api.example.com
${INGRESS_HOST} petstore.example.com
EOF

Confirm API Execution

If the Petstore app is running as expected, and all of the Portal elements are properly configured, then we should be able to exercise the currently unsecured API endpoints.

% curl http://api.example.com/api/pets
[{"id":1,"name":"Dog","status":"available"},{"id":2,"name":"Cat","status":"pending"}]

Configure Okta for Self-Registration

In this section, we will establish an Okta account and application, and then configure that application for self-registration.

Establish Okta Account

If you are an existing Okta customer, then you can skip over much of this section. But even if you are not an established Okta customer, it is easy to establish a free developer account that allows you to complete the integration exercise outlined in this guide. First, visit the developer signup page and walk through their process.  Okta then provisions a developer account that you can access via a URL like this: https://dev-2933640-admin.okta.com/ .  This provides access to an account dashboard and tools to manage Okta applications and users.

Establish Okta Application

You will need to establish an Okta Application to integrate with Gloo Edge. In this guide we prioritized providing a quick-start for testing over production readiness. In establishing the Okta application from the dashboard wizard, we defined this as a Web type application, set a Sign on method of OpenID Connect, and gave the application the name Petstore.
Beyond that, we changed only one of the default settings from the new-application wizard: Login redirect URIs, for which we provided a single value http://petstore.example.com/callback. For more details on creating Web type application integrations with Okta, we found this guide helpful.  Our final Okta Petstore application profile looked like this.

Configure an Okta User Group (and maybe some Users)

When you establish an Okta application, you get one user group for free, the Everyone group.  We could use that default group to drive this exercise.  But instead let’s make this a bit more realistic by creating a separate group to hold our set of users (including self-registered ones) that will have access to our “public” Petstore API.  Let’s establish a group called PetstoreUsers.  Navigate to Directory > Groups and establish the PetStoreUsers group.

You may also want to create some Okta Users and assign them to the PetstoreUsers Group, although that isn’t strictly required since soon we’ll be able to create Users dynamically from our web interface using self-service registration.

Configure a Groups Claim from the Petstore Application

Gloo Portal expects there to be a claim presented from the IdP as part of an ID token in the OIDC auth flow that identifies the Groups that a particular authorized user is part of.  More information on how this works in Okta is presented here.  Okta does not present this claim by default.

So we need to enable this by specifying a custom groups claim in two different places, the Okta Application configuration and the Authorization Server configuration.

First, let’s tackle the Application configuration.  Navigate to Applications > Applications in the admin console and click through to the Petstore application.  Switch to the Sign On tab and edit the Groups claim filter to specify the claim name is groups and that it should match the regex pattern .*.  In other words, all group names that are part of the application will be passed back to the Portal in the groups claim.

Second, we’ll configure the Authorization Server to pass back the groups claim with just the PetstoreUsers group name.  Navigate in the admin console to Security > API and click through to the default Authorization Server config.  Switch to the Claims tab and add a groups claim as shown below.

Note that we specified the name of the claim as groups, we changed the token type value from the default to ID Token, and we specified a Filter value to only include groups whose names start with the string Petstore, which will match our group named PetstoreUsers.

Enable Okta Self-Registration on Account

By default, Okta accounts are not enabled for self-service registration (SSR).  In this section, we will walk through the steps to configure this.  For more details, the Okta guide to managing self-registration settings is quite helpful.

First, we will navigate the Okta admin console to Directory > Self-Service Registration and enable the feature.

After enabling SSR, we need to customize the settings for our application.  We specify the Assign to group as PetstoreUsers, the user group we created earlier in this exercise, so that any users created by SSR will automatically be placed into the group that we will configure to have access to the Petstore APIs.

And just to save a bit of time for this exercise, we will also turn off the default setting that the user must verify their email address in order to be activated as a member of the group.  Okta will still send a confirmation email to the specified address, but it will not require any action from the newly registered user in response.

Enable Okta Self-Registration on Petstore Application

Not only is self-registration disabled by default on Okta accounts, it is also disabled by default on the Applications that comprise those accounts.  In this section, we will walk through the process of enabling and configuring SSR on the Okta Petstore Application.

First, use the Okta admin console to navigate back to the Petstore Application and switch to the Assignments tab.  You should notice that the SELF SERVICE panel is marked Disabled.  Edit those settings to allow users to request access to the application, and to mark administrator approval as Not Required.

Also on the Assignments tab, select Groups and assign the PetstoreUsers group that we created earlier to this application.  Your final Assignments tab interface should look something like below.

Integrate Petstore Portal with Okta

In this section, we’ll walk through the steps for configuring OIDC integration with Okta, which will allow us to delegate auth decisions to the IdP.

Establish Okta Client Secret

The OIDC standard specifies two bits of credentials that an OIDC client needs to present to its provider, a client ID and a client secret.  Okta provides this information on its admin console’s Application detail interface.

We will use the client secret from Okta to configure a corresponding Kubernetes Secret, which we will apply to our Portal config as part of the OIDC integration.

export CLIENT_SECRET=your-oidc-client-secret-here
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: petstore-portaloidc-secret
  namespace: default
data:
  client_secret: "$(echo $CLIENT_SECRET | base64)"
EOF

Configure Portal for Okta Auth Delegation

Our initial Portal had no authorization configured at all.  There is an internal HTTP basic auth mechanism that we could use, but that is typically used just for testing purposes.  The internal mechanism is missing self-registration and a host of other features found in enterprise IdP’s like Okta.

We will now add to our Portal configuration an oidcAuth stanza to connect with our Okta application. You’ll need to fill in the client ID shown in Okta and the correct host for your developer account in the issuer URL. Ensure that you include the groups value in the additionalScopes field so that the group claims are included in the request.

cat <<EOF | kubectl apply -f -
apiVersion: portal.gloo.solo.io/v1beta1
kind: Portal
metadata:
  name: petstore-portal
  namespace: default
spec:
  banner:
    fetchUrl: https://i.imgur.com/EXbBN1a.jpg
  customStyling: {}
  description: The Gloo Portal for the Petstore API
  displayName: Petstore Portal
  domains:
  - petstore.example.com
  - petstore.example.com:$INGRESS_PORT
  favicon:
    fetchUrl: https://i.imgur.com/QQwlQG3.png
  primaryLogo:
    fetchUrl: https://i.imgur.com/hjgPMNP.png
  publishedEnvironments:
  - name: dev
    namespace: default
  staticPages: []
  portalUrlPrefix: http://petstore.example.com:$INGRESS_PORT/
  oidcAuth:
    clientId: 0oaksnsdvwTd1b5wR5d6      # uses client ID from Okta application
    clientSecret:
      name: petstore-portaloidc-secret  # uses client secret from Okta application
      namespace: default
      key: client_secret
    groupClaimKey: groups  # reads the "groups" claim we configured to pass with Okta ID token
    # Issuer info available at your Okta's account .well-known OpenId config URL
    # Example: https://dev-2933640.okta.com/oauth2/default/.well-known/openid-configuration
    additionalScopes:
     - groups
    issuer: https://dev-2933640.okta.com/oauth2/default
EOF

Configure Portal Group to Shadow Okta Group

Finally, we will configure a Portal Group Custom Resource to shadow the PetstoreUsers group that we created to hold the users of our public API, including the self-registered ones.

cat <<EOF | kubectl apply -f -
apiVersion: portal.gloo.solo.io/v1beta1
kind: Group
metadata:
  name: oidc-group
  namespace: default
spec:
  accessLevel:
    apiProducts:
    - name: petstore-product  # identify APIProducts accessible to this group
      namespace: default
      environments:
      - name: dev  # identify Environments in which this APIProduct is accessible to this portal
        namespace: default
    portals:
    - name: petstore-portal   # identify Portals that will be accessible to this group
      namespace: default
  oidcGroup:
    groupName: PetstoreUsers  # identifies OIDC group that this group represents
EOF

Test Self-Registration with the Petstore Portal

Let’s see how self-registration works in practice by visiting our portal at http://petstore.example.com.

Click through the Login button and then Log in with OpenID Connect.  This redirects you to an Okta login dialog as shown below.  Note in particular the Sign up link at the bottom of the dialog, which is provided because we configured Okta for self-service registration.

Click through the Sign up link, provide the information requested, and click Register.

When everything is configured correctly, your self-registered user will be added to the PetstoreUsers group and you will be redirected back to the portal home.  But now you will notice that you are signed in to the portal with the email address of the newly registered user.

In addition to being added to the Portal, you should also receive a verification email from Okta.  Given the way that we configured SSR in Okta, this does not require confirmation by the new user.  There is a Verify Account button in the Okta-generated email, but portal use is not predicated on verification.  This message is simply a notification.  We could have configured Okta to require user confirmation but chose against it just to keep this process as simple as possible.

Play in the Portal Sandbox

Now that we are authenticated to the Petstore Portal, we can play in the portal’s execution sandbox.  Click through View APIs on the portal home page and click into the v1 sandbox.  You should see 4 operations listed there in a familiar Swagger-like style.  (The same type of interface is also presented for the newer gRPC service support as well.)

Click through the GET /api/pets operation and Try it out followed by Execute.  You should see a response from the endpoint identical to what we saw earlier in this exercise.

Congratulations!  You have now configured a customized Petstore API Portal that supports self-service registration of new users with Okta.

Learn More

In this blog post, we walked step-by-step through the process of building out a Petstore Portal that uses OIDC integration with Okta to allow users to register themselves.  We established all of the Portal elements, we configured an Okta application from scratch, and we tested it out in our Portal sandbox.

And we are just scratching the surface of features we could deploy to this portal.  For example, in a perfect world we might want our public API users to be rate-limited and require them to generate API Keys so that we can better track and control their usage.  Creating a Usage Plan is an easy way to add these capabilities.

For more information, check out the following resources.