Authorization
Last updatedGraphQL Access Token#
An access token is a credential that is bound to certain set of permissions. The set of permissions is decided during token generation. It is not bound to any specific user by the application, but it might be issued with specific user in mind.
Access token has an obligatory expiration time after which it will no longer authorize any requests.
Obtaining Access Token via AMS#
For GraphQL access you need user token with correct permissions. This could be done in the backend AMS. Navigate to System -> Api Tokens
and then add a new token by clicking + Integration API TOKEN
button.
Here we are able to provide restrictions, select permissions, and expiration time. Requirements for generating token are:
- Providing description (it is a good practice to provide a description that allows unambiguous token identification)
- At least one permission
Expiration time is optional - the default value equals 30 days.
Token Revocation#
Access tokens can be revoked in the AMS when necessary. Navigate to System -> Api Tokens
and select the token that you want to invalidate.
This is the moment when good practice of naming tokens unambiguously pays off. When token
details are displayed use the X Revoke
button.
Authorizing Requests#
Header#
One way to authorize the request is to provide an Authorization
header:
1
2
3
POST *base*/graphql
Authorization: Bearer <access token>
CURL example:
1
2
3
4
5
curl "${BASE_URL}/graphql" \
-X POST \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"query":"{ __schema { types { name } } }"}'
Cookie#
Another way to authorize request to GraphGL API is to add a cookie named graphql-access
with only the access token as value.
1
2
3
POST *base*/graphql
Cookie: graphql-access=<access token>
CURL example:
1
2
3
4
5
curl "${BASE_URL}/graphql" \
-X POST \
-H "Cookie: graphql-access=${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"query":"{ __schema { types { name } } }"}'
For instructions on how to attach a header or cookie in your API client refer to the client's documentation.
Permissions#
The list of permissions is changing as new permissions are added to match new queries and mutations.
During your integration's initial tests in QA, it's worth to note all the information returned in extensions.permissionsUsed
, so that you know exactly which permissions are required for use cases covered in your integration. This way you can later use API tokens with minimal permissions when you move to Production, which we highly recommend. Running Production integrations on tokens with full admin permissions is considered bad practice, and a potential security vulnerability.
If you call the API without required permissions, you will be informed about this explicitly:
Request:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
orderConnection(
last: 10, before: "bnVtYmVyOjE2Ng==", where: {storeType: WHOLESALE}
)
{
totalCount
pageInfo{hasPreviousPage, hasNextPage, startCursor, endCursor}
edges{
node{
number
status
grandTotal{
value
currency {code}
}
orderDate
}
cursor
}
}
}
Response:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"errors": [
{
"message": "You need Order:read permission to access orderConnection.",
"extensions": {
"category": "authorization"
},
"locations": [
{
"line": 6,
"column": 3
}
],
"path": [
"orderConnection"
]
}
],
"extensions": {
"complexity": 50,
"permissionsUsed": [
"Order:read"
]
}
}
Deprecations in the latest GQL API versions#
We're making some changes to our GraphQL Integration API because we want it to reflect business concepts better and need to align the naming.
We are doing our best not to introduce breaking changes so that existing queries still work, but you can switch to using new names at any time. For example, when a field is renamed, a new field is added, and the old one still works. When a returned type changes, the old type is turned into an interface so that fragments explicitly specifying types are not broken.
Moreover, we are now returning a list of deprecated fields used under extensions, so you can see exactly whether your queries use deprecated fields and when they will be deleted.
Example response:
1
2
3
4
5
6
7
8
9
10
11
12
{
"data": {
...
},
"extensions": {
"deprecatedFieldsUsed": [
"Field: Query.displays, reason: Use ObjectWithTranslations instead of Localizable, date of removal: 2023-09-04",
"Field: Display.localized, reason: Renamed localized to translations, date of removal: 2023-09-04",
"Field: LanguageTranslation.translations, reason: Renamed to fields, date of removal: 2023-09-04"
],
}
}
User warnings - not required, but important!#
GraphQL mutations return HTTP 200 response, and the way issues are communicated is through the userErrors
field in payloads. However, not all issues are equal, and some non-critical ones actually don't prevent mutations from succeeding.
This is especially important for batch actions like price updates (setPrices), where it is really important to save all other prices rather than failing because of one that is wrong for some trivial reason, e.g.:
- "Duplicate product ID 123 skipped"
- "Duplicate variant ID 456 skipped"
- "Product variant with id 789 not assigned to product 123"
- "Product ID 345 is a bundle with dynamic price type, changing its prices has no effect, skipped"
Sometimes warnings are purely informative, like "Weight unit has been changed from KILOGRAMS to POUNDS", or "Weight has been rounded to 3 decimal places".
userErrors
from now on contain only errors, and userWarnings
- all other issues. They both have a message and a path.
1
2
3
4
5
6
7
8
9
10
11
12
mutation updateProductWeight {
updateProduct(id: 1, input: {
weight: {
value: 11
unit: POUNDS
}
}) {
product { id, weight { formattedValue } }
userErrors { message path }
userWarnings { message path } # NEW!
}
}
It makes sense to add userWarnings
to all mutations, even if you don’t expect anything like mentioned above. New warnings can be added in the future.