OIDC applications

The Maverics Identity Orchestrator can be used as an IDP to protect OIDC apps.

ℹ️
To define OIDC type apps the oidcProvider must be defined.

Configuration options

Name

name is a unique identifier for the app.

Type

type represents the application type. When defining OIDC apps, the type should be oidc.

Client ID

clientID is a unique ID used by client applications to identify themselves.

Client Secret

clientSecret is a secret used for authenticating client applications. The value of the secret can be stored in a secret provider.

Authentication Redirect URLs

redirectURLs are a list of the allowed URLs the response of an authentication request can be sent.

Logout Redirect URLs

logoutRedirectURLs are a list of the allowed URLs the response of a logout request can be sent.

Claims Mapping

claimsMapping provides a way to map attributes on a user’s session to standard claims on the ID token. The claims added to the ID token are determined by the value of the scope query parameter sent in the authorization request. The scopes supported, in addition to the required openid scope are email, profile, address and phone. The mapping of claim values to a scope is listed in OpenID Connect Core 1.0, section 5.4.

Returning a nonce provided in the authorization request via a claim in the ID token response is supported as well.

For example, if email and profile scopes are requested in the authorization request, the IDP being used is azure, and the claims mapping contains mappings to Azure attributes, then the email and profile claims will be included in the ID token response with the value of the associated attribute.

Attribute Providers

attrProviders are an optional configuration for an identity system or data store from which the OIDCProvider may retrieve additional attributes used in claimsMapping.

Connector

connector is a reference to the name of the defined connector which will be used as an attribute provider.

Username Mapping

usernameMapping defines the attribute that will be used as a search key to query for the user’s attributes.

Authentication

authentication defines how users are authenticated for this app.

IDPs

idps lists the IDPs which will be used to authenticate the user.

IsAuthenticated service extension

isAuthenticatedSE is an optional service extension that can be used to override the default behavior that determines if a user is already authenticated. This extension must be used with authenticateSE.

Authenticate service extension

authenticateSE is an optional service extension used to take control of how authentication will be done. This extension must be used with isAuthenticatedSE.

Access Token

accessToken defines the configuration for the OAuth access token.

Type

typecan be set to either jwt (default) or opaque.

Length

length defines the length of an opaque access token. The length can set to between 22 and 256 characters. If unset, the default length is 28 characters.

Lifetime Seconds

lifetimeSeconds controls the lifetime of an access token. By default, access tokens have a lifetime of one hour.

Refresh Token

refreshToken defines the configuration for the OAuth refresh token. Refresh tokens are used to get a new access token without user interaction.

Refresh tokens are automatically rotated with every access token refresh. That is, everytime a refresh token is used to generate a new access token, a new refresh token is also returned. If a previously issued refresh token is reused outside an acceptable window, the active refresh token will be invalidated.

Refresh tokens are rotated to protect against refresh token replay attacks and compromised long-lived refresh tokens. For more information, please reference the OAuth Security Topics RFC.

Allow Offline Access

allowOfflineAccess defines whether a client can request refresh tokens. Refresh tokens will only be issued if the allowOfflineAccess flag is set to true and the authorization request includes the offline_access scope.

Length

length can be set to between 22 and 256 characters to define the length of a refresh token. If unset, the default length is 28 characters.

AllowedAudiences

allowedAudiences is an optional configuration that represents a list of audiences that are allowed to consume access tokens. When a client makes a request to the authorization endpoint, an optional resource parameter can be included that indicates the target audience of the token. The value provided in the resource parameter must be on the list of allowed audiences.

This configuration is used when resource servers, such as APIs, authorize via the access token. For more information, please reference RFC 9068.

BuildIDTokenClaims service extension

The buildIDTokenClaimsSE is an optional service extension that can customize how claims in the ID token are built.

BuildAccessTokenClaims service extension

The buildAccessTokenClaimsSE is an optional service extension that can customize how claims in the access token are built.

Examples

Basic OIDC App Config Example

apps:
  - name: exampleOIDCApp
    type: oidc
    clientID: exampleClientID
    clientSecret: <exampleClientSecret>
    redirectURLs:
      - https://app.enterprise.com/oidc
    logoutRedirectURLs:
      - https://app.enterprise.com/oidc/logout
    attrProviders:
      - connector: ldap
        usernameMapping: okta.email
    authentication:
      idps:
        - okta
    claimsMapping:
      email: ldap.mail
      given_name: ldap.givenname
      family_name: ldap.sn

OIDC App With Service Extension

apps:
  - name: exampleOIDCApp
    type: oidc
    clientID: exampleClientID
    clientSecret: <exampleClientSecret>
    redirectURLs:
      - https://app.enterprise.com/oidc
    authentication:
      isAuthenticatedSE:
        funcName: IsAuthenticated
        file: /etc/maverics/extensions/auth.go
      authenticateSE:
        funcName: Authenticate
        file: /etc/maverics/extensions/auth.go
    buildAccessTokenClaimsSE:
      funcName: BuildAccessTokenClaims
      file: /etc/maverics/extensions/auth.go
    buildIDTokenClaimsSE:
      funcName: BuildIDTokenClaims
      file: /etc/maverics/extensions/auth.go
    claimsMapping:
      email: okta.email

/etc/maverics/extensions/auth.go

package main

import (
	"net/http"

	"github.com/strata-io/service-extension/orchestrator"
)

func IsAuthenticated(api orchestrator.Orchestrator, _ http.ResponseWriter, _ *http.Request) bool {
	logger := api.Logger()
	logger.Debug("se", "determining if user is authenticated")
	
	session, err := api.Session()
	if err != nil {
		logger.Error("se", "unable to retrieve session", "error", err.Error())
		return false
	}

	isOktaAuth, err := session.GetString("okta.authenticated")
	if err != nil {
		logger.Error("se", "unable to retrieve session value", "error", err.Error())
		return false
	}
	if isOktaAuth == "true" {
		return true
	}

	return false
}

func Authenticate(api orchestrator.Orchestrator, rw http.ResponseWriter, req *http.Request) {
	logger := api.Logger()
	logger.Debug("se", "authenticating user")

	oktaIDP, err := api.IdentityProvider("okta")
	if err != nil {
		logger.Error(
			"se", "failed to retrieve Okta IDP",
			"error", err.Error(),
		)
		http.Error(
			rw,
			http.StatusText(http.StatusInternalServerError),
			http.StatusInternalServerError,
		)
		return
	}
	oktaIDP.Login(rw, req)
}

func BuildAccessTokenClaims(api orchestrator.Orchestrator, _ *http.Request) (map[string]any, error) {
	logger := api.Logger()
	logger.Debug("se", "building access token claims")
	
	session, err := api.Session()
	if err != nil {
		logger.Error("se", "unable to retrieve session", "error", err.Error())
		return nil, err
	}

	roles, err := session.GetString("okta.roles")
	return map[string]any{
		"scope": roles,
	}, err
}

func BuildIDTokenClaims(api orchestrator.Orchestrator, _ *http.Request) (map[string]any, error) {
	logger := api.Logger()
	logger.Debug("se", "building ID token claims")
	
	session, err := api.Session()
	if err != nil {
		logger.Error("se", "unable to retrieve session", "error", err.Error())
		return nil, err
	}

	groups, err := session.GetString("okta.groups")
	return map[string]any{
		"groups": groups,
	}, err
}