GraphQL APIs#
The Curity Identity Server has the following GraphQL APIs:
- a sub-endpoint of the Dynamic Client Registration endpoint to work with DCR clients.
- an endpoint under each Token Service where a Data source has been configured for Database Clients.
- the Granted Authorization GraphQL endpoint for managing granted authorization on a Token Service.
- the User Management GraphQL endpoint. It contains a schema to manage user accounts as graph data, as well as buckets.
Using Access Tokens#
In order to work with the GraphQL API an access token needs to be obtained. The authorization manager configured on the corresponding profile defines what the token needs to contain in order to gain access.
Most commonly, the access token needs to contain a certain scope to pass the first authorization step, and if the groups authorization manager is used it also needs to contain the groups claim with a user group that matches one of the rules in the authorization manager.
Normally, no groups are added to an access token, regardless of the user-account’s attributes. For this reason, it is usually necessary to map the groups claim for the access token, as explained in the User Management with GraphQL Tutorial.
Introspecting the Schema#
The GraphQL schema can be obtained from the GraphQL endpoint. However it is required to provide an access token with at least read access to see the schema.
Most GraphQL tools allow you to configure the headers sent when accessing the endpoints.
Example of adding an access token to GraphiQL
After this is done, the tool will be able to introspect the endpoint and the schema should become visible.
The schema exposes two top level types: Query and Mutation. The Query is used when reading data and Mutation when updating.
Top level schema in GraphiQL
Query schema in GraphiQL
It can be desirable to disable this feature in production for security reasons. There’s a configuration setting to do that for all the GraphQL endpoints, see Configuration Reference.
GraphQL Errors#
The GraphQL APIs provided by the Curity Identity Server represent errors according to the GraphQL specification. In addition to what is defined by that specification, the extensions object may contain a classification key with a stable identifier for the error type. See the documentation for specific GraphQL APIs and operations for more information on the extensions.classification usage and values.
The content of the message key in a GraphQL error object contains a “string description of the error intended for the developer as a guide to understand and correct the error”, as defined by the GraphQL specification. Unless documented otherwise, the content of the message key is not ready to be presented in an end-user interface (e.g. it is not localized), can change between the Curity Identity Server versions, and can change depending on the Curity Identity Server configuration, namely the setting controlling the exposure of detailed error messages.
Using Queries#
There are two types of queries, singular and plural; to obtain a single account or multiple.
query SingleAccount {
accountByUserName(userName: "johndoe") {
id
userName
emails {
primary
value
}
}
}
{
"data": {
"accountByUserName": {
"id": "b626d5d206f64165bb5c14a843545c9d",
"userName": "johndoe",
"emails": [
{
"primary": true,
"value": "john.doe@example.com"
}
]
}
}
}
When reading multiple resources, the system provides cursor based pagination to be able to get partial results. It’s also possible to filter the result.
query AllAccounts{
accounts(sorting: { sortBy: created, sortOrder: ASCENDING } ) {
edges {
node {
id
userName
emails {
value
primary
}
}
}
pageInfo {
endCursor
hasNextPage
}
totalCount
}
}
{
"data": {
"accounts": {
"edges": [
{
"node": {
"id": "1e99fb8210a944f1b6069a0cfe1c59c5",
"userName": "bcryptUser",
"emails": [
{
"value": "bcrypt.user@example.com",
"primary": true
}
]
}
}
],
"pageInfo": {
"endCursor": null,
"hasNextPage": false
},
"totalCount": 13
}
}
}
The API loosely follows the definitions in relay.dev for pagination. Most concepts in the GraphQL APIs should be familiar to anyone experienced with GraphQL APIs.
Mutations#
All Graphql requests (queries and mutations) can be sent over HTTP GET or POST methods. However, mutations should be sent using the HTTP POST method only, as those requests can contain sensitive data. Accepting mutations over HTTP GET can be disabled for all the GraphQL endpoints using a configuration setting, see Configuration Reference.
Mutation Errors#
When a GraphQL mutation fails due to data source constraints violation, a GraphQL error message is returned. The errors.extensions.field element contains the details about the attribute causing the violation.
{
"errors": [
{
"message": "The username provided is already registered",
"locations": [],
"extensions": {
"classification": "constraint-violation",
"field": "userName"
}
}
]
}
For Database Clients, warnings can also be returned. They are reporting issues that should be better to address, but that did not prevent the requested operation to succeed. They are returned in the database client’s metadata, in the warnings field.
{
"data": {
"updateDatabaseClientById": {
"client_id": "db-client-one",
"meta": {
"warnings": {
"user_authentication.allowed_authenticators": "The following authenticators are present in the client 'db-client-one' but not in the profile: [authenticator-X]"
}
}
}
}
}
DynamoDB limitations#
Ad-hoc querying and sorting does not fit well into a database like DynamoDB, which is typically designed for a well-known set of access patterns. Due to this, the DynamoDB data source presents some limitations when used with the User Management, Dynamic Client Registration and Database Client services.
Filtering and searching for attributes also suffers from the case sensitive nature of the DynamoDB. E.g.: this means when one is searching for an attribute the searching term has to match in uppercase and lowercase characters.
User Management service limitations#
- The GraphQL
accounts(...)query does not support thesortingattribute on DynamoDB. Sort is instead based on the provided filter and always uses ascending order:- When filtering by
userName, results are sorted byuserNamein ascending UTF-8 byte order. - When filtering by
email, results are sorted byemailin ascending UTF-8 byte order.
- When filtering by
- The GraphQL
accounts(...)query only supports theSTARTS_WITHfilter type for thefilteringattribute, theENDS_WITHfilter type is not supported. - The GraphQL
accounts(...)query supports listing without thefilteringattribute only when table scans are allowed. In which case, the results are not sorted. - No strong consistency is provided due to the eventually consistent nature of DynamoDB.
Dynamic Client Registration service limitations#
- No strong consistency is provided due to the eventually consistent nature of DynamoDB.
- The GraphQL
dynamicallyRegisteredClients(...)query supports listing withouttemplateClientnoruserNameattributes only when table scans are allowed. In which case, the results are not sorted. - When sorting by
name, theclient_idis used instead of the client’s name.
Database Client limitations#
- No strong consistency is provided due to the eventually consistent nature of DynamoDB.
- Sorting is supported by one column only, the client’s
name. - Searching by client ID and/or Name utilising CONTAINS method is implemented using filters.
MongoDB limitations#
Starts With Filter Type#
The GraphQL accounts(...) and databaseClients(...) query supports the STARTS_WITH filter type for the filtering attribute with a minimum of 3 characters in the filter value. Thus allowing to perform indexed search with this operator. The ENDS_WITH filter type is supported but will execute the query using a collection full-scan.
GraphQL error for unsupported features#
When a GraphQL query uses an unsupported feature, a GraphQL error message is returned. The errors.extensions attribute contains the details about the unsupported feature.
{
"errors": [
{
"message": "At least 1 filter key must be defined",
"locations": [],
"extensions": {
"classification": "operation-not-supported",
"not-supported-cause": "FILTERING_ABSENT"
}
}
]
}
The possible not-supported-cause extension attribute values are listed in the table below:
| Cause | Message |
|---|---|
| FILTERING_ABSENT | At least 1 filter key must be defined |
| FILTERING_ENDS_WITH | Filtering operator ends_with is not supported |
| FILTERING_FILTER_TOO_SMALL | The filter value does not contain enough characters to perform a starts_with search |
| SORTING | Sorting is not supported |