Skip to main content
Skip table of contents

HCMS Authorization JWT and OAuth integration

Details about JWT authorization and some basic notes about custom OAuth integration.

This document describes possible ways to configure a JWT authorization provider and use it to integrate HCMS with some OAuth2/OpenID server. Note that there is no special OpenID nor OAuth2 support out-of-box; instead, the generic JWT support is flexible enough to use it with OAuth2 standard (which is based on JWT).

Examples are usually provided for Keycloak (the authorization server used by future versions for censhare server) and AWS Cognito (identity and authorization service in AWS cloud), but the similar principles can be used with other implementations. Please note that configuration of these services is out of scope of this document.

Keys

OAuth mandates use of asymmetric cryptography, with public keys publicly available - usually as a JWKS URI endpoint. Using this endpoint is the recommended way to integrate HCMS with OAuth2 server; putting keys directly to HCMS configuration is, however, possible if needed.

OpenID-compatible providers provide so-called well-known endpoint which can be used to obtain, among other information, the correct JWKS URI (URI). This endpoint is constructed by appending /.well-known/openid-configuration to the base provider URL and the response contains JSON property jwks_uri with correct JWKS URI. Note that step must be done manually; HCMS does not actually support this "well-known" endpoint.

XML
<jwt>
  <!-- keycloak; obtained from https://censare-test-css01.censhare.com/auth/realms/censhare/.well-known/openid-configuration -->
  <jwks url="https://censhare-test-css01.censhare.com/auth/realms/censhare/protocol/openid-connect/certs"/>
  <!-- AWS Cognito, with eu-central-1_6WIPtfucI  being the Pool Id
    obtained from https://cognito-idp.eu-central-1.amazonaws.com/eu-central-1_6WIPtfucI/.well-known/openid-configuration
  -->
  <jwks url="https://cognito-idp.eu-central-1.amazonaws.com/eu-central-1_6WIPtfucI/.well-known/jwks.json"/>
</jwt>

Note that the jwks element has been added in HCMS version 2.4 and is not available in previous version.

Direct roles mapping

Some OAuth2 providers have their own (non-standard) way to assign groups or roles to users and then serve them as a JWT claim. This can be directly used by HCMS.

For example, AWS Cognito automatically exposes names of all user's groups as a claim cognito:groups. To use them as HCMS roles, the default claim name must be changed:

XML
<jwt roles-claim-name="cognito:groups">
  <jwks url="https://cognito-idp.eu-central-1.amazonaws.com/eu-central-1_6WIPtfucI/.well-known/jwks.json"/>
</jwt>

Keycloak, on the other hand, allows each user to have several "Roles" assigned and exposes them in claim realm_access, but this claim is actually a complex structure (JSON object). Configuration this needs to provide full path to the array (note that separator must be defined):

XML
<jwt roles-claim-name="realm_access/roles" roles-claim-separator="/">
  <jwks url="https://censhare-test-css01.censhare.com/auth/realms/censhare/protocol/openid-connect/certs"/>
</jwt>

This kind of roles mapping is very convenient, but inflexible and not viable for all providers.

Rule-based role assignment

Roles can be assigned by using <role> elements. Each one represents one rule and (potentially) adds one additional role to the request.

  • Element content (text) is the role name.
  • Attributes are optional. <role>rolename</role> element just always adds this role (this is rarely useful).
  • Attribute claim-name declares that the role is added only when the claim of this name is present in the JWT token.
    • Value of the claim is irrelevant (use claim-regex to test it).
    • If the attribute claim-separator is also present, then the claim-name is actually a path (with the claim-separator character used as separator) into a nested JSON object. This is the same as roles-claim-separator in the <jwt element (above).
      • Claim name/path is verbatim - there is no special character processing and not escaping available. The separator must be chosen carefully to avoid conflict with the property names.
  • Claim value can be tested by regular expression, defined by attribute claim-regex.
    • Java regular expression pattern is fully supported.
    • Fully value of the claim must match. For substring matches, .* must be added at the start and the end.
    • If the claim value is array, each element is matched separately until first match.
    • Otherwise, claim value is treated as a string and automatically converted, if needed. This is true also for JSON object and arrays - the regular expression is evaluated against their serialized form.

Examples:

XML
<jwt>
  <jwks url="https://censhare-test-css01.censhare.com/auth/realms/censhare/protocol/openid-connect/certs"/>
  <role claim-name="scope" claim-regex="\\bemail\\b">has-email</role>
  <role claim-name="aud" claim-regex="account">logged-in</role>
  <role claim-name="realm_access/roles" claim-separator="/" claim-regex=".*offline.*">allow-offline</role>
</jwt>

Note that the <role> elements are at the same level as the <jwks element. While the <pem> and <hmac> elements also support nested <role> (which are applied only if the appropriate key is used to verify the token), the <jwks element does not.

User asset lookup

Standard

When the "sub" claim contains valid integer number (i.e. string containing only decimal digits) and the number matches existing asset, this asset is considered the "user's asset" and available as userId (=user) variable. These assets must be of type person. or any subtype; if the asset is of any other type, the request is refused with 401 Unauthorized.

Custom

This is an optional feature, which must be carefully configured for a specific data model and specific JWT claims (usually issued by some OpenID Connect provider).

The <jwt> element can contain one or more <user-lookup/> elements, each with mandatory query attribute. This query attribute must contain a valid HCMS query; it is parsed at startup time. Invalid syntax prevents start, but non-syntax/data-model errors (missing schema, missing feature, invalid feature type, missing variable) are ignored this time. This allows HCMS to start even before the schema(s) used actually exist.

XML
<jwt>
  <jwks url="https://dev-77547913.okta.com/oauth2/v1/keys" cache-keys="3000" cache-error="5"/>
  <!-- the recommended way to identify user: special feature with the user's id in one specific provider  -->
  <user-lookup query="@account.openid[sub=${jwt/claim/sub} &amp; provider=&quot;okta.dev-77547913.&quot;]"/>
  <!-- much simpler, but not recommended by the OIDC standard: just use the user's email  -->
  <user-lookup query="@user.email=${jwt/claim/email}"/>
</jwt>

On each request, when the JWT is successfully parsed and validated, those queries are properly compiled and executed. Evaluation ends with the first query that is successfully compiled, executed and finds at least one asset; this asset is then used as the user's asset.

  • Compilation errors are ignored, because
    • the schemas still might be missing
    • variables might be missing and that should prevent request execution
  • Queries are executed without any schema context, so @schemaname is always necessary.
  • Most request variables are available, with the obvious exception of those about user (user/userId, userName, etc).
    • Note that the same is true about the filter queries inside cs:permissions (if any).
  • Presumably, queries use JWT claim variables (typically ${jwt/claim/sub}), but this is not enforced. It is HCMS administrator's responsibility to configure this mechanism in a secure way.
  • Queries are executed with special "internal daemon" privileges, having all roles and even bypassing false permission.
    • Recommendation: the schema used in these queries should require roles that are never normally used. Frontend should never have any access to these entities.

Unlike the direct asset-id parsing, no special requirements about the asset type are enforced. Asset type is actually already part of the queries (as part of schemas).

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.