This is the legacy documentation of Project-level Custom Applications, which is in maintenance mode. Visit the new documentation for Org-level Custom Applications.
Proxy to External API
Update: the documentation of this page has been updated to reflect the new functionalities of the proxy endpoint. It is strongly recommended that you follow and implement the new setup, for security reasons.
If you are interested in more advanced functionalities, let us know and open a support issue.
Developing Custom Applications might require you to have your own backend server, for several reasons: storing sensitive credentials for third-party services, performing custom server logic, etc.
If that's the case, you're probably facing a problem: how do you authenticate requests to your API?
Merchant Center applications can make authenticated requests using the proxy endpoints to the underlying APIs that are configured and supported for the Merchant Center. Requests are authenticated via the mcAccessToken
stored in a secure cookie, which is set only for commercetools.com
domains.
Since your backend server is hosted on a different domain, you can't send authenticated requests to your server from your Custom Application. Well, you can but you don't know if the request comes from an authenticated user or not.
Proxying requests to the external API
To be able to validate that the request comes from an authenticated user, the Merchant Center API Gateway provides an endpoint that must be used to connect to your external API in a secured manner.
/proxy/forward-to
Requests to that endpoint should additionally pass the following HTTP headers:
Accept-version: v2
: See Versioning.X-Forward-To: <url>
: The URL to your external API.X-Project-Key: <project-key>
: The key of the project currently being used by the Custom Application. 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.
To facilitate the usage of the built-in Apollo client and the SDK actions, we provide some helpers to easily integrate with the request configuration for the HTTP headers listed above.
Sending custom HTTP headers
This feature is available from version >= 20.2.0
.
In case you need to send certain HTTP headers to the external API, you can do so for both the Apollo client and the SDK actions. See the examples below on how to configure the HTTP headers for both scenarios.
All custom HTTP headers are sent to the Merchant Center API with a prefix x-forward-header-
, as it allows the Merchant Center API to allow requests with those headers to be forwarded. However, the request forwarded to the external API contains the correct headers without the prefix.
Usage for Apollo
We can leverage the context
option for Apollo queries to adjust how request is configured and executed. The @commercetools-frontend/application-shell
package now exposes an utility function to configure the Apollo context for the /proxy/forward-to
usage.
import React from 'react';import {createApolloContextForProxyForwardTo,useMcQuery,} from '@commercetools-frontend/application-shell';import { useApplicationContext } from '@commercetools-frontend/application-shell-connectors';const Fetcher = () => {// Assuming that the `custom-application-config.json` contains the custom value:// `{ additionalEnv: { externalApiUrl: 'https://my-custom-app.com/graphql'} }`const externalApiUrl = useApplicationContext(context => context.environment.externalApiUrl);const { loading, data, error } = useMcQuery(MyQuery, {context: createApolloContextForProxyForwardTo({// The URL to your external APIuri: externalApiUrl,// Optionally pass custom HTTP headersheaders: {'x-foo': 'bar',},}),});// ...};
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 the external API using the SDK actions, you can now use the forwardTo
object, which wraps the normal action creators and configures them with the required HTTP headers.
actions.forwardTo.get({ uri: 'https://my-custom-app.com/graphql' });actions.forwardTo.del({ uri: 'https://my-custom-app.com/graphql' });actions.forwardTo.head({ uri: 'https://my-custom-app.com/graphql' });actions.forwardTo.post({uri: 'https://my-custom-app.com/graphql',payload: { say: 'Hello' },});// You can also pass custom HTTP headers, for example:actions.forwardTo.get({uri: 'https://my-custom-app.com/graphql',headers: {'x-foo': 'bar',},});
As a reminder, action creators can be dispatched using the useAsyncDispatch
React hook. You can check out an example in the Playground application.
import React from 'react';import { useApplicationContext } from '@commercetools-frontend/application-shell-connectors';import { actions, useAsyncDispatch } from '@commercetools-frontend/sdk';const Fetcher = () => {const dispatch = useAsyncDispatch();// Assuming that the `custom-application-config.json` contains the custom value:// `{ additionalEnv: { externalApiUrl: 'https://my-custom-app.com/graphql'} }`const externalApiUrl = useApplicationContext(context => context.environment.externalApiUrl);React.useEffect(() => {const sendRequest = async () => {const result = await dispatch(// The URL to your external APIactions.forwardTo.get({ uri: externalApiUrl }));// do something with the result};sendRequest();}, [dispatch, externalApiUrl]);// ...};
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's imperative that the external API is securely protected and that only users with access to the project should be 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 is 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>","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 particular case is the URL of the Merchant Center API Gateway.aud
: The audience is the value that the exchange token is intended for. In this particular case is the exact URL passed to theX-Forward-To
HTTP header.iat
: This value 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
: This value represents the project key that the request is associated to. 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;};
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
.issuer
(string): Either a cloud identifier or a valid URL to the Merchant Center API Gateway. The cloud identifier maps to the Merchant Center API URL of the related cloud region.Cloud Identifier Merchant Center API Gateway URL gcp-au
https://mc-api.australia-southeast1.gcp.commercetools.com
gcp-eu
https://mc-api.europe-west1.gcp.commercetools.com
gcp-us
https://mc-api.us-central1.gcp.commercetools.com
aws-fra
https://mc-api.eu-central-1.aws.commercetools.com
aws-ohio
https://mc-api.us-east-2.aws.commercetools.com
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
.
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.
Below is an example of validating 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};
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 Open ID 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.
You can read more about JSON Web Key Set here and here.
The setup described above is not needed when using the @commercetools-backend/express
package which already contains the necessary setup.
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.
Important: The default version of the API may change in the future. If you're building a Custom Application and care about the stability of the API, be sure to 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, after which the version is marked as deprecated and the new version becomes the default version.
Feature maturity | Deprecation period |
---|---|
Beta | 4 months |
General Availability | 12 months |
The following table lists all the supported versions and the possible start date for their deprecation period:
Version | Is default | Deprecated from |
---|---|---|
v1 | ✅ | 2020-08-25 |
v2 |
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 (see Proxying requests to the external API).
If you use those packages, you only need to follow the instructions in the release notes to update to new versions for the packages.