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 |
---|---|---|---|
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 "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 |
| 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 |
---|---|---|
Client (application) ID | | Typically a GUID, available in an Overview screen. |
JWKS URI |
| 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 |
| 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
|
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.