GraphQL APIs

The Curity Identity Server has the following GraphQL APIs:

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.

Note

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.

../../_images/setup-token.png

Fig. 197 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.

../../_images/schema-1.png

Fig. 198 Top level schema in GraphiQL

../../_images/schema-2.png

Fig. 199 Query schema in GraphiQL

Using Queries

There are two types of queries, singular and plural; to obtain a single account or multiple.

Listing 283 Querying for an account by username
query SingleAccount {
  accountByUserName(userName: "johndoe") {
    id
    userName
    emails {
      primary
      value
    }
  }
}
Listing 284 Response when querying for an account by username
{
  "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.

Listing 285 Querying for multiple accounts
query AllAccounts{
  accounts(sorting: { sortBy: created, sortOrder: ASCENDING } ) {
    edges {
      node {
        id
        userName
        emails {
          value
          primary
        }
      }
    }
    pageInfo {
      endCursor
      hasNextPage
    }
    totalCount
  }
}
Listing 286 Response when querying multiple accounts
{
  "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.

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.

Listing 287 Error when datasource already contains a user with provided userName attribute
{
    "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.

Listing 288 Warning returned about provided authenticators
{
    "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 the sorting attribute on DynamoDB. Sort is instead based on the provided filter and always uses ascending order:
    • When filtering by userName, results are sorted by userName in ascending UTF-8 byte order.
    • When filtering by email, results are sorted by email in ascending UTF-8 byte order.
  • The GraphQL accounts(...) query only supports the STARTS_WITH filter type for the filtering attribute, the ENDS_WITH filter type is not supported.
  • The GraphQL accounts(...) query supports listing without the filtering attribute 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 without templateClient nor userName attributes only when table scans are allowed. In which case, the results are not sorted.
  • When sorting by name, the client_id is 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.

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.

Listing 289 Error when filtering attribute is absent and table scans are disabled
{
    "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