Configuring the Token Exchange Service

 

What is the Token Exchange Service?

The Token Exchange Service converts a (signed) Json Web Token (JWT) into a sealed OpenEdge CLIENT-PRINCIPAL, using the Spring components that are configured as part of a PASOE instance.

It is typically called from an OpenEdge client, as part of the JwtAuthenticationService - a service that allows sign-ins to GUI clients using OAuth2 flows.

API

The service is installed in a standalone webapp; typically this is named guiauth, although it can have any name. This document assumes that the webapp and ABL application are both named guiauth.

Request

There is a single endpoint at /<webapp>/web/exchange , with an optional query parameter , dbg .

The request must include an Authorization header, with an authentication scheme of Bearer, and the JWT token as the value.

For example, a valid request would be

GET /guiauth/web/exchange HTTP/1.1 Host: sfrbo.consultingwerkcloud.com:8820 User-Agent: OpenEdge-HttpClient/0.5.0 (WIN32/64) OpenEdge/12.7.0.1181 Lib-ABLSockets/0.6.0 Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.dyt0CoTl4WoVjAHI9Q_CwSKhl6d_9rhM3NrXuJttkao

Response

HTTP Status

Cause

Content Type

Response

HTTP Status

Cause

Content Type

Response

200 / OK

Successful execution

application/json

The default response body contains only a base64-encoded CLIENT-PRINCIPAL

{ "token": "<base64-encoded-client-principal>" }

If the request is made with a dbg query string (eg GET /guiauth/web/exchange?dbg) the following properties are added to the response.

"qualifiedUserId": "<client-principal-qualified-user-id>", "domainType": "<client-principal-domain-type>", "loginState": "<client-principal-login-state>", "sealAt": "<client-principal-seal-timestamp>", "expireAt": "<client-principal-login-expiration-timestamp>", "loginHost": "<client-principal-login-host>", "clientTty": "<client-principal-client-tty>", "sessionId": "<client-principal-session-id>", "roles": [ "<client-principal-role>" ], "properties": [ {"<client-principal-property-name>": "<client-principal-property-value>"} ]

400 / Bad request

  • No Authorization header

  • Invalid login state

application/json

405 / Method not allowed

Request made with a HTTP method that is not GET

Depends on request’s Accept header

Static error page as configured by the webapp

500 / Internal server error

Any otherwise-unhandled exception

Depends on request’s Accept header

Static error page as configured by the webapp

501 / Not implemented

Request made with a HTTP method that is not GET and not any standard HTTP method

Depends on request’s Accept header

Static error page as configured by the webapp

PASOE setup

The Token Exchange Service uses a dedicated oeabl-based webapp to convert a JWT into an OpenEdge CLIENT-PRINCIPAL.

Install guiauth webabb

Using the command-line, use the tcman deploy command

Note the final argument is the name of an ABL application for this service. This can be anything but for simplicity should be the same as the name of the webapp.

This results in a PROPATH that is self-contained in the PASOE instance.

Deploy web handler

The Consultingwerk.SmartFramework.Authentication.JwtExchangeWebHandler webhandler must be deployed into the <instance>/ablapps/guiauth/openedge folder.

Configure web handlers

The default web handler should be OpenEdge.Web.DefaultWebHandler ; this is configured in <instance>/conf/openedge.properties .

Configuration of the webhandler varies between OpenEdge versions. In OpenEdge 12.2 and later, a exchange.handlers file can be used. In other versions, the configuration is done in the openedge.properties configuration file. Information about these approaches can be found at https://docs.progress.com/bundle/pas-for-openedge-management/page/Deploy-web-handler-services.html .

Using exchange.handlers

Create a file named exchange.handlers in the <instance>/webapps/guiauth/WEB-INF/adapters/web/exchange folder. You will most likely have to create the folder. Save the below into the exchange.handlers file.

If this file is modified while the instance is running, the instance must be told to reload the configuration. See https://docs.progress.com/bundle/pas-for-openedge-reference/page/Refresh-web-handlers-for-an-ABL-web-application-jmx-refreshWeb.html.

Using openedge.properties

The web handler must be added to the [guiauth.guiauth.WEB] section in <instance>/conf/openedge.properties .

Configure webapps/guiauth/WEB-INF/oeablSecurity.properties

The webapps/guiauth/WEB-INF/oeablSecurity.properties file contains the configuration needed to exchange the JWT.

Information needed from the identity provider (IdP)

Before the webapp’s security can be configured, certain information is needed from the identity provider (the service that issues the JWT).

Data

Used for

Comment

Data

Used for

Comment

Client (application) ID

oauth2.resSvc.audience

Typically a GUID, available in an Overview screen.

JWKS URI

jwtToken.keystore.jwkurl

Used to validate the signatures of JWTs being converted. This information should be part of an OpenID metadata or similar document.

Claim name for CLIENT-PRINCIPAL user id

jwtToken.usernameField

If set, this JWT claim is used to populate the CLIENT-PRINCIPAL’s QUALIFIED-USER-ID attribute; the default value is the sub claim.

Note

  • This only works in OpenEdge 12.7+

Examples of where some of these values can be found in Azure AD are in https://consultingwerk.atlassian.net/wiki/spaces/SCL/pages/1875476481/Use+Azure+Active+Directory+AD+OAUTH2+authentication+with+PASOE .

Configure domains and keys

A domain and it’s sealing key (Domain Access Code or DAC) must be provided to enable the CLIENT-PRINCIPAL to be sealed. A CSV is used as input data to generate a secure keystore by the $DLC/bin/gendomreg tool. The default keystore used in a PASOE instance is <instance>/conf/ABLDomainRegistry.keystore . Alternate locations for the keystore used can be configured using the OEClientPrincipalFilter.registryFile property.

Care should be taken not to replace any existing domains.

The CSV file containing the list of domains and their DAC values MUST NOT be deployed to a production instance.

If the JWT’s sub claim is used for the CLIENT-PRINCIPAL QUALIFIED-USER-ID (the default behaviour), then the blank OE Domain will be used. The value of the OEClientPrincipalFilter.domain property can be used to assign a default domain in such cases. This domain’s DAC should also be generated into the domain keystore, regardless of whether it is blank or has a value.

Sample configuration file

Other configuration

Only the web transport should be enabled for the guiauth webapp. Similar to the below, the adapterEnabled property for all other transports must be set to 0

Testing

Use a client to log in. This client can be a web browser, or REST client or any other client that allows you to capture / read the response. The JWT will be returned as part of the redirect URI (either in the URL as a fragment or query, or in a response body).

Create an HTTP request and pass the JWT as the bearer token (see above).

JWT validation

You can validate JWT’s at https://jwt.ms/ or https://jwt.io .

Troubleshooting

If there are format/syntax issues with the configuration, errors will be reported in the instance’s session manager (“dated”) log (eg <instance>/logs/guiauth.<date>.log).

If there are issues with the token conversion, it may be necessary to increase logging levels to capture errors. Typically logging all “Spring Security” messages will shed some light, at the cost of very large session manager logs.