OAuth Authorization

available since version 15.3

Pricefx supports OAuth 2.0 authorization and has implemented token endpoints according to https://datatracker.ietf.org/doc/html/rfc6749#section-6.

Currently we implement the Authorization Code Grant flow only. We do NOT implement the Dynamic Client Registration, hence the client needs to be setup prior manually.

We do support PKCE (as per https://datatracker.ietf.org/doc/html/rfc7636).

Client Registration and Prerequisites Setup

In order to use OAuth 2.0 a client with its specific client_id needs to be defined. This is done in a new application property called oauthConfiguration. Multiple clients can be registered.

The configuration details are as follows:

JSON
{
  "knownClients" : {
    "client1_full_profile": {
        "redirect_uri": "http://localhost:8000/callback",
        "samlProfile": "PFXAZURE",
        "token_expiry": 7200,
        "client_secret": "secrethere",
        "client_description": "Some reasonably short text. Like a label",
        "defaultScope": "CUSTOMER_FETCH,CUSTOMERDETAILS_FETCH"
    },
    "client2_minimal_profile": {
        "redirect_uri": "http://localhost:8000/callback",
    }
  }
}
  • The client1_full_profile and client2_minimal_profile are not fixed strings but are the client_id values as they are sent by the OAuth 2.0 client and need to be adjusted accordingly. If the client sends a client_id that is not configured the flow will not work. As a minimum the redirect_uri needs to be configured as it will be sent by the client.

  • saml_profile: If configured and non-empty, the user authentication will go through the named SSO SAML auth flow (DEFAULT may be used if there is only one configured). Otherwise the app will show a login page.

  • token_expiry: Lifetime of the access token in seconds. If omitted, the default value is 7200.

  • client_secret: If configured, the client needs to send this along with the token endpoint per one of the two allowed means (HTTP auth header or form post value).

  • client_description: Text that is shown on the consent screen to describe the requesting client. If blank, client_id is shown.

  • defaultScope: If missing or null (not empty!), no “maximum” scope is applied. If specified as a list of valid permission names, the issued access tokens will carry these permissions (unless even fewer permissions are asked for in the request scope; see below). That means that the JWT will only be good for API endpoints that enforce no specific permission or require permissions that match the ones in the list. Note that the user has to have these permissions too! I.e., no extension of permissions is possible here!

API Endpoints

The URL scheme of both endpoints is:

https://<instance base URL>/pricefx/<partition>/oauth/<keyword>

oauth/authorize

This endpoint implements the https://datatracker.ietf.org/doc/html/rfc6749#section-3.1 authorization endpoint per OAuth 2.0 specification and requires the parameters as defined here: https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1. At the end of the flow it will send a code value back to/via the redirection URL specified in the client’s config & initial request (as they need to be the same).

The code has to be consumed within 10 minutes and can be used only once!

Example of an authorization request URL:

https://<instance base URL>/pricefx/<partition>/oauth/authorize?response_type=code&client_id=client2_minimal_profile&redirect_uri=http%3A%2F%2Fredirecthost%2Foauth%2Fcallback

oauth/token

This endpoint implements the https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3 token endpoint per OAuth 2.0 specification.

Example of a token request URL:

https://<instance base URL>/pricefx/<partition>/oauth/token?grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb

The response will look similar to this one:

{
       "access_token":"2YotnFZFEjr1zCsicMWpAA .... ",
       "token_type":"bearer",
       "expires_in":7200,
       "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA ...."
     }

There are some implementation specifics regarding the tokens:

The access_token will be a valid JWT token for that instance/partition for that user with a lifetime of 7200 seconds in this case. It can be used in the regular ways (e.g., as Cookie) but also newly in the standard “bearer” mode, i.e., as part of an HTTP Authorization header that looks like:

Authorization: bearer <token value here>

The refresh_token is also a valid (=signed) JWT token, however with a longer (currently unlimited but to-be-reviewed) lifetime. It carries a new type field that earmarks it as a refresh token. Hence it CANNOT be used directly as auth credential like the access token, but only as credential in the oauth/token request using the refresh_token scenario.

Request Scope / Default Scope Mechanism

In our implementation we generally define the scope as a comma separated list of permission names (in upper case). Invalid permission names will be removed/ignored in the process.

OAuth 2.0 generally knows about the scopes. I.e., the client can send an (optional) scope field in the authorize request, basically describing what set of permissions it likes to obtain. This is the request scope. In our implementation we can also define the maximum list of permissions (= the default scope) on a per known client basis. The mechanism works like this:

  • If no request scope is sent, the default scope is applied.

  • If no default scope is configured, no specific scope is applied to the access token. It then behaves like any session for/by that user (i.e., user permissions are always applied!).

  • If a request scope is sent and no default scope exists, the request scope is added to the access token. When using this token the effective permissions will be merged with the actual user permissions (intersection).

  • If both the request scope and the default scope exist / are sent, the intersection of the two will be applied to the token (and a further intersection with the user’s permissions while using the token).