Scalepoint uses OAuth 2.0 "Client Credentials" flow for server-to-server API calls. It is also sometimes referred to as "2LO." or "2-legged OAuth".
This flow does not assert any individual user identity, but simply allows client (customer's backend system) to authenticate against the API with an application account.
Here is how it looks:
Instead of using popular client_secret
that is effectively a password, we use private_key_jwt
authentication method for clients as defined in OpenID Connect Core 1.0 - Client Authentication, which requires confidential client to assert its identity by presenting a signed JWT token to the token endpoint.
So there are two tokens used in this flow:
Scalepoint provides simple to use client helper libraries in Java/.NET that abstract token generation away from you.
There are many other OAuth2 frameworks, libraries and even enterprise-class API gateways available for all platforms today that support this flow, but the implementations above should be sufficient.
The key parameters that must be fulfilled are:
Parameter | Description |
---|---|
client_id | Client identifier. Provided by Scalepoint |
certificate | Self-signed X.509 certificate (2048 bit RSA) with private key. Used to sign the assertion. See below how to generate and register it. |
tokenEndpointUri | Scalepoint OAuth2 token endpoint, see Endpoints section for more details |
scope(s) | OAuth2 scope(s) to request access to. I.e. case_integration |
The authentication token must be signed with a private key on the client. A self-signed RSA certificate can be used, since we don't rely on X.509 Certificate Authorities, but use peer-to-peer trust by pinning known public keys to customer accounts.
Self service key management portal isn't available initially, so customers have to send us the public key as base64 DER encoded certificate.
Multiple public keys per service account are supported to enable key rotation.
You have at least these options:
New-SelfSignedCertificate -CertStoreLocation cert:\localmachine\my -dnsname <client.certificate.test> -HashAlgorithm SHA256 -KeyAlgorithm RSA -KeyLength 2048 -KeyExportPolicy Exportable -KeySpec Signature -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.3") -NotAfter (Get-Date).AddDays(1095)
It is recommended to save the thumbprint from the output of this command and use it to find the certificate by in the application code. Check the client example.
makecert -r -pe -n "CN=<client.certificate.test>" -b 01/01/2015 -e 01/01/2018 -eku 1.3.6.1.5.5.7.3.3 -sky signature -a sha256 -len 2048 -ss my -sr LocalMachine
If you run a Windows version lower than 10, you can use makecert.exe instead, because New-SelfSignedCertificate
included in previous versions of Windows/Powershell cannot generate CSP certificate. It could come with your Windows of you can get it as part of Windows SDK, in which case it would be somewhere in C:\Program Files (x86)\Windows Kits\10\bin\x64\makecert.exe
(version in the path can differ).
Here's a one-liner to install Windows SDK with makecert.exe:
iwr "https://go.microsoft.com/fwlink/p/?LinkID=698771" -OutFile "$env:TEMP\win10-sdksetup.exe"; start "$env:TEMP\win10-sdksetup.exe" "/features OptionId.WindowsSoftwareDevelopmentKit /ceip off /q" -NoNewWindow -Wait
keytool -genkey -keyalg RSA -alias authkey -keysize 2048 -ext KU=digitalSignature,keyEncipherment -ext EKU=codeSigning -validity 730 -keystore keystore.jks -storepass <password> -dname "cn=<client.certificate.test>"
keytool -export -alias authkey -file certificate.cer -keystore keystore.jks -storepass <password>
Make sure to edit placeholders (in angular brackets) and optionally adjust file names (.jks and .cer) as necessary.
openssl req -subj '/CN=<client.certificate.test>' -new -newkey rsa:2048 -days 1095 -x509 -keyout client.key -out client.crt
You can append "-nodes" to save private key without password, which is, however, not recommended.
Do this only if you really have to!
The token endpoint request must include the following elements in the POST request body:
Key | Value | Description |
---|---|---|
grant_type | client_credentials | Client Credentials flow |
client_id | example: future_insurance |
(optional) Client identifier. |
client_assertion_type | urn:ietf:params:oauth:client-assertion-type:jwt-bearer | JWT Bearer token assertion type |
client_assertion | example: eyJ0eXA...FJcCY8gw (shortened) |
JWT token signed with client private key, check below how to generate one |
scope | example: api1 api2 |
Requested scopes, separated by space |
If request succeeded, the token endpoint will return HTTP status 200 OK and a JSON body similar to this:
{
"access_token": "eyJ0eXAiOiJKV1...sjghxBcw",
"expires_in": 3600,
"token_type": "Bearer"
}
The token itself is in "access_token" and the client SHOULD cache it for the duration according to "expires_in", which is in seconds.
The JWT token must be signed with RS256 and include the following claims:
Type | Value | Description |
---|---|---|
"iss" | example: future_insurance |
Client identifier |
"sub" | example: future_insurance |
The same client identifier |
"aud" | example: https://accounts.scalepoint.com/connect/token |
Token Endpoint URL |
"jti" | random string | Unique token identifier. Subsequent requests with duplicate "jti" values will be discarded to prevent replay attacks. This is a "nonce". |
"exp" | example: 1444747048 |
Token expiration time. Recommended to keep token TTL very short (i.e. 1 minute) since these tokens are only to be used once. Clock skew is accounted for in addition to that. |
Here is an example of such token's header and payload parts:
{
"typ": "JWT",
"alg": "RS256",
"x5t": "GC-1BetJL2e7dVk9VhH_FlQ7Jpk"
}
{
"jti": "35bc88e8-e61d-4aa1-8d46-4aed1798da2e",
"sub": "future_insurance",
"iat": 1444813734,
"iss": "future_insurance",
"aud": "https://accounts.scalepoint.com/connect/token",
"exp": 1444810144,
"nbf": 1444810134
}
Once you obtain an access token from the authorization server token endpoint, you can start making actual API requests using that token.
The token must be included in the HTTP Authorization header using "Bearer" schema. The header should look like this:
Authorization: Bearer <access_token>
Here's an example in C#:
using (var apiClient = new HttpClient())
{
var request = new HttpRequestMessage
{
RequestUri = new Uri(resourceUrl),
Method = HttpMethod.Post,
Headers = {
Authorization = new AuthenticationHeaderValue("Bearer", accessToken) /* <-- access_token */
},
/* Content = ... */
};
var result = await apiClient.SendAsync(request).ConfigureAwait(false);
}
For authentication, Scalepoint provides two endpoints – one for production and one for everything else.
https://accounts.scalepoint.com/connect/token
https://sandbox-accounts.scalepoint.com/connect/token
You can read more about the actual flow by following one of the links: