OAuth Authorization

AVAILABLE SINCE 15.3 updated in version 17.2

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

The partition acts as the OAuth 2.0 authorization server. It supports the Authorization Code Grant flow only. Dynamic Client Registration is not supported, so an administrator registers each client manually.

Implementations by Version

Pricefx has two implementations. They differ in where you register the clients and in what the client must send.

Version

Client registration

Described in

17.2 and later

OAuth Administration page. Client secrets are stored securely.

OAuth Client Administration (Version 17.2 and Later)

15.3 to 17.1

oauthConfiguration application property

oauthConfiguration Application Property (Version 17.1 and Earlier)

In version 17.2, partitions still use the oauthConfiguration application property by default. The partition uses the clients from the OAuth Administration page only after Pricefx switches it to the new implementation. Until then, the section for version 17.1 and earlier applies.

OAuth Client Administration (Version 17.2 and Later)

You register the client on the OAuth Administration page. Give the client developer the Client ID, a client secret, and the Redirect URL.

This implementation has these rules:

  • Every client authenticates with a client secret. Public clients without a secret are not supported.

  • PKCE with the S256 method is required, as defined in RFC 7636.

  • Scopes are not supported. The access token has exactly the permissions of the user who gave consent.

Endpoints

Endpoint

URL

Authorization

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

Token

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

Authorization Request

The client sends the user to the authorization endpoint with these parameters, as defined in RFC 6749, section 4.1.1:

Parameter

Value

response_type

code

client_id

The Client ID from the OAuth Administration page. It is a UUID.

redirect_uri

The Redirect URL from the OAuth Administration page. The value must match exactly.

state

Required. The partition returns the value unchanged.

code_challenge

The Base64url-encoded SHA-256 hash of the code verifier. It has 43 characters.

code_challenge_method

S256. The plain method is not supported.

scope

Omit it or leave it empty.

Example of an authorization request URL:

https://<instance base URL>/pricefx/<partition>/oauth/authorize?response_type=code&client_id=<Client ID>&redirect_uri=http%3A%2F%2Flocalhost%3A9876%2Fcallback&state=<state>&code_challenge=<code challenge>&code_challenge_method=S256

The user signs in and grants consent. The partition then redirects the user to the redirect_uri with the code and state parameters.

The partition handles errors in two ways:

  • If client_id is missing, unknown or not a UUID, or if redirect_uri does not match, the partition returns HTTP 400. It does not redirect the user.

  • For all other errors, the partition redirects the user to the redirect_uri with the error and error_description parameters, as defined in RFC 6749, section 4.1.2.1.

The code is valid for 1 minute, and the client can use it only once. If the client sends the same code again within 5 minutes, the partition rejects it with invalid_grant. The partition also revokes the grant, so the access tokens issued for that code stop working.

Token Request

The client exchanges the code for tokens with a POST request to the token endpoint, as defined in RFC 6749, section 4.1.3. The request body contains these form parameters:

  • grant_type=authorization_code

  • code – the code from the authorization response

  • redirect_uri – the same value as in the authorization request

  • code_verifier – the PKCE code verifier

The client authenticates in one of these ways:

  • In the HTTP Authorization header, with Basic authentication (client_id:client_secret).

  • With the client_id and client_secret form parameters in the request body.

Example of a token request:

Bash
curl -X POST "https://<instance base URL>/pricefx/<partition>/oauth/token" \
  -u "<Client ID>:<client secret>" \
  -d "grant_type=authorization_code" \
  -d "code=<code>" \
  -d "redirect_uri=http://localhost:9876/callback" \
  -d "code_verifier=<code verifier>"

The response looks similar to this one:

JSON
{
  "access_token": "eyJhbGciOi ....",
  "token_type": "bearer",
  "expires_in": 1800,
  "refresh_token": "rt_ ...."
}

The expires_in value is the Token TTL of the client in seconds. The default is 1800 (30 minutes). An access token never outlives its grant. When the grant reaches its Absolute Refresh Token TTL sooner, expires_in is shorter than the Token TTL.

Refresh the Access Token

To get a new access token, the client sends a POST request to the token endpoint with these form parameters and the same client authentication:

  • grant_type=refresh_token

  • refresh_token – the refresh token from the token response

The response contains a new access token. The refresh token stays the same: the partition does not issue a new one.

The refresh token is valid as long as its grant. The grant is the consent that the user gave to the client. Two settings of the client limit the grant:

  • Idle Refresh Token TTL – The grant expires when the client does not use it for this time. Each token refresh and each API call with an access token counts as use.

  • Absolute Refresh Token TTL – The grant expires this time after the client received its first tokens, even if the client uses it regularly.

When the grant expires, the user must sign in and give consent again.

The partition rejects the refresh request in these cases:

Case

Error

The refresh_token parameter is missing.

invalid_request

The client secret is disabled, or the client is disabled or deleted.

invalid_client

The grant has expired.

invalid_grant

The user who gave consent is deactivated.

invalid_grant

Use the Access Token

The access token is a JWT. The client sends it in the HTTP Authorization header:

Authorization: Bearer <access token>

The access token contains these claims:

Claim

Value

iss

The issuer: https://<instance base URL>/pricefx/<partition>

aud

The audience. It is the same URL as iss. The partition rejects a token with a different audience.

sub

The ID of the user who gave consent.

iat

The time when the partition issued the token.

exp

The time when the token expires.

jti

A unique ID of the token.

type

oauth_code_flow_access_token

partition

The partition name.

client_id

The Client ID.

grant_id

The ID of the grant.

The partition returns HTTP 401 when the access token expires. It also rejects the token immediately when the grant expires, when an administrator disables or deletes the client, or when the user is deactivated. For details, see OAuth Administration.

oauthConfiguration Application Property (Version 17.1 and Earlier)

Up to version 17.1, you register clients in the oauthConfiguration application property. In version 17.2, this configuration applies until Pricefx switches the partition to the new implementation.

This feature is currently experimental and subject to change, in particular

  • Reducing permission scopes and to allowing for a secret to be used are currently unsupported

  • From version 17.2, the OAuth Administration page replaces the application property.

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_description": "Some reasonably short text. Like a label"
    },
    "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.

  • samlProfile: 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_description: Text that is shown on the consent screen to describe the requesting client. If blank, client_id is shown.

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.

The client must send client_id as a form parameter, also when it uses Basic authentication.

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.