Skip to content

OAuth 2.0 connections ​

A configuration value can hold an OAuth 2.0 flow. Precept gets the token, stores it and refreshes it. Actions read the token as config.<name>.accessToken.token.

Flow types ​

FlowWhat it needsBrowser step
oauth2.authCodeFlowAuthorization URL, token URL, client ID, client authenticationYes. A user signs in and approves access.
oauth2.clientCredentialsToken URL, client ID, client authenticationNo
oauth2.implicitFlowAuthorization URL, client ID, client secretYes
oauth2.customFlowAn action that returns a tokenNo
oauth2.jwtBearerToken URL, client ID, private keyNo. Some providers need a one-time consent grant.

A value in an oauth2.* block can read another config field with a template. For example, clientId: `{config.consumerKey}` takes the client ID from the consumerKey field. The settings page does not show a template value as a PDM default.

Client authentication ​

clientAuth sets how Precept proves its identity to the token endpoint. It works on oauth2.authCodeFlow, oauth2.clientCredentials and oauth2.jwtBearer.

ValueWhat Precept sendsUse it when
noneNothing. On oauth2.authCodeFlow, the client ID in the request body.The provider identifies the client by the signed assertion alone, or the client is a public client.
clientSecretBasicThe client ID and secret in an Authorization: Basic headerThe provider expects HTTP Basic authentication.
clientSecretPostThe client ID and secret in the request bodyThe provider expects the credentials as form fields.
privateKeyJwtA signed JWT as the client assertionThe provider wants a key instead of a shared secret.

The default depends on the flow:

FlowDefault
oauth2.authCodeFlowclientSecretPost
oauth2.clientCredentialsclientSecretBasic
oauth2.jwtBearernone

clientCredentials also accepts an optional scope.

The two secret methods need a client secret. privateKeyJwt needs a private key. You enter both in the integration settings, not in the PDM. privateKeyJwt also accepts keyId, algorithm and assertionLifetime, which work as described for JWT Bearer.

The privateKeyJwt assertion uses the client ID as the iss and sub claims and the token URL as the aud claim. The audience setting of oauth2.jwtBearer does not change it.

This example uses Microsoft Entra ID. The client proves its identity with a signed JWT instead of a secret.

pdm
vendor acme
name entra

config {
  auth: oauth2.clientCredentials {
    tokenUrl: "https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token"
    scope: "api://<app-id>/.default"
    clientAuth: "privateKeyJwt"
  }
}

JWT Bearer ​

oauth2.jwtBearer implements the signed-JWT grant (RFC 7523). Precept signs a short-lived assertion with your private key and sends it to the token endpoint. The endpoint returns an access token. There is no refresh token. Precept signs a new assertion when the token expires. If the token response has no expires_in, Precept signs a new assertion each time an action needs the token.

Settings ​

SettingRequiredMeaning
tokenUrlYesThe token endpoint. The PDM must set it.
clientIdYesThe application identifier. It becomes the iss claim.
subjectNoThe user to act as. It becomes the sub claim.
audienceNoThe aud claim. Defaults to tokenUrl.
scopeNoSpace-separated scopes.
scopeLocationNoclaim (default) puts the scope in the assertion. body sends it as a form field.
claimsNoExtra static claims, as an object.
algorithmNoThe signing algorithm. See Keys and algorithms.
keyIdNoThe kid header of the assertion.
assertionLifetimeNoSeconds the assertion stays valid. Default 300. The validator warns above 3600.
privateKeyYesA secret. See Keys and algorithms.
privateKeyPassphraseNoA secret. The passphrase of an encrypted key.
clientAuthNoDefault none.
clientSecretWith a secret clientAuthA secret.
consentUrlNoA template for the consent link. Use the placeholders {clientId}, {scope} and {redirectUri}. The Grant consent button appears only when the PDM sets this value.

The settings page has no field for scopeLocation, claims, algorithm, assertionLifetime or consentUrl. Set them in the PDM.

The PDM must set tokenUrl. It usually also sets audience, scope and consentUrl. The user sets clientId, subject and the key on the settings page.

Keys and algorithms ​

Precept accepts these private key formats:

  • A PEM file: PKCS#1, PKCS#8, encrypted PKCS#8 with a passphrase, or an EC key (BEGIN EC PRIVATE KEY)
  • A private JWK
  • A Google service-account JSON file

An RSA key must have 2048 bits or more. An EC key must use the P-256, P-384 or P-521 curve. If the file also holds a certificate, Precept uses the private key and ignores the certificate. Precept repairs PEM text that lost its line breaks or that holds \n escapes, for example a key copied from an environment variable.

Precept does not accept legacy encrypted PEM (a key with a Proc-Type: header). Convert it with openssl pkcs8 -topk8.

The key sets the default algorithm:

KeyDefaultOther values
RSARS256RS384, RS512, PS256, PS384, PS512
EC P-256ES256None
EC P-384ES384None
EC P-521ES512None

If algorithm does not match the key, the token request fails.

On the settings page, click Upload file and choose a .pem, .key or .json file. To enter the key as text, click Paste instead. If the key is encrypted, a Key passphrase field appears. After you save, the field shows "Stored" with Replace and Remove. Precept never shows the key again.

Example: DocuSign ​

This PDM connects to the DocuSign demo environment.

Start from the DocuSign template.

pdm
vendor acme
name docusign

defaults {
  http {
    format: "json"
    auth {
      headers: {
        Authorization: `Bearer {config.auth.accessToken.token}`
      }
    }
  }
}

config {
  baseUrl: string title("API Base URL") example("https://demo.docusign.net/restapi")

  auth: oauth2.jwtBearer {
    tokenUrl: "https://account-d.docusign.com/oauth/token"
    audience: "account-d.docusign.com"
    scope: "signature impersonation"
    consentUrl: "https://account-d.docusign.com/oauth/auth?response_type=code&scope={scope}&client_id={clientId}&redirect_uri={redirectUri}"
  }
}

Set it up in two places.

In DocuSign

  1. Open your integration key (the app) in DocuSign.
  2. Add an RSA keypair. Save the private key to a file.
  3. Add <your Precept URL>/sso/consent-complete to the redirect URIs of the integration key.

In Precept

  1. Open the Configuration tab of the integration.
  2. Set Client ID to the integration key.
  3. Set Subject (user to act as) to the DocuSign user ID.
  4. Upload the private key.
  5. Click Save Configuration.
  6. Click Grant consent. DocuSign opens in a new tab. Approve the request. You do this once per user and integration key.
  7. Click Test connection. Precept requests a token and shows the result.

Grant consent and Test connection use the saved settings. If the form has unsaved changes, they ask you to save first.

The consent redirect lands on <Precept base URL>/sso/consent-complete. That page only confirms that consent is recorded. Return to Precept to continue.

For production, change the token URL to https://account.docusign.com/oauth/token and the audience to account.docusign.com. Override both values in the UI, or publish a second PDM.

Other providers ​

Google service account ​

Upload the JSON key file on the settings page. Precept reads the private key from it. It also fills in the client ID, key ID and audience from the file, if the form and the PDM leave them empty. Precept fills these values only when you upload or paste the file on the settings page. If you send the key through the API, set the client ID yourself.

pdm
vendor acme
name googleapi

config {
  auth: oauth2.jwtBearer {
    tokenUrl: "https://oauth2.googleapis.com/token"
    scope: "https://www.googleapis.com/auth/drive.readonly"
  }
}

Salesforce ​

Salesforce takes the audience from the login host and needs no scope. The API base URL comes with the token response. See Token response extras.

pdm
vendor acme
name salesforce

defaults {
  http {
    format: "json"
    baseUrl: `{config.auth.tokenResponse.instance_url}/services/data/v60.0`
    auth {
      headers: {
        Authorization: `Bearer {config.auth.accessToken.token}`
      }
    }
  }
}

config {
  auth: oauth2.jwtBearer {
    tokenUrl: "https://login.salesforce.com/services/oauth2/token"
    audience: "https://login.salesforce.com"
  }
}

Box ​

Box needs an extra claim and sends the client credentials in the request body.

pdm
vendor acme
name box

config {
  auth: oauth2.jwtBearer {
    tokenUrl: "https://api.box.com/oauth2/token"
    claims: { box_sub_type: "enterprise" }
    clientAuth: "clientSecretPost"
  }
}

Custom grant type ​

grantType replaces the grant_type value that oauth2.clientCredentials sends. The default is client_credentials. The rest of the flow does not change. The value must be a quoted string.

grantType works only on oauth2.clientCredentials. On another flow, the validator reports grantType is only supported on oauth2.clientCredentials.

The client certificates example uses grantType: "client_cert".

Client certificates (mTLS) ​

Some providers need a client certificate on every connection. Add a tls block to defaults.http. Precept then presents the certificate on the action calls of the module and on the token requests of its OAuth flows.

EntryMeaning
certThe client certificate. A PEM block or a chain. Base64 DER, for example from a binary .cer file, also works.
keyThe private key of cert. PEM: PKCS#1, PKCS#8, encrypted PKCS#8 or an EC key.
passphraseThe password of an encrypted key or of pfx.
pfxA .p12 or .pfx bundle, in place of cert and key.
caA CA bundle in PEM. It replaces the default trust store when Precept checks the server certificate.

Each entry is a literal value or a template. Usually the template reads a config field. If a value is empty, Precept leaves the entry out. For example, an optional passphrase that the user did not set is left out. If neither cert and key nor pfx has a value, Precept sends no client certificate.

The validator checks the tls block:

  • An unknown entry fails with Unknown tls entry '<x>'; use cert, key, passphrase, pfx or ca.
  • pfx together with cert or key fails with tls cannot combine pfx with cert or key.
  • key, pfx and passphrase must read a field declared secret.

Certificate fields ​

Add the file() annotation to a string config field to show an Upload file button for it. The argument lists the file types to offer, for example file(".pem,.crt,.cer"). file() does not make a field secret. Declare the key and password fields secret as well.

Precept stores a text file as text. It stores a binary file, such as a .p12 file, as base64.

When you save, Precept checks the certificate fields that defaults.http.tls reads:

  • A certificate or CA field must hold a certificate.
  • A field must be 256 KiB or smaller.
  • When both halves of a pair are present, the key must belong to the certificate and the password must open the key or the .p12 file.

If a check fails, Precept does not save. The message starts with "The client certificate could not be used".

You can save a certificate before its key, or a .p12 file or encrypted key before its password. Precept accepts an expired certificate on save. The settings page shows "Valid until <date>" below a certificate. In the last 30 days it shows "Expires in N days", and after that date it shows "Expired on <date>".

Token requests ​

The token requests of oauth2.authCodeFlow, oauth2.clientCredentials and oauth2.jwtBearer use the tls block of defaults.http. To use a different certificate for the token requests, add a tls block to the oauth2.* block. To send no certificate on the token requests, set tls: false in the oauth2.* block.

pdm
auth: oauth2.clientCredentials {
  tokenUrl: "https://auth.example.com/token"
  tls: false
}

An oauth2.customFlow runs an action, so it uses the tls block of defaults.http.

Example: Pershing through Wove Connect ​

The Wove Connect token endpoint needs grant_type=client_cert, the consumer key and secret as HTTP Basic credentials, and the client certificate.

pdm
vendor acme
name pershing

defaults {
  http {
    format: "json"
    tls {
      cert: `{config.clientCertificate}`
      key: `{config.clientKey}`
      passphrase: `{config.clientKeyPassphrase}`
    }
    auth {
      headers: {
        Authorization: `Bearer {config.auth.accessToken.token}`
      }
    }
  }
}

config {
  baseUrl?: string
    - title("Base URL")
  consumerKey: string
    - title("Consumer key")
  consumerSecret: secret string
    - title("Consumer secret")
  clientCertificate: string
    - title("Client certificate (PEM)")
    - file(".pem,.crt,.cer")
  clientKey: secret string
    - title("Client private key (PEM)")
    - file(".pem,.key")
  clientKeyPassphrase?: secret string
    - title("Private key password")

  auth: oauth2.clientCredentials {
    tokenUrl: "https://apigatewayb2b.bnymellon.com/token"
    grantType: "client_cert"
    clientAuth: "clientSecretBasic"
    clientId: `{config.consumerKey}`
    clientSecret: `{config.consumerSecret}`
  }
}

The official module official/pershingAccountOpen holds the full version. official/pershingAccountOpenP12 uses a .p12 file in place of the PEM certificate and key.

Test connection ​

Test connection requests a new token with the saved settings, even when the stored token is still valid. Precept stores the new token. The result shows how long the token is valid and the names of any token response extras.

  • If a required setting is missing, the result names the settings to save first.
  • oauth2.authCodeFlow needs a refresh token. Click Initiate and sign in before you test.
  • oauth2.implicitFlow has no Test connection button.

If defaults.http has a tls block, Test connection also checks the client certificate:

  • If only one of the certificate and the key is saved, the result names the missing one.
  • If the baseUrl config field is set, Precept opens a TLS connection to that host and reports the TLS version and the server certificate. It sends no HTTP request.

If a module has a file() field and no OAuth flow, the Configuration tab shows a Test connection button for the whole module. That test checks only the TLS connection. It needs the baseUrl field and the client certificate.

Token response extras ​

Some providers return more than a token. Precept stores the extra fields of the token response under config.<name>.tokenResponse.<field>. For a block named auth, the Salesforce instance_url is config.auth.tokenResponse.instance_url.

Extras are stored this way for oauth2.jwtBearer, oauth2.clientCredentials and oauth2.authCodeFlow. A refresh of an oauth2.authCodeFlow token updates them.

Defaults and overrides ​

Values in the PDM block are defaults. The integration settings show them as field values with a "from PDM" badge. When a user edits a value, Precept stores an override. Reset removes the override, and the PDM default applies again.

Errors ​

When the token endpoint returns an error, Precept shows the provider's error and error_description with a hint. Test connection shows the same hint.

Provider errorHint
consent_requiredGrant consent for this client ID; use the consent link in the integration's settings.
invalid_grantCheck the subject and audience, that the public key is registered with the provider, and the server clock.
invalid_clientCheck the client ID and the client authentication method or secret.
unauthorized_clientThis client is not allowed to use this grant type at the provider.

Precept also reports key problems as configuration errors. Each message starts with "The private key could not be used". The problems are:

  • The file is not PEM, JWK or a Google service-account file.
  • The file holds a public key or a certificate, not a private key.
  • The key is legacy encrypted PEM.
  • The key is encrypted and has no passphrase, or the passphrase is wrong.
  • The RSA key has fewer than 2048 bits, or the key type or curve is not supported.
  • The algorithm does not match the key.

Workflows do not retry these errors.

When the client certificate fails, the message starts with "TLS handshake with <host> failed" and has one of these hints:

ProblemHint
The server rejects the client certificate or closes the connectionThe server did not accept the client certificate. Check that it is the certificate issued for this environment (QA or production) and that it has not been revoked.
The server certificate is not trusted, has expired or has the wrong nameThe server's certificate is not trusted. Upload the provider's CA bundle in the CA field.
The password is wrongThe password for the private key or .p12 file is wrong.
The key does not match the certificateThe private key does not belong to this certificate.
The client certificate has expiredThe client certificate has expired. Upload the renewed certificate.