Reindex
TeamBlogDocs

Authentication

Authentication allows you to know who is the current user, to log user in and to register new users. There are two main parts of authentication system. Reindex request may contain a JSON Web Token (JWT), that holds the credentials of the user making the request. In addition, Reindex provides a way to authenticate new or existing users via one of social login providers (e.g. Google), so that user can receive an appropriate JWT. Such users are also stored as an object of User type.

JWT can be used without social login and even without the User type. For instance, an admin token that you have received is a JWT that does not include a user ID, but just a flag that tells the user is an admin instead. Similarly, you can generate your own tokens if you’d want to integrate custom login system with Reindex.

JSON Web Tokens

Every request to Reindex GraphQL API may contain a Authorization header with a JSON Web Token inside. JSON Web Token is a JSON object, that is Base64 encoded and cryptographically signed by a secret key. Reindex can verify the signature and see that the token is valid. Reindex will then use the information passed in token for authentication.

We support sub (Subject), isAdmin, exp (Expiration Time) and iat (Issued At) claims.

If sub is present it must be a valid ID of User object. isAdmin is a boolean and determines if a user has admin permissions. Both exp and iat is a number of seconds since Unix epoch.

ReindexSecret

The secret key that is used to sign the token is stored in an object of type ReindexSecret. createReindexSecret and deleteReindexSecret allow you to create new secret keys and delete existing ones. There is currently no way to choose what secret is used for signing, so unless you generate the token manually, there is little reason to have more than one secret.

You may manually create JWT with the secret using one of the JWT libraries or even through jwt.io website. You should use HS256 as a signing algorithm.

Social login authentication providers

Reindex provides a built-in facility to log user in, using one of the supported authentication providers. Supported providers are:

Using those providers requires configuring them at the provider’s website. Refer to the documentation of each authentication provider for details on how to create an app in their system. When you’re asked to provide a callback URL you should use https://NAME-OF-YOUR-APP.myreindex.com/auth/PROVIDER_NAME. After the configuration you should get two IDs, client or app ID and client or app secret. This configuration should be stored in Reindex ReindexAuthenticationProvider type. You can create one, for example for Google, with the following GraphQL query:

mutation {
  createReindexAuthenticationProvider(input: {
    type: google,
    clientId: "YOUR-GOOGLE-CLIENT-ID",
    clientSecret: "YOUR-GOOGLE-CLIENT-SECRET",
    isEnabled: true
  }) {
    changedReindexAuthenticationProvider {
      clientId,
      id,
      clientSecret,
      type,
      isEnabled
    }
  }
}

Tutorial has a detailed guide on how to enable Google authentication.

After the provider is configured, users can login using a selected provider. The logic of cross-domain OAuth communication is pretty complicated, but reindex-js provides a shortcut. Refer to reindex-js documentation.

Custom scopes

When a user authenticates using one of the social login providers, the app will obtain an access token that can be used to securely make requests to the API of the provider. The access token is stored in the accessToken field of the user credentials object and you can use it to request more data in your own server-side code, for example Facebook friends or GitHub repositories.

In order to fetch additional data, you may need to ask additional permissions (OAuth scopes) during the authentication. These scopes can be configured using the scopes field of ReindexAuthenticationProvider, which takes a list of scope strings. If not defined, default scopes for each provider will be used.

You can find the list of supported scopes from the developer documentation of each provider:

(Twitter provider does not have custom scopes.)

For example, to configure Facebook login to request the user_likes permission in addition to the email permission (included by default) set up a ReindexAuthenticationProvider object like this:

{
  type: facebook,
  clientId: "YOUR-FACEBOOK-APP-ID",
  clientSecret: "YOUR-FACEBOOK-APP-SECRET",
  isEnabled: true,
  scopes: ["email", "user_likes"]
}

Login with Auth0 Lock

Auth0 also provides an embeddable login form Lock that makes it easy to add a polished login experience to any web or native app. You can use Lock for authenticating with Reindex using the loginWithToken mutation that takes an Auth0 id_token as an input and returns a Reindex token for a signed in user.

To set up login with Lock, first create an authentication provider with your Auth0 credentials (in GraphiQL):

mutation {
  createReindexAuthenticationProvider(input: {
    type: auth0,
    clientId: "AUTH0-CLIENT-ID",
    clientSecret: "AUTH0-CLIENT-SECRET",
    domain: "AUTH0-DOMAIN",
    isEnabled: true,
  }) {
    id
  }
}

Then add Lock in your app and call loginWithToken in the callback function for Lock:

const reindex = new Reindex(reindexURL);
const lock = new Auth0Lock(auth0ClientID, auth0Domain);
lock.show((error, profile, id_token) => {
  reindex.loginWithToken('auth0', id_token);
});

That’s all! You can now enable any Auth0 connection and your users can login to your Reindex app with it. And as with other Reindex authentication providers, the user profile in Reindex is automatically updated on every login from Auth0.

User type

Social login stores the data about the user inside User nodes. The field credentials contains fields with a corresponding provider name ( google, facebook, twitter, github) and information received from the provider. Credentials can only be read by the user themself or admins. If you want to display some of this information to other users, you should copy it to some other user field.

A new user is created on the first time the user signs in using a provider. After that the same user object is used.

Unlike other built-in types, you can extend User type and thus it’s included in your schema. It’s not possible to delete the User type, though.

Copying provider’s user information to public fields

Very often you would want to display some user information, like their username and email, that are available from the social login providers. Credentials are only readable by the user, so you need to copy that data to some of your defined fields. Let’s assume that the user type has fields username and avatar and we want to copy them from one of the user’s social login profiles.

  1. Copying as a Relay mutation
import Relay from 'react-relay'

export default class UpdateUserInfoMutation extends Relay.Mutation {
  static fragments = {
    user: () => Relay.QL`
      fragment on User {
        id
        credentials {
          github {
            displayName
            picture
          }
          google {
            displayName
            picture(size: 73)
          }
          facebook {
            displayName
            picture(height: 73, width: 73)
          }
          twitter {
            displayName
            picture(size: bigger)
          }
          auth0 {
            displayName
            picture
          }
        }
      }
    `
  };

  getMutation() {
    return Relay.QL`mutation { updateUser }`
  }

  getVariables() {
    const credentials = normalizeCredentials(this.props.user)
    return {
      id: this.props.user.id,
      username: credentials.displayName,
      avatarUrl: credentials.picture,
    }
  }

  getFatQuery() {
    return Relay.QL`
      fragment on _UserPayload {
        changedUser {
          username
          avatarUrl
        }
      }
    `
  }

  getConfigs() {
    return [
      {
        type: 'FIELDS_CHANGE',
        fieldIDs: {
          changedUser: this.props.user.id,
        },
      },
    ]
  }

  getOptimisticResponse() {
    const credentials = normalizeCredentials(this.props.user)
    return {
      changedUser: {
        username: credentials.displayName,
        avatarUrl: credentials.picture,
      }
    }
  }
}

function normalizeCredentials(user) {
  for (const provider of [
    'github', 'google', 'facebook', 'twitter', 'auth0'
  ]) {
    if (user.credentials[provider]) {
      return user.credentials[provider]
    }
  }
}

You can run this mutation in, for example, componentDidMount of your root component.

  1. Updating via direct GraphQL call, let’s assume profile contains displayName and picture.
const updateResult = await reindex.query(`
  mutation($input: _UpdateUserInput!) {
    updateUser(input: $input) {
      id
    }
  }
`, {
  input: {
    id: reindexUserId,
    username: profile.displayName,
    avatarUrl: profile.picture,
  },
});

for (const error of updateResult.errors || []) {
  console.error(error);
}
Topics:

Authentication

Authentication allows you to know who is the current user, to log user in and to register new users. There are two main parts of authentication system. Reindex request may contain a JSON Web Token (JWT), that holds the credentials of the user making the request. In addition, Reindex provides a way to authenticate new or existing users via one of social login providers (e.g. Google), so that user can receive an appropriate JWT. Such users are also stored as an object of User type.

JWT can be used without social login and even without the User type. For instance, an admin token that you have received is a JWT that does not include a user ID, but just a flag that tells the user is an admin instead. Similarly, you can generate your own tokens if you’d want to integrate custom login system with Reindex.

JSON Web Tokens

Every request to Reindex GraphQL API may contain a Authorization header with a JSON Web Token inside. JSON Web Token is a JSON object, that is Base64 encoded and cryptographically signed by a secret key. Reindex can verify the signature and see that the token is valid. Reindex will then use the information passed in token for authentication.

We support sub (Subject), isAdmin, exp (Expiration Time) and iat (Issued At) claims.

If sub is present it must be a valid ID of User object. isAdmin is a boolean and determines if a user has admin permissions. Both exp and iat is a number of seconds since Unix epoch.

ReindexSecret

The secret key that is used to sign the token is stored in an object of type ReindexSecret. createReindexSecret and deleteReindexSecret allow you to create new secret keys and delete existing ones. There is currently no way to choose what secret is used for signing, so unless you generate the token manually, there is little reason to have more than one secret.

You may manually create JWT with the secret using one of the JWT libraries or even through jwt.io website. You should use HS256 as a signing algorithm.

Social login authentication providers

Reindex provides a built-in facility to log user in, using one of the supported authentication providers. Supported providers are:

Using those providers requires configuring them at the provider’s website. Refer to the documentation of each authentication provider for details on how to create an app in their system. When you’re asked to provide a callback URL you should use https://NAME-OF-YOUR-APP.myreindex.com/auth/PROVIDER_NAME. After the configuration you should get two IDs, client or app ID and client or app secret. This configuration should be stored in Reindex ReindexAuthenticationProvider type. You can create one, for example for Google, with the following GraphQL query:

mutation {
  createReindexAuthenticationProvider(input: {
    type: google,
    clientId: "YOUR-GOOGLE-CLIENT-ID",
    clientSecret: "YOUR-GOOGLE-CLIENT-SECRET",
    isEnabled: true
  }) {
    changedReindexAuthenticationProvider {
      clientId,
      id,
      clientSecret,
      type,
      isEnabled
    }
  }
}

Tutorial has a detailed guide on how to enable Google authentication.

After the provider is configured, users can login using a selected provider. The logic of cross-domain OAuth communication is pretty complicated, but reindex-js provides a shortcut. Refer to reindex-js documentation.

Custom scopes

When a user authenticates using one of the social login providers, the app will obtain an access token that can be used to securely make requests to the API of the provider. The access token is stored in the accessToken field of the user credentials object and you can use it to request more data in your own server-side code, for example Facebook friends or GitHub repositories.

In order to fetch additional data, you may need to ask additional permissions (OAuth scopes) during the authentication. These scopes can be configured using the scopes field of ReindexAuthenticationProvider, which takes a list of scope strings. If not defined, default scopes for each provider will be used.

You can find the list of supported scopes from the developer documentation of each provider:

(Twitter provider does not have custom scopes.)

For example, to configure Facebook login to request the user_likes permission in addition to the email permission (included by default) set up a ReindexAuthenticationProvider object like this:

{
  type: facebook,
  clientId: "YOUR-FACEBOOK-APP-ID",
  clientSecret: "YOUR-FACEBOOK-APP-SECRET",
  isEnabled: true,
  scopes: ["email", "user_likes"]
}

Login with Auth0 Lock

Auth0 also provides an embeddable login form Lock that makes it easy to add a polished login experience to any web or native app. You can use Lock for authenticating with Reindex using the loginWithToken mutation that takes an Auth0 id_token as an input and returns a Reindex token for a signed in user.

To set up login with Lock, first create an authentication provider with your Auth0 credentials (in GraphiQL):

mutation {
  createReindexAuthenticationProvider(input: {
    type: auth0,
    clientId: "AUTH0-CLIENT-ID",
    clientSecret: "AUTH0-CLIENT-SECRET",
    domain: "AUTH0-DOMAIN",
    isEnabled: true,
  }) {
    id
  }
}

Then add Lock in your app and call loginWithToken in the callback function for Lock:

const reindex = new Reindex(reindexURL);
const lock = new Auth0Lock(auth0ClientID, auth0Domain);
lock.show((error, profile, id_token) => {
  reindex.loginWithToken('auth0', id_token);
});

That’s all! You can now enable any Auth0 connection and your users can login to your Reindex app with it. And as with other Reindex authentication providers, the user profile in Reindex is automatically updated on every login from Auth0.

User type

Social login stores the data about the user inside User nodes. The field credentials contains fields with a corresponding provider name ( google, facebook, twitter, github) and information received from the provider. Credentials can only be read by the user themself or admins. If you want to display some of this information to other users, you should copy it to some other user field.

A new user is created on the first time the user signs in using a provider. After that the same user object is used.

Unlike other built-in types, you can extend User type and thus it’s included in your schema. It’s not possible to delete the User type, though.

Copying provider’s user information to public fields

Very often you would want to display some user information, like their username and email, that are available from the social login providers. Credentials are only readable by the user, so you need to copy that data to some of your defined fields. Let’s assume that the user type has fields username and avatar and we want to copy them from one of the user’s social login profiles.

  1. Copying as a Relay mutation
import Relay from 'react-relay'

export default class UpdateUserInfoMutation extends Relay.Mutation {
  static fragments = {
    user: () => Relay.QL`
      fragment on User {
        id
        credentials {
          github {
            displayName
            picture
          }
          google {
            displayName
            picture(size: 73)
          }
          facebook {
            displayName
            picture(height: 73, width: 73)
          }
          twitter {
            displayName
            picture(size: bigger)
          }
          auth0 {
            displayName
            picture
          }
        }
      }
    `
  };

  getMutation() {
    return Relay.QL`mutation { updateUser }`
  }

  getVariables() {
    const credentials = normalizeCredentials(this.props.user)
    return {
      id: this.props.user.id,
      username: credentials.displayName,
      avatarUrl: credentials.picture,
    }
  }

  getFatQuery() {
    return Relay.QL`
      fragment on _UserPayload {
        changedUser {
          username
          avatarUrl
        }
      }
    `
  }

  getConfigs() {
    return [
      {
        type: 'FIELDS_CHANGE',
        fieldIDs: {
          changedUser: this.props.user.id,
        },
      },
    ]
  }

  getOptimisticResponse() {
    const credentials = normalizeCredentials(this.props.user)
    return {
      changedUser: {
        username: credentials.displayName,
        avatarUrl: credentials.picture,
      }
    }
  }
}

function normalizeCredentials(user) {
  for (const provider of [
    'github', 'google', 'facebook', 'twitter', 'auth0'
  ]) {
    if (user.credentials[provider]) {
      return user.credentials[provider]
    }
  }
}

You can run this mutation in, for example, componentDidMount of your root component.

  1. Updating via direct GraphQL call, let’s assume profile contains displayName and picture.
const updateResult = await reindex.query(`
  mutation($input: _UpdateUserInput!) {
    updateUser(input: $input) {
      id
    }
  }
`, {
  input: {
    id: reindexUserId,
    username: profile.displayName,
    avatarUrl: profile.picture,
  },
});

for (const error of updateResult.errors || []) {
  console.error(error);
}