Integrate with your own API
Learn about integration, authentication, and local development when using your own APIs.
Developing customizations might require you to have your own backend server. For example, to store sensitive credentials for third-party services, performing custom server logic, etc.
Previously, you learned that customizations must use the Merchant Center API to make requests to any of the Composable Commerce APIs. This is because the Merchant Center API handles the authentication and authorization logic required to access those APIs.
Similarly, a Merchant Center customization can rely on the Merchant Center API to connect to your own server on behalf of the client-side application. Your server can then verify the validity of the request coming from an authenticated user in the Merchant Center.
Proxying requests to the external API
To validate that requests originate from an authenticated user, the Merchant Center API Gateway provides an endpoint that must be used to connect to your external API in a secure manner.
/proxy/forward-to
HTTP headers
Requests to the /proxy/forward-to
endpoint should additionally pass the following HTTP headers:
Accept-version
(required): see Versioning.X-Forward-To
(required): set it to the external API URL.X-Forward-To-Audience-Policy
(required): see Configuring the audience policy.X-Forward-To-Claims
(optional): see Configuring custom claims.X-Project-Key
(required): the key of the commercetools Project currently being used by the customization. The Merchant Center API Gateway will perform a validation check to ensure that the user has access to the Project, then forward the request to your server only if the check was successful. The project key can be retrieved from the Application context.
To facilitate the configuration of these headers we provide some helpers when configuring the request with an HTTP client. For more information, see Data fetching.
Configuring the audience policy
This feature is available from version 21.8.0
onwards.
The audience policy can be used to determine how the audience
value is exchanged between the Merchant Center API Gateway and the external API.
Supported values:
forward-url-full-path
: sets theaudience
using the full URL (origin + path). This is the default policy.forward-url-origin
: sets theaudience
using only the origin URL part.
For example, given the forward-to URL https://my-customization.com/api/123
, the audience
for each policy is determined as following:
forward-url-full-path
:https://my-customization.com/api/123
forward-url-origin
:https://my-customization.com
Configuring custom claims
Use the X-Forward-To-Claims
header to make the Merchant Center API Gateway include extra public claims in the exchange token sent along with forwarded requests. The value must be a list of the claims separated by a space.
Supported claims:
permissions
: includes the list of applied permissions for the logged-in user. The permission values are formatted ascan<permission-name>
, for example,canViewChannels
."X-Forward-To-Claims": "permissions"This feature is available from version
21.14.0
onwards.
Learn more about using this header in the following sections.
Custom HTTP headers
This feature is available from version 20.2.0
onwards.
You can send HTTP headers specific to the external API by prefixing them with x-forward-header-
.
{headers: {'x-forward-header-x-foo': 'bar'}}
The Merchant Center API will remove the prefix before forwarding the request to the external API.
Usage for Apollo
We can leverage the context
option for Apollo queries to adjust how the request is configured and executed. The @commercetools-frontend/application-shell
package exposes a utility function to configure the Apollo context for the /proxy/forward-to
usage. See createApolloContextForProxyForwardTo
.
Usage for SDK actions
By default, all requests with the SDK actions are configured to be sent to the Merchant Center API Gateway.
When making requests to an external API using the SDK actions, you can use the forwardTo
object, which wraps the normal action creators and configures them with the required HTTP headers.
actions.forwardTo.get({ uri: 'https://my-customization.com/graphql' });actions.forwardTo.del({ uri: 'https://my-customization.com/graphql' });actions.forwardTo.head({ uri: 'https://my-customization.com/graphql' });actions.forwardTo.post({uri: 'https://my-customization.com/graphql',payload: { say: 'Hello' },});// You can also pass custom HTTP headers, for example:actions.forwardTo.get({uri: 'https://my-customization.com/graphql',headers: {'x-foo': 'bar',},});// To change the audience policy:actions.forwardTo.get({uri: 'https://my-customization.com/api',audiencePolicy: 'forward-url-origin',});// To make the MC API Gateway include the user permissions in the JWT exchangeactions.forwardTo.get({uri: 'https://my-customization.com/api',includeUserPermissions: true,});
As a reminder, you can dispatch actions using the useAsyncDispatch
React hook. For more information, see Data fetching for REST HTTP clients.
Usage for custom HTTP clients
This feature is available from version 21.11.0
onwards.
When using a custom HTTP client you can pass an additional configuration object forwardToConfig
. For more information, see executeHttpClientRequest
.
Authenticating requests from the external API
When a valid request is sent via the /proxy/forward-to
endpoint, the Merchant Center API Gateway forwards the request to the external API with an Authorization: Bearer <token>
HTTP header.
The bearer token is a short-living JSON Web Token (JWT) that is used exclusively for the authorization exchange between the Merchant Center API Gateway and the external API. The token is valid for 60 seconds.
The external API must verify that the token is valid by using the JSON Web Key Set endpoint.
It is your responsibility to ensure that the external API is securely protected and that only authorized users of a Project are able to connect to the API.
Exchange token structure
The exchange token follows the JWT structure, specified by the JWT specification. This means that the exchange token contains claims which are statements about the token and are used to assert the token itself.
The exchange token structure consist of Registered Claim Names and Private claims:
{"exp": 1600092401,"sub": "<subject>","iss": "<hostname>","aud": "<audience-url>","type": "exchange","<hostname>/claims/project_key": "<project-key>","<hostname>/claims/user_permissions": ["can<permission-1>","can<permission-2>"],"iat": 1600092341}
exp
: the expiration time on or after which the exchange token must not be accepted for processing.sub
: the subject identifier is the unique user identifier of a Merchant Center account, specifically the ID of the logged-in user.iss
: the issuer identifier is the URL identifier of the server that issued the JWT. In this case, it is the URL of the Merchant Center API Gateway.aud
: the audience is the value that the exchange token is intended for. The value depends on the audience policy.iat
: issued at represents the time at which the JWT was issued.type
: this value is a private claim and is always set toexchange
.<iss>/claims/project_key
: represents the Project key associated with the request. The claim is a public claim prefixed by a collision-resistant namespace, as per the JWT specification.<iss>/claims/user_permissions
(optional): contains a list of permissions names that are applied to the logged-in user. For more information, see Configuring custom claims. The claim is a public claim prefixed by a collision-resistant namespace, as per the JWT specification.
Validating the JSON Web Token
To facilitate the implementation of validating the JWT from the Authorization: Bearer <token>
HTTP header, we provide a package with built-in functions and helpers to perform the heavy work.
npm install --save @commercetools-backend/expressyarn add @commercetools-backend/express
Usage for Express.js
The package includes an async Express.js middleware that performs the token validation and assigns a session
object to the request
object.
type TSession = {userId: string;projectKey: string;userPermissions?: string[];};
The middleware requires some options:
audience
(String): the public-facing URL of your API server. The value should only contain the origin URL (protocol, hostname, port). The request path is inferred from the incoming request.For example, given the external API is hosted at
https://my-api-server.com
, theaudience
value must be set tohttps://my-api-server.com
.audiencePolicy
(String): see audience policy.issuer
(String): either a cloud identifier or a valid URL to the Merchant Center API Gateway.inferIssuer
(Boolean): determines whether the issuer should be inferred from the custom request HTTP headerx-mc-api-cloud-identifier
which is sent by the Merchant Center API Gateway when forwarding the request. This might be useful in case the server is used in multiple regions. Default:false
jwks
(Object): see options ofjwks-rsa
.getRequestUrl
(Function): returns the URL (URI path + query string) of therequest
to validate the audience. Use this in case therequest
object does not contain either anoriginalUrl
or anurl
property (see the following serverless example for using AWS Lambda functions).type function getRequestUrl(request: Request): string;
You can use the middleware as following:
const express = require('express');const {createSessionMiddleware,CLOUD_IDENTIFIERS,} = require('@commercetools-backend/express');const app = express();app.use(createSessionMiddleware({audience: 'https://my-api-server.com',issuer: CLOUD_IDENTIFIERS.GCP_EU,}));app.use((request, response, next) => {// `request.session` contains the useful information});
Usage for Serverless Functions
The package also exposes an async createSessionAuthVerifier
factory function that can be used directly instead of the middleware. In fact, the middleware is just a thin wrapper around this function.
The options to configure the function are the same as the Express.js middleware.
The following example validates the JWT for Google Cloud Functions:
const {createSessionAuthVerifier,CLOUD_IDENTIFIERS,} = require('@commercetools-backend/express');const sessionAuthVerifier = createSessionAuthVerifier({audience: 'https://my-api-server.com',issuer: CLOUD_IDENTIFIERS.GCP_EU,});exports.handler = async function (request, response) {try {await sessionAuthVerifier(request, response);} catch (error) {response.status(401).send(JSON.stringify({ message: 'Unauthorized' }));return;}// `request.session` contains the useful information};
Other cloud providers might not use a standard Node.js/Express request
object. For example, AWS Lambda functions receive an event
object with different properties.
In these cases, you need to implement the getRequestUrl
function to correctly map the expected URL (URI path + query string).
The following example shows how to map an AWS Lambda function event:
const {createSessionAuthVerifier,CLOUD_IDENTIFIERS,} = require('@commercetools-backend/express');const sessionAuthVerifier = createSessionAuthVerifier({audience: 'https://my-api-server.com',issuer: CLOUD_IDENTIFIERS.GCP_EU,// This example assumes that the `event` is based on Lambda v2 of the payload format.getRequestUrl: (event) =>`${event.rawPath}${event.rawQueryString ? '?' + event.rawQueryString : ''}`,});exports.handler = async function (event, context) {try {await sessionAuthVerifier(event, context);} catch (error) {return {statusCode: 401,body: JSON.stringify({ message: 'Unauthorized' }),};}// `event.session` contains the useful information};
Using JSON Web Key Set endpoint
The Merchant Center API Gateway exposes a /.well-known/jwks.json
endpoint that can be used to validate JWTs, as defined in OpenID Connect (OIDC) specification. The endpoint provides a rotating public key used to verify the JWT signature. Additionally, there is also a /.well-known/openid-configuration
discovery endpoint.
To view the list of available hostnames for the Merchant Center API Gateway, see hostnames.
To learn more about JSON Web Key Set, see the following external documentation pages:
The previously described setup is not needed when using the @commercetools-backend/express
package which already contains the necessary setup.
Local development using a secure tunnel
Developing a customization locally, together with an external API, has some limitations.
For example, using the /proxy/forward-to
endpoint of the Merchant Center API Gateway with a target URL pointing to http
is not allowed. Only https
targets are allowed.
The most straightforward solution is to use the production URL of the external API, which may be inconvenient in the development process.
Therefore, some kind of secure tunnel solution can be used to develop both the customization and external API together, locally. Using a secure tunnel ensures that the URL used to connect to the API via the /proxy/forward-to
endpoint is a valid remote URL with https
protocol.
ngrok
One of the most popular secure tunnel solutions is ngrok, which is a reverse proxy that establishes an encrypted tunnel between the ngrok
agent application running locally and the upstream service.
You can sign up and download the agent application from the ngrok website. Once installed, start the agent application by running the following command:
ngrok http <desired_port>
<desired_port>
is the port bound to your server application (for example, 6000
).
The exposed external URL of the created tunnel:
The secure tunnel provides a unique URL that points to the local HTTP server.
The URL https://<identifier>.ngrok.io
can now be used as the URL of the server to be forwarded to via the Merchant Center API /proxy/forward-to
.
This is also the value of the audience
to be configured in your session middleware when using the @commercetools-backend/express
package.
const express = require('express');const {createSessionMiddleware,CLOUD_IDENTIFIERS,} = require('@commercetools-backend/express');const app = express();app.use(createSessionMiddleware({audience: 'https://33ea-87-183-162-9.eu.ngrok.io',issuer: CLOUD_IDENTIFIERS.GCP_EU,}));app.post('/echo', (req, res) => {res.send('It works 🙌');});app.listen(6000, () => {console.log('Running on port 6000.');});module.exports = app;
We recommend that you configure the external API URL using environment variables to be able to define different values for development (using a secure tunnel) and production.
To do this, specify the external API URL as an additionalEnv
property using a variable placeholder in your Custom Application config or Custom View config.
{"additionalEnv": {"externalApiUrl": "${env:EXTERNAL_API_URL}"}}
In the application code you can then access the value using the application context.
A monitoring web application running at http://localhost:4040/
is available when running the ngrok
agent, which can be helpful to debug the incoming HTTP requests.
Cloudflare Tunnel
As an alternative, you can use the Cloudflare Tunnel service.
After installing the cloudflared
application and running it:
cloudflared tunnel --url http://localhost:<desired_port>
The server application can be reached via the provided URL:
The same setup as ngrok
applies here as well.
Versioning
The /proxy/forward-to
endpoint is versioned using the Accept-version
HTTP header.
Versions follow an incremental number in the format v1
, v2
, etc.
The default version of the API may change in the future. If you're building a customization and want to use stable APIs, we recommend that you request a specific version in the Accept-version
HTTP header.
Current and deprecated versions
The current version refers to the default version used by the API, if no Accept-version
header is provided.
When we introduce a new version, the default version enters a deprecation period up to 12 months, after which the version is marked as deprecated and the new version becomes the default version.
The following table lists all the supported versions and the possible start date for their deprecation period:
Version | Is default | Deprecated from |
---|---|---|
v2 | ||
v1 | ✅ | 2020-08-25 |
Migrating to new versions
We want to make the migration to a new version as simple as possible and abstract away the changes into the packages provided by the application-kit repository.
For instance, changes related to v2
are all included in the package @commercetools-backend/express
and the data fetching components improvements.
If you use those packages, you only need to follow the instructions in the release notes to update to new versions for the packages.