OAuth 2.0

KClient can obtain a user identity via OAuth 2.0 and pass that identity on to Connnection Manager which will verify it before passing it on to KCML.

Configuration (IDP)

When setting up an identity provider account for use with KClient one or both of KClient's callback URLs must be registered. The possible URLs are:

Note that the HTTP callback will use an ephemeral port so the configuration should be set to allow any port number. HTTP callback is the default. KClient can be configured to use a KClient URL as an alternative.

Configuration (Connection Manager)

Configuration on the application server is stored in kcml.conf in an [oauth2] section.

client_id This is the value issued when registering with the identity provider. Only tokens matching this ID wil be accepted as valid
client_secret To verify tokens signed using the HS256 algorithm access is required to the secret used to sign them. The RS256 public key algorithm is preferred.
issuer The domain name of the identity provider.
user As we are not authenticating a normal user account this is the user which KCML will run as.
proxy URL of proxy if one is required to access the IDP
debug_errors In case token validation fails error descriptions are written to syslog. To aid development setting this value to 1 will also write those descriptions are also written to KClient. This value should not be set in production environments.
matchclaims As an additional check after the token has been verified, the claims within the token can be tested before allowing KCML to start.
claim_lists As a convenience where a matchclaims test could accept one of many values it is possible to add named lists of values for use in a matchclaims expression.

There are also some settings which control how Connection Manager generates KClient links.

callback_port Fix KClient's callback port to a single value rather than use epehemeral ports.
use_kclient_uri Use a kclient:: URL as the callback.
scopes Space separated list of scope KClient will request. By default KClient will request all published scopes.

Checking Claims

Connection manager has the ability to perform checks on a token's claim values via the matchclaims tag. These checks are performed once the token is verified as otherwise valid. There is a simple syntax that supports operators = <> $IN AND XOR & OR as well as parentheses.

	// Only allow login if you have a verified email
	email_verified="true"
	// Valid email and check location
	email_verified="true" AND location="France"
	// Exclude a specific value
	email_verified="true" AND location<>"France"
	// Allow login if location is found in a list of allowed values.
	// To configure add an entry to claims_lists named 'ALLOWED_LOCATIONS' where the value is a list of whitespace separated strings.
	location $IN ALLOWED_LOCTIONS

KClient Command Line

KClient needs to know the issuer URL to get configuration information from the identity provider. This will come from <issuer>/.well-known/openid-configuration

--oidc-param-issuer myauth.com

KClient also needs the client id which was issued when registering with the identity provider:

--oidc-param-client-id Z2Fz90QfKG4mILP3Pa5w7trxXI38r92e

The identity provider needs a redirect URI to pass an authorization code to. This can be a local http URI (the default) or a KClient URI

To use a KClient URI specify:

--oidc-param-appscheme

Although no parameter is required to use an http URI it might be necessary to specify a particular port to listen on. Normally KClient will automatically use an ephemeral port as per RFC8252 but some identity providers require a URI with a specific port be registered (Auth0 does this). In this case you will need to specify a port higher than 1024 with the limitation that only a single KClient can authorize at a time.

--oidc-param-callbackport 1234

When using an http URI KClient will generate a basic respose page indicating success or displaying any errors. KClient can redirect the browser to a custom handler page which may display appropriate branding etc. It is then the page's responsibility to display any errors. These will be passed as query strings such as 'error=access_denied' and are generated by the identity provider.

--oidc-param-redirect-callback https://mycompany.com/myhandlerpage

By default KClient will ask for all scopes published in .well-known/openid-configuration. It is possible to specify the exact scopes KClient will request to avoid requesting more information than is needed and also if scopes are required which are not published. In this case the 'openid' scope should always be requested along with 'offline_access' if refresh tokens are required. The scope list is entered as a space separated list.

--oidc-param-scopelist "openid email"

Auth0 specific. After a logout redirect to this URL. The url must match the one configured in the IDP's settings.

--oidc-param-logout-redirect-callback

Use this option to force the user to enter their credentials even if the browser already has a current login session.

--oidc-param-forceauth

These arguments combine with the usual host and service arguments to log into a system e.g.

kclient.exe -h myhost -v myservice --oidc-param-client-id Z2Fz90QfKG4mILP3Pa5w7trxXI38r92e --oidc-param-issuer myauth.com

Use In KCML

Connection Manager exposes the identity information via two environment variables:

KCML_OIDC_SUBJECT The principle that is the subject of the identity token. The format of this value is dependent on the identity provider. It is unique within the identities the provider manages.
LOGNAME As we have not authenticated as a regular Unix user LOGNAME will not contain a valid user name. Instead it will contain a special value which can be compared with the KCML variable _KCML_LOGNAME_MAGIC$. LOGNAME is normally read-only but when it contains the magic value it can be written to with a value of the application's choosing. Once set LOGNAME becomes read-only again.

Accessing Token Data

KCML maintains session state internally and provides some functions to access it.

KCML_OIDC_GetClaimRetrieve a single claim. KCML will search the ID token first and will then fetch and search any user info returned from the IDP's user_info endpoint
KCML_OIDC_GetClaimsGet all ID token claims as a single JSON string
KCML_OIDC_LogInTokenObtain a new auth token by prompting the user to reauthenticate via KClient and a browser
KCML_OIDC_LogOutTokenEnd session. Drop stored tokens, revoke any refresh tokens and call the IDP's end_session_endpoint
KCML_OIDC_RefreshTokenAttempt to refresh auth token without user interaction. KCML will do this automatically if KCML_Session_SetTimeout has been used with KCML_SESSION_SETTIMEOUT_SSOTokenExpiry set in KCML_SESSION_SETTIMEOUT
KCML_OIDC_RevokeTokenRevoke OIDC auth token
KCML_OIDC_SetTokenExpirySet auth token expiry in seconds. For testing. Cannot be used to extend token lifespan
KCML_OIDC_TokenExpiresInReturns time to auth token expiry in seconds

Examples

Test if session is single sign on

	IsSSO = FLD($MACHINE.MACHINE_AuthType) == 1

Upate LOGNAME with a value derived from the 'sub' claim

	IF (ENV("LOGNAME") == _KCML_LOGNAME_MAGIC$)
		// LOGNAME is not a real user. We can overwrite it.
		'SomeIDMapping(ENV("KCML_OIDC_SUBJECT"), BYREF MappedID$)
		// Use our derived username
		ENV("LOGNAME")=MappedID$
	END IF

Get a claim value. The variable is automatically redimmed

	LOCAL DIM email$
	'KCML_OIDC_GetClaim("email", BYREF email$)

Set up session timeout with token refresh and expiry. This is used if there is a requirement to maintain a valid OAuth session.

	LOCAL DIM t$_KCML_SESSION_SETTIMEOUT
	'_Init_KCML_SESSION_SETTIMEOUT(BYREF t$)
	FLD(t$.KCML_SESSION_SETTIMEOUT_Callback) = SYM('Tokentimeout)
	// Setting KCML_SESSION_SETTIMEOUT_SSOTokenExpiry causes session timeout to track token lifetime
	// and attempt to refresh tokens shortly before they expire.
	// If a refresh fails the timeout callback will be called with KCML_SESSION_TIMEOUT_SSOTokenExpired set
	FLD(t$.KCML_SESSION_SETTIMEOUT_SSOTokenExpiry) = TRUE
	'KCML_Session_SetTimeout(BYREF t$)
	DEFSUB 'Tokentimeout(BYREF s$_KCML_SESSION_TIMEOUT)
		LOCAL DIM login
		LOCAL DIM err$
		IF (FLD(s$.KCML_SESSION_TIMEOUT_SSOTokenExpired))
			// Token has expired. Any token refresh KCML may have attempted has failed.
			// To regain a valid session we must log in again.
			login = 'KCML_OIDC_LogInToken(BYREF err$)
			IF (login == FALSE)
				// Login has failed err$ contains an error message.
				// Maybe attempt a log in again or close the application.
			END IF
		ELSE
			// Normal session timeout things happen here
		END IF
	END SUB

To more easily test token timeouts we can force them to expire soon.

	// Set expiry to a small value. If using a very small value the token could expire
	// before a token refesh completes.
	'KCML_OIDC_SetTokenExpiry(30)
	// Check it. KCML will refresh the token in the background.
	// After a successful refresh the remaning time will increase.
	a$ = $PRINTF("Token expires in %d seconds", 'KCML_OIDC_TokenExpiresIn())
	// One way of forcing a refresh failure is to revoke the refresh token (if there is one)
	'KCML_OIDC_RevokeToken(BYREF err$)
	// It's also possible to refresh at any time.
	IF (NOT 'KCML_OIDC_RefreshToken(BYREF err$))
		// Token refresh has failed
	END IF

Log out

	// This opens the end_session_endpoint on the user's browser and will clear the browser's login state.
	// Any web applications the user has on the same IDP will also be logged out. Other KCML sessions will be unaffected.
	'KCML_OIDC_LogOutToken(BYREF err$)