Authentication in Customer's Canvas Hub
- 18-19 minutes to read
In this guide, we'll dive into how authentication works within the Customer's Canvas API. A solid understanding of this process is crucial for the protection and security of both your valuable Customer's Canvas data and your users' confidential order information.
Supported Auth schemes
Customer's Canvas implements OAuth2 authentication. It supports three OAuth2 flows:
- Client Credentials Flow
- Implicit Flow
- Authorization Code Flow
How to register your app in Customer's Canvas Hub
Before you can start using API in your app, you need to register your app in your Customer's Canvas account. Here you choose your preferred OAuth2 flow and get the credentials.
To register an app, follow these steps:
Go to your Customer's Canvas account.
Navigate to Settings > External apps.
Click Create.
Specify your app name.
Tip
Avoid generic names like My app or Test. Instead, name it in the same way as you call your app internally. Otherwise, it may lead to confusion.
Choose the preferred auth type (OAuth2 flow). See some guidelines on how to choose it in the next section.
Fill in the necessary fields (they vary depending on the auth type - see further explanations).
Copy the Client Id and Secret. You need to use this data in your code.
Click Save.
Choosing an Authentication Type
Deciding which authentication scheme to use can be challenging. Here are some suggestions to help you choose the right one for your needs.
Client Credentials
Choose this authentication scheme if:
- You need backend-to-backend communication between your app and Customer's Canvas.
- You don't want a Customer's Canvas login screen to appear during authorization.
- You can securely add app credentials to your app configuration, and they are not accessible to unauthorized personnel.
Implicit
Choose this authentication scheme if:
- You're making API calls from your browser-based frontend or mobile application.
- You need a Customer's Canvas login screen to appear during authorization, and the API calls should be made on behalf of a signed-in user.
- You don't have a server application that could be used as a proxy for this API.
Note
If possible, you should consider making API calls from your backend app instead of a frontend app for better security. Use Implicit only if creating a backend app is not an option.
Authorization Code
Choose this authentication scheme if:
- You're making API calls from your server-side backend app.
- You need a Customer's Canvas login screen to appear during authorization, and the API calls should be made on behalf of a signed-in user.
Token lifetime
This parameter refers to how long the access token remains valid. The default value is 3600 seconds (1 hour).
It's recommended to keep the token lifetime as short as possible for security reasons. If you request a new token before each API call, you can use a shorter lifetime, such as a few minutes. However, if that's not convenient and you prefer to capture a token on app launch, you can increase the lifetime depending on the length of an app session.
If you're not sure which value to set, it's best to leave the default value of 3600 seconds.
Permissions
When your app accesses Customer's Canvas resources using the Client Credentials Flow, it does so on behalf of a tenant admin. To protect your resources in case the access token is leaked, it's recommended that you restrict your app's access to only the necessary resources.
You can achieve this by using Permissions (also known as scopes). For each resource type, you can decide whether to prevent any access (No Access), allow only read access (Read), allow both read and update access (Update), or allow all CRUD (Create, Read, Update, Delete) operations (Full).
For the Implicit and Authorization Code flows, the Permissions block is disabled. The reason is that, for these flows, the app works on behalf of a user who signed in. The app can do whatever these users are allowed to.
Redirect Settings
When using the Implicit or Authorization Code flow in OAuth2, it's necessary to configure additional parameters such as redirect URLs. To understand how to fill them, let's briefly review how these flows work:
- To authenticate, you make an API call to a special authorize endpoint.
- As part of the data you send to this endpoint, you specify the redirect URL of your app.
- The user is redirected from your app to Customer's Canvas login page.
- Once the user successfully signs in, they are redirected back to the redirect URL specified in Step 2. The access token is included in the query string parameters of the redirect URL.
Note
This process will be explained in more detail later.
To enhance security and prevent malicious users from receiving access tokens to their apps, it is required to add the same redirect URL (or multiple URLs if you have different implementations of the authentication process) to these settings. You can think of it as a whitelist of redirect URLs.
The same idea is behind the post logout redirect URLs, which are supposed to handle the sign-out process of a user.
You may be concerned about the potential Cross-Origin Resource Sharing (CORS) issues. To avoid them, you should also include the base URL of all your redirect and post-logout endpoints in the Allowed CORS origins field.
Here's an example of how to configure these settings:
- Redirect URIs: https://example.com/api/cc-auth/login-callback
- Post logout redirect URIs: https://example.com/api/cc-auth/logout-callback
- Allowed CORS origins: https://example.com
Require client secret
This toggle determines whether the app must always send the client secret to the authorize endpoint. Enabling it provides an extra layer of protection against unauthorized access by malicious apps. However, for the Implicit flow, the client secret needs to be exposed on the frontend, making it easier to find.
Hence, we recommend turning it off for Implicit flow, but always enabling it for the Authorization Code flow.
Require PKCE
The PKCE (Proof Key for Code Exchange) parameter is a security feature used in the OAuth2 Authorization Code flow to prevent authorization code interception attacks.
When you toggle it on, the app is required to provide additional data to the authorize endpoint to verify its identity. This feature enhances security and should be enabled in the production version of your app. However, you may temporarily turn it off during troubleshooting or when creating a proof-of-concept of your app.
Revoke Credentials
To revoke the credentials, just remove the External App and create a new one with the same settings. Old Client ID and Client Secret won't be accepted by Customer's Canvas anymore.
Add credentials to your code
After registering your app as explained above, you'll need to add the Client ID and Client Secret of your External App to your application. This can be done through the app configuration settings or environment variables, depending on your app technology.
Although this may seem like a simple task, it's essential to keep in mind that these credentials provide access to your Customer's Canvas resources and personalization data. Therefore, it's crucial to follow these guidelines:
- Never hardcode these credentials in your code.
- Never commit config or .env files containing these credentials under version control system.
- If you accidentally do so, revoke the credentials and create new ones.
- For .NET developers working in a development environment, we recommend passing these parameters to the configuration using the dotnet user-secrets command, as explained in the Safe storage of app secrets in development in ASP.NET Core article in Microsoft documentation. Use similar mechanisms for other platforms if possible.
- For production environments, consider using secret management solutions like Azure KeyVault, AWS Secret Manager, AWS System Manager Parameter Store, HashiCorp Vault, or similar systems.
By following these guidelines, you can help protect Customer's Canvas credentials from unauthorized access.
Authentication API
Customer's Canvas offers an OAuth2-compatible authentication API with the same base address used for the Customer's Canvas admin panel:
- For the US environment - https://customerscanvashub.com
- For the EU environment - https://eu.customerscanvashub.com
To implement a standard OAuth2 authorization scenario, you'll need to work with the following main endpoints:
- Get a discovery document endpoint (information about other API endpoints)
- Authorize endpoint
- Token request endpoint
- End session endpoint
While there are additional endpoints available, these four are enough to implement a basic scenario.
The goal of using this API is to obtain an access token, which should be included with all requests to Customer's Canvas APIs, as explained further.
Note that the authentication API is not covered by Swagger, so there are no official API clients available. You'll need to either make HTTP calls directly or find a third-party library to handle OAuth2 authentication. For example, for .NET applications, you can use the IdentityModel library (see the docs).
In the examples that follow, we'll be using HTTP syntax.
Discovery document
A discovery document is used to retrieve metadata about the Customer's Canvas identity provider. This endpoint contains information about all other API endpoints, so it's the only one you need to hardcode (or add to your app's configuration).
The address of this endpoint is https://<base-url>/.well-known/openid-configuration
. If you open the discovery document address in your browser, you'll see a JSON document like this:
GET https://<base-url>/.well-known/openid-configuration
{
"issuer": "https://customerscanvashub.com",
"jwks_uri": "https://customerscanvashub.com/.well-known/openid-configuration/jwks",
"authorization_endpoint": "https://customerscanvashub.com/connect/authorize",
"token_endpoint": "https://customerscanvashub.com/connect/token",
"userinfo_endpoint": "https://customerscanvashub.com/connect/userinfo",
"end_session_endpoint": "https://customerscanvashub.com/connect/endsession",
... /* Omitted for brevity */
}
This document is generally stable and unlikely to change frequently. So, you may be tempted to just hardcode the addresses of the endpoints you need in your code. However, unless you are building a one-off app or experiment with API, we recommend fetching and caching these addresses on app launch rather than hardcoding them in your code.
Authorize
This endpoint is utilized to display the login screen, which is an integral part of Implicit and Authorize Code flows. You can obtain the address for this endpoint by using the authorization_endpoint
parameter in a discovery document.
Note
If you are seeking information about how to obtain a token for the Client Credentials flow, please skip ahead to the next section.
Like the Document Discovery endpoint, it is also a GET request. It requires passing a number of arguments as query parameters. Different combinations of these parameters are suitable for various scenarios. For a comprehensive list of these parameters, please refer to the Authorize endpoint reference. For the purpose of this article, we will focus solely on our specific scenarios.
Authorizing for Implicit flow
To start the authentication with the Implicit flow, make the following request:
GET https://<base-url>/connect/authorize?
client_id=9f4deec9-xxxx-xxxx-xxxx-e8faae92ab1c
& scope=Assets_full Projects_full
& redirect_uri=https://example.com/myapp/login-callback
& state=123456789
& response_type=token
Note
Line breaks and white spaces are added for better readability.
When making the request, use the following parameters:
client_id
: copy the client ID from your External App settings.scope
: insert a space-separated list of required permissions (see thescopes_supported
in the Discovery Document).redirect_uri
: specify a URL where the user is redirected after successful login. If it is not specified in the External App settings, it will be rejected.state
: pass a value that will be echoed back to theredirect_uri
. This is explained in more detail below.response_type
: for the Implicit flow you should usetoken
.
Once you call this endpoint, the user will be redirected to the Customer's Canvas login page. After the user successfully authorizes the request, they will be redirected to the URL specified in the redirect_uri
parameter. This URL will include a fragment that looks like this:
https://<YOUR REDIRECT URL>#access_token=<ACCESS TOKEN>&token_type=Bearer&expires_in=3600&scope=<SCOPES>&state=<YOUR STATE>
You will need to parse this fragment string and use the access_token
and state
values.
It's likely that you'll need to use the access token not only immediately after receiving it but also in future interactions. As a result, you'll need to establish a "login session" and preserve the state of this session, perhaps by storing it in a database. Before invoking the Authorize endpoint, you can create an empty login session record and utilize its ID as a state parameter when calling the Authorize endpoint.
When the redirect_url
is invoked, Customer's Canvas will pass this ID back via the state
in the URL fragment. This will enable you to identify your session and complete the login process. For instance, you could save the access token to the login session state at this point.
Another function of the state
parameter is to ensure that no one attempts to access a callback URL directly.
Once you have an access token, you can use it in the API calls. Read on for further details.
Authorize for Authorization Code flow (no PKCE)
When you set up the Authorization Code flow and disable the PKCE option, the usage of the Authorize endpoint is strikingly similar to the Implicit flow. The only difference is that you must specify the response_type
as code
:
GET https://<base-url>/connect/authorize?
client_id=9f4deec9-xxxx-xxxx-xxxx-e8faae92ab1c
& scope=Assets_read Private_assets_update
& redirect_uri=https://example.com/myapp/login-callback
& state=123456789
& response_type=code
Note
Line breaks and white spaces have been inserted between & and parameters for improved readability.
If the user is signed in, you will be redirected to your redirect_uri
, which will contain additional query string parameters:
https://example.com/myapp/login-callback?code=<AUTH CODE>&scope=<SCOPES>&state=<YOUR STATE>
The code
will consist of a one-time-use key that you must utilize with the Token endpoint (see below) to obtain an access token.
Authorize for Authorization Code flow (with PKCE)
The Authorization Code flow without PKCE is susceptible to a security vulnerability. If attackers intercept the authorization code and activate it before you, they can obtain an access token on your behalf. However, PKCE can prevent this by generating a special secret value, known as a code verifier, and creating a corresponding code challenge based on it.
To create a code verifier, you need to generate a random string with a length between 43 and 128 characters. It's crucial to note that shorter values could lead to error messages that are difficult to troubleshoot, so ensure that the string is long enough. While it's possible to use a constant value, generating a new code verifier for each request and storing it in a login session enhances security.
A code challenge is a base64-encoded SHA-256 hash value of the code verifier string. Here's a pseudo-code example:
var code_verifier = GenerateRandom43CharacterString();
var code_challenge = EncodeBase64(CreateHash("SHA256", code_verifier));
Tip
Most web stacks have implemented the SHA-256 hash calculation. For example, in C#, you can use the System.Security.Cryptography.SHA256 class, while PHP offers the hash function with $algo = sha256, and NodeJS provides the crypto module.
Once you obtain the code challenge value, you can call the Authorize endpoint like this:
GET https://<base-url>/connect/authorize?
client_id=9f4deec9-xxxx-xxxx-xxxx-e8faae92ab1c
& scope=Assets_read Private_assets_update
& redirect_uri=https://example.com/myapp/login-callback
& state=123456789
& code_challenge=UiQJkoYcXeFjsgrdgTfUXIwJRAV07SX4MDrq8AoZhP0
& code_challenge_method=S256
& response_type=code
Note that in addition to code_challenge
, there is an additional parameter, code_challenge_method
, which should always be equal to S256
. At this time, no other values are supported.
The remaining flow is nearly identical to the scenario without PKCE, with the only exception being that you need to send the code verifier when using the Token endpoint, as explained further.
Requesting an Access Token
To request an access token from the Customer's Canvas identity provider, you can utilize the Token endpoint. The address of the endpoint can be obtained through the token_endpoint
parameter of the Discovery Document.
Important
Please note that for the Implicit flow, the Authorize endpoint already returns an access token. Hence, there is no need to request it separately. If you are implementing the Implicit flow, you can skip ahead to the next section.
It is important to keep in mind that the Token endpoint call should be a POST request with parameters passed with the application/x-www-form-urlencoded
content type. The parameters that need to be included depend on the authentication type you choose.
Requesting an Access Token for the Client Credentials Flow
To acquire a token for the Client Credentials flow, you need to send the following x-www-form-urlencoded POST request:
POST https://<base-url>/connect/token
Content-Type: application/x-www-form-urlencoded
client_id=9f4deec9-xxxx-xxxx-xxxx-e8faae92ab1c
client_secret=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
scope=Assets_full Projects_full
grant_type=client_credentials
Note
Line breaks have been added, and form encoding has been removed for better readability.
The response from this request will be in the form of a JSON object that includes the access token, its expiration time, token type, and scope.
{
"access_token": "<YOUR TOKEN HERE>",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "Assets_full Projects_full"
}
The token should be used within the value specified in the expires_in parameter (in seconds). After the token expires, you can request another one.
Requesting an Access Token for the Authorization Code Flow
The process of obtaining a token for the Authorization Code flow is similar to that of the Client Credentials flow, with a few additional steps:
- Use the
grant_type
parameter set toauthorization_code
. - Include the
code
parameter, which contains the code received in the redirect URL you passed to the Authorize endpoint. - Include the
redirect_uri
, which is the same as the one for the Authorize endpoint. - If you use PKCE, you must include the string used to build a
code_challenge
as acode_verifier
. It is essential to note that you need to send the original string, not its hash representation, unlike the Authorize endpoint.
You can send the following x-www-form-urlencoded POST request to acquire the access token:
POST https://<base-url>/connect/token
Content-Type: application/x-www-form-urlencoded
client_id=9f4deec9-xxxx-xxxx-xxxx-e8faae92ab1c
client_secret=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
scope=Assets_full Projects_full
code=1234567xyz
code_verifier=pNmzR9QCVJzqoFbHu2rEWuWQR130SFAnSPdZPnOtFso
redirect_uri=https://example.com/myapp/login-callback
grant_type=authorization_code
Note
As before, line breaks have been added, and form encoding has been removed for better readability.
The response to this request will also be a JSON object containing the access token, its expiration time, token type, and scope.
{
"access_token": "<YOUR TOKEN HERE>",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "Assets_full Projects_full"
}
Updating a Token Without Re-Login
When a token expires, you must request a new one. For the Client Credentials flow, this is acceptable, but for the Authorization Code flows, users may find it inconvenient to re-login every hour (or another period you configure). Fortunately, the Customer's Canvas identity service supports refresh tokens, which can be used to receive new access tokens with the updated expiration date.
Important
Refresh tokens are not supported for the Implicit flow due to security reasons.
To obtain a refresh token, you should slightly modify the standard flow of receiving an access token. It is necessary to request an extra scope - offline_access
. Your Authorize endpoint call should look like this:
GET https://<base-url>/connect/authorize?
client_id=9f4deec9-xxxx-xxxx-xxxx-e8faae92ab1c
& scope=offline_access Assets_read Private_assets_update
& redirect_uri=https://example.com/myapp/login-callback
... /* other params omitted for brevity */
After receiving a code and requesting a token, the response will include the refresh_token
in addition to the access_token
:
{
"access_token": "<YOUR ACCESS TOKEN>",
"expires_in": 3600,
"token_type": "Bearer",
"refresh_token": "<YOUR REFRESH TOKEN>",
"scope": "Assets_full Artifacts_full offline_access"
}
Save this refresh token along with the access token in the login session, and remember to save the expiration time. Every time you use the access token, check if the token is about to expire (e.g., less than 60 seconds left). If so, use the Token endpoint with the grant_type
set to refresh_token
. Pass your current refresh token like this:
POST https://<base-url>/connect/token
Content-Type: application/x-www-form-urlencoded
client_id=9f4deec9-xxxx-xxxx-xxxx-e8faae92ab1c
client_secret=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
refresh_token=<YOUR CURRENT REFRESH TOKEN>
grant_type=refresh_token
This will return a new pair of access and refresh tokens. The new expiration date will be incremented by the normal expiration period configured in the External App settings. Update these values in the login session and continue using the updated access token as usual.
End session
Finally, let's discuss the proper way to sign out your users.
Important
This applies to the Implicit and Authorize Code flows only.
To accomplish this, you will need the identity token (or the id_token) that contains user information. Similar to the refresh token, you can obtain the identity token as part of the regular authentication flow.
The Authorize endpoint call will look slightly different depending on whether you use Implicit or Authorize Code call.
Obtain id_token for the Implicit flow
To obtain the id_token for the Implicit flow, you need to do three things:
- Pass an additional scope -
openid
. - Add
id_token
to theresponse_type
, making itid_token token
. - Add a new parameter -
nonce
, which should be a random string unique to each request.
Here's an example endpoint call:
GET https://<base-url>/connect/authorize?
client_id=9f4deec9-xxxx-xxxx-xxxx-e8faae92ab1c
& scope=openid Assets_read Private_assets_update
& redirect_uri=https://example.com/myapp/login-callback
& response_type=id_token token
& state=123456789
& nonce=<SOME UNIQUE RANDOM STRING>
As a result, the fragment of the redirect URL will include an additional element - id_token
:
https://example.com/myapp/login-callback#id_token=<YOUR ID TOKEN>&access_token=<YOUR ACCESS TOKEN>&token_type=Bearer&expires_in=3600&scope=openid%20Assets_full%20Artifacts_full&state=1234&session_state=<...>
This token can be parsed as a regular JWT token. But what is more important, you can use it in the EndSession endpoint, as explained below.
Obtain id_token for the Authorize Code flow
To obtain the id_token for the Authorize Code flow, add openid
to the scope. No need to change the response_type
or pass the nonce
. Here's an example endpoint call:
GET https://<base-url>/connect/authorize?
client_id=9f4deec9-xxxx-xxxx-xxxx-e8faae92ab1c
& scope=openid offline_access Assets_read Private_assets_update
& redirect_uri=https://example.com/myapp/login-callback
... /* omitted for brevity */
& response_type=code
Note
As you can see, we have combined openid
with offline_access
to be able to get not only the id_token but also the refresh token.
After you get the authorization code and pass it to the Token endpoint, the resulting JSON document will include all tokens:
{
"id_token": "<YOUR ID TOKEN>",
"access_token": "<YOUR ACCESS TOKEN>",
"expires_in": 3600,
"token_type": "Bearer",
"refresh_token": "<YOUR REFRESH TOKEN>",
"scope": "openid Assets_full Artifacts_full offline_access"
}
Using the EndSession endpoint
To use the EndSession endpoint, obtain the endpoint address from the end_session_endpoint
property of the Discovery Document. Send a GET request to this address to sign out the user. This endpoint requires passing only three parameters:
GET https://<base-url>/connect/endsession?
id_token_hint=<YOUR ID TOKEN>
& post_logout_redirect_uri=https://example.com/myapp/logout-callback
& state=123456789
Here's what you need to know about these parameters:
id_token_hint
: This is an id_token that you obtained earlier. It identifies the user that you want to sign out.post_logout_redirect_uri
: This is the URL of an endpoint in your app that will finalize the logout process by removing the login session, etc. It must be one of the URLs that you added to the External App settings as Post logout redirect URIs.state
: This is the state ID that will be passed to the post-logout URL through the query string.
Once you have implemented the post-logout endpoint, it's a good idea to redirect the user to the home or login page.
Making API Calls
Once you have successfully obtained an access token, you can proceed with making calls to the Customer's Canvas API by utilizing the Bearer Token. This can be accomplished by adding an Authorization
header to your request, which should look like the following:
GET https://api.customerscanvashub.com/api/...
Authorization: Bearer <YOUR ACCESS TOKEN HERE>
As long as you communicate with the API within the permissible scopes, and the token is not expired, Customer's Canvas will readily accept your tokens.