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_GetClaim | Retrieve 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_GetClaims | Get all ID token claims as a single JSON string |
KCML_OIDC_LogInToken | Obtain a new auth token by prompting the user to reauthenticate via KClient and a browser |
KCML_OIDC_LogOutToken | End session. Drop stored tokens, revoke any refresh tokens and call the IDP's end_session_endpoint |
KCML_OIDC_RefreshToken | Attempt 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_RevokeToken | Revoke OIDC auth token |
KCML_OIDC_SetTokenExpiry | Set auth token expiry in seconds. For testing. Cannot be used to extend token lifespan |
KCML_OIDC_TokenExpiresIn | Returns 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$)