Skip to content

OIDC and Identity Provider Setup

This guide covers configuring user authentication for the Riptides control plane UI and API using OpenID Connect (OIDC) identity providers.

Riptides uses OIDC to authenticate human users (platform engineers, security teams, operators) who access the control plane. The flow works as follows:

  1. A user navigates to the Riptides UI or runs a kubectl command that requires authentication.
  2. They are redirected to the configured identity provider (GitHub, Google, Bitbucket, or a static provider).
  3. After authenticating with the IdP, the user is redirected back with an OIDC token.
  4. Riptides validates the token and establishes a session.

You configure this with two Riptides custom resources:

  • IdentityProvider: Defines the external IdP (GitHub, Google, etc.) and any restrictions (e.g., required organizations).
  • OIDCClient: Defines an application that can initiate OIDC flows (the Riptides UI, kubectl, or your own applications).

  • A running Riptides control plane
  • kubectl access to the riptides-system namespace
  • OAuth app credentials from your identity provider (e.g., a GitHub OAuth App)

The most common setup uses GitHub as the identity provider, restricting access to members of specific organizations.

First, create a GitHub OAuth App:

  1. Go to your GitHub organization’s Settings > Developer settings > OAuth Apps > New OAuth App.
  2. Set the Authorization callback URL to https://<your-controlplane-url>/oidc/auth/callback.
  3. Note the Client ID and generate a Client Secret.

Then create the IdentityProvider resource:

apiVersion: core.riptides.io/v1alpha1
kind: IdentityProvider
metadata:
name: github
namespace: riptides-system
spec:
name: GitHub
github:
clientID: "<your-github-client-id>"
clientSecret: "<your-github-client-secret>"
requiredOrgs:
- name: "<your-github-organization>"

Field reference:

FieldDescription
spec.nameDisplay name shown on the login screen.
github.clientIDOAuth App client ID from GitHub.
github.clientSecretOAuth App client secret from GitHub.
github.requiredOrgsList of GitHub organizations. Only members of these organizations can authenticate.
Terminal window
riptides-cli ctl apply -f identityprovider-github.yaml

Verify the provider is ready:

Terminal window
riptides-cli ctl get identityprovider github -o jsonpath='{.status.state}'
# Available

If it shows Failed, check the reason:

Terminal window
riptides-cli ctl get identityprovider github -o jsonpath='{.status.message}'
apiVersion: core.riptides.io/v1alpha1
kind: IdentityProvider
metadata:
name: google
namespace: riptides-system
spec:
name: Google
google:
clientID: "<your-google-client-id>"
clientSecret: "<your-google-client-secret>"
requiredHostedDomains:
- "<your-domain.com>"

The requiredHostedDomains field restricts login to users with email addresses in the specified domain(s).

apiVersion: core.riptides.io/v1alpha1
kind: IdentityProvider
metadata:
name: bitbucket
namespace: riptides-system
spec:
name: Bitbucket
bitbucket:
clientID: "<your-bitbucket-client-id>"
clientSecret: "<your-bitbucket-client-secret>"

For development environments where you do not want to set up an external IdP, use the static provider with a single hardcoded user identity:

apiVersion: core.riptides.io/v1alpha1
kind: IdentityProvider
metadata:
name: static-dev
namespace: riptides-system
spec:
name: "Dev Login"
static:
email: "admin@example.com"
name: "Dev Admin"
preferredUsername: "admin"
groups:
- "platform"

Warning: The static provider is intended for development and testing only. Do not use it in production.


OIDCClients represent applications that initiate OIDC authentication flows. Riptides supports two types:

A native client is used for the built-in Riptides UI and kubectl access. It uses PKCE (Proof Key for Code Exchange) and does not require a client secret.

apiVersion: core.riptides.io/v1alpha1
kind: OIDCClient
metadata:
name: riptides-ui
namespace: riptides-system
spec:
native: {}
redirectUrls:
- "https://<your-controlplane-url>/ui/callback"
- "http://localhost:8000/auth/callback"

The redirectUrls list includes:

  • The production UI callback URL.
  • A localhost callback for kubectl authentication flows (the CLI opens a local HTTP server to receive the callback).
Terminal window
riptides-cli ctl apply -f oidcclient-native.yaml

For integrating external applications (dashboards, CI/CD systems, custom tooling) with the Riptides control plane API, create a custom OIDCClient:

apiVersion: core.riptides.io/v1alpha1
kind: OIDCClient
metadata:
name: my-dashboard
namespace: riptides-system
spec:
custom:
grantTypes:
- authorization_code
- refresh_token
responseTypes:
- code
authMethod: client_secret_post
redirectUrls:
- "https://dashboard.example.com/callback"

Field reference:

FieldDescription
custom.grantTypesOAuth 2.0 grant types the client can use.
custom.responseTypesOAuth 2.0 response types the client can use.
custom.authMethodHow the client authenticates to the token endpoint (client_secret_post, client_secret_basic).
redirectUrlsAllowed callback URLs after authentication.
Terminal window
riptides-cli ctl apply -f oidcclient-custom.yaml

After creation, Riptides generates a client ID and client secret for the custom client. Retrieve them:

Terminal window
riptides-cli ctl get oidcclient my-dashboard -o yaml

Use these credentials in your application’s OIDC configuration.


Once both resources are configured, the authentication flow works as follows:

  1. User visits the Riptides UI (or runs a kubectl command that requires auth).
  2. Riptides redirects to the IdP (e.g., GitHub’s OAuth authorization page).
  3. User authenticates with their IdP credentials (e.g., GitHub username/password + 2FA).
  4. IdP redirects back to the redirectUrl configured on the OIDCClient, with an authorization code.
  5. Riptides exchanges the code for an ID token and validates it against the IdentityProvider configuration (checking organization membership, domain, etc.).
  6. Session is established. The user can access the UI or issue authenticated API/kubectl commands.

For kubectl, the flow uses a local HTTP server:

Terminal window
# This triggers the OIDC flow — a browser window opens for authentication
riptides-cli ctl get workloadidentities

Step 3: Grant access with ClusterRoleBindings

Section titled “Step 3: Grant access with ClusterRoleBindings”

Configuring an IdentityProvider is not enough on its own. After authentication, Riptides checks Kubernetes RBAC to decide what the user can do. You grant access by binding the groups that your IdP returns to one of the predefined ClusterRoles:

ClusterRoleAccess
riptides:users:viewerRead-only access to all Riptides resources.
riptides:users:editorFull read/write access to all Riptides resources and Kubernetes Secrets in riptides-system.
riptides:cluster-adminUnrestricted access to all resources and verbs across the entire cluster. Use with caution.

When a user authenticates via the GitHub IdentityProvider, their GitHub team memberships are injected as OIDC groups claims in the form <org>:<team-slug>. For example, a member of the devs team in the abbazappa organization gets the group abbazappa:devs.

Important: Groups are populated from team memberships only. Being a member of an org without belonging to any team produces an empty groups claim — the user can log in but will have no RBAC access. Make sure the users you want to grant access are in at least one GitHub team.

Bind a team group to the editor role:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: riptides-github-devs-editor
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: riptides:users:editor
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: "abbazappa:devs"
Terminal window
riptides-cli ctl apply -f clusterrolebinding-devs.yaml

Any member of abbazappa/devs who logs in with GitHub will now have editor access. Adding or removing someone from the GitHub team takes effect on their next login — no Kubernetes changes required.

For read-only access (e.g. for an ops team):

subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: "abbazappa:ops"
roleRef:
kind: ClusterRole
name: riptides:users:viewer
apiGroup: rbac.authorization.k8s.io

For nested teams the group name includes the full parent chain: abbazappa:platform:devs.

The same pattern works for any IdentityProvider. The group names in the binding just need to match whatever groups claim the IdP returns:

ProviderGroups claim format
GitHub<org>:<team-slug> (e.g. abbazappa:devs), or <org>:<parent>:<child> for nested teams
Google / OIDCGroups returned in the IdP’s groups claim
Entra (Azure AD)Group object IDs or display names, depending on configuration

OIDC tokens are cached locally by the oidc-login plugin. If you already have a valid cached token, creating or updating a ClusterRoleBinding will not take effect until the token is refreshed. Force a fresh login by clearing the token cache:

Terminal window
kubectl oidc-login clean

Then run any kubectl command — the plugin will open a browser for re-authentication and the new token will include the updated groups.


You can configure multiple IdentityProviders simultaneously. The login screen will show all available providers, and users choose which one to authenticate with.

Terminal window
riptides-cli ctl get identityprovider -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.state}{"\n"}{end}'

  1. Check that each IdentityProvider reached Available state:

    Terminal window
    riptides-cli ctl get identityprovider -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.state}{"\n"}{end}'
  2. Check that the OIDCClient exists:

    Terminal window
    riptides-cli ctl get oidcclient
  3. Open the Riptides UI in a browser. You should see a login page with your configured identity provider(s). Click the provider name, authenticate, and confirm you are redirected back to the UI with an active session.

  4. Run any Riptides command and confirm it completes successfully:

    Terminal window
    riptides-cli ctl get workloadidentities