Graph Identities

The purpose of this technote is to demonstrate queries/mutations for identifying an actor attempting to use the graph.


This article demonstrates queries and mutations for identifying an actor attempting to use the graph. These examples can be useful for inspiring how the actor identity may be described in a GraphQL Schema and includes multiple use-cases for both users and services.

Our use-case identifies authenticated users and services as well as anonymous users.

First we define interfaces for both query and mutation identity types.

GraphQL
1interface Identity {
2  "An ID we can use to identify the session."
3  id: ID!
4}
5
6interface MutableIdentity {
7  "An ID we can use to identify the session."
8  id: ID!
9}

Then we create concrete types of these interfaces for our authenticated and unauthenticated identities.

GraphQL
1type AnonymousIdentity implements Identity @key(fields: "id") {
2  "An ID we can use to identify the session."
3  id: ID!
4}
5
6type UserIdentity implements Identity @key(fields: "id") {
7  "An ID we can use to identify the session."
8  id: ID!
9  "A reference to the authenticated user entity."
10  user: User
11}
12
13type ServiceIdentity implements Identity @key(fields: "id") {
14  "An ID we can use to identify the session."
15  id: ID!
16  "A reference to the authenticated service entity."
17  service: Service
18}

As well as an authentication error response.

GraphQL
1type NotAuthenticatedError {
2  message: String
3}

Next, we will construct our query type and response unions. We have selected unions here so that we are able to return both identities and errors as response data.

Nullability becomes important here; there is nuance around how nullability affects the way in which clients will need to handle failures.

GraphQL
1union AuthIdentity = UserIdentity | ServiceIdentity | NotAuthenticatedError
2union CurrentUser = UserIdentity | AnonymousIdentity
3
4type Query {
5  "A query which returns an authenticated user or service, or an anonymous identity."
6  session: Identity!
7  "A query which returns the currently authenticated identity or an error."
8  whoami: AuthIdentity!
9  "A query which returns the currently authenticated user identity, or an anonymous identity."
10  me: CurrentUser!
11}
⚠️ caution
In the previous example, none of the query response types are nullable, meaning the request will fail if the server is unable to provide some kind of Identity. Alternatively we could allow nullable responses which could be used to represent the anonymous or unauthenticated use-case.Additionally, by providing a NotAuthenticatedError as part of the response union, we can inform the client applications when to serve specific experiences to the user.

Then by separating the mutation and query identities we are able to separate:

  • Actions that can be performed on identities.

  • Queries which can be performed on identities.

GraphQL
1type MutableAnonymousIdentity implements MutableIdentity @key(fields: "id") {
2  id: ID!
3  authenticateWithEmailAndPassword(
4    credentials: EmailAndPasswordCredentials!
5  ): UserIdentity!
6}
7
8type MutableUserIdentity implements MutableIdentity @key(fields: "id") {
9  id: ID!
10  resetPassword(credentials: ResetPasswordCredentials!): UserIdentity!
11}
12
13type MutableServiceIdentity implements MutableIdentity @key(fields: "id") {
14  id: ID!
15  service: MutableService!
16}
17
18union MutableCurrentUser = MutableUserIdentity | NotAuthenticatedError
19
20type Mutation {
21  session: MutableIdentity!
22  me: MutableCurrentUser!
23}

Finally, operations using the previously defines schema would look like the following.

GraphQL
1query CurrentAuth {
2	session {
3		id
4	}
5	whoami {
6		id
7	}
8	me {
9		id
10	}
11}
12
13mutation Login($credentials: EmailAndPasswordCredentials!) {
14	session {
15		... on MutableAnonymousIdentity {
16			authenticateWithEmailAndPassword(credentials: $credentials) {
17				user {
18					...
19				}
20			}
21		}
22	}
23}
24
25mutation ResetPassword($credentials: ResetPasswordCredentials!) {
26	me {
27		... on MutableUserIdentity {
28			resetPassword(credentials: $credentials) {
29				user {
30					...
31				}
32			}
33		}
34	}
35}