GraphQL with Shaype authentication

GraphQL interface allows you to execute requests in the context of a logged-in end user (customer). The typical scenario where you would use this API, is to build user experience for your customers, in the form of a mobile app of web application.

This guide will explain how to access GraphQL interface using Shaype authentication provider.

📘

Note

The queries and mutations of the GraphQL interface are separate from the authentication-related functions. The latter are available under our REST-based Authentication Api.

Because of this, the authentication scheme for GraphQL is more complex. We have the notion of login, Access and Refresh tokens, elevated access (required for high security operations, like executing a transfer), magic links and passcodes, and so on.

We will discuss these concepts in the subsequent sections.

Access and Refresh Tokens

Both GraphQL interface and Authentication API work with signed JWT tokens. You will receive Access Token and Refresh Token from us, as a result of the customer login flow.

To access GraphQL queries and mutations, you need to provide the acquired Access Token as the Authentication: Bearer HTTP header. Valid token will grant you access with the security context of the end user for whom it was issued.

Access Tokens are short-lived. After expiration, an Access Token needs to be refreshed using the Refresh Token. This can be done by calling the Re-issue Access Token endpoint.

📘

Token signatures

Access and Refresh Tokens are always generated by our authentication infrastructure, and as such, come already signed by us. In case you rely on Shaype authentication provider to manage Users, we will also share the token signing key with you - this will allow you to validate Shaype-issued tokens before executing actions against your own back-end.

Responses returning Access and Refresh Tokens contain following fields:

FieldDescription
access_tokenShort-lived Access Token
access_expires_utcAccess Token expiry, as number of seconds passed since the Unix epoch
access_token_scopeThe scope for the Access Token (e.g. email or passcode)
refresh_tokenLong-lived Refresh Token
refresh_expires_utcRefresh Token expiry, as number of seconds passed since the Unix epoch
refresh_token_scopeThe scope for the Refresh Token (e.g. email or passcode)
hotp_counterOTP validation attempt counter for elevated access
hotp_saltOTP salt used to generate elevated access header

Getting the tokens - sign-up flow

This following authorisation flow relates to the initial onboarding of a customer. The descriptions below are related to a registration implemented using a mobile app, but for a website the steps will be the same - with the exception of step 5, where a HTTP redirect will be used instead of a redirect using the app's deep linking scheme.

2162
  1. Customer initiates registration.
    The App will request the following information from the user upon registration, as detailed below:
    • Customer email address
    • Mobile number
    • Passcode
  2. The App will call the Request Magic Link email endpoint.
  3. This endpoint will send an email containing a magic link to the customer. Shaype will deliver this to your system via a webhook and you will be responsible to send the email tot the customer.
  4. After clicking it, the magic link will take the customer to the authentication endpoint via a browser and call the Redirect Magic Link to App endpoint, passing OTP and STATE. The customer will be redirected to the App using a deep linking scheme registered for the App.
  5. The App will then use the parameters from the redirect link to call the Issue Access and Refresh Tokens endpoint to get an Access and Refresh Tokens with email scope. This will allow the App to login and thus the ability to call authentication APIs with limited scope.
  6. In the next step, the customer will set a 6 digit numerical passcode on the App. The App will call the Set a Passcode for User endpoint with customers passcode hashed. This call should be authenticated using the Access Token from the previous call, passed as Authentication:Bearer header
  7. That endpoint will respond with a new Access and Refresh Tokens with passcode scope, that will allow the App to call the GraphQL interface.

Getting the tokens - subsequent login flow

This following authorisation flow relates to the any subsequent logins made by the customer. (i.e. the user has already passed the onboarding set up phase described in section above.)

2162
  1. The customer will login with their passcode/Face ID/Touch ID.
  2. The App will call the Re-issue Access Token endpoint with the refresh token that it had previously received from a prior new app login.
  3. That endpoint will respond with a new Access Token with passcode scope, that will allow the App to call the GraphQL interface.

Getting the tokens - new device flow

This following authorisation flow relates to the initial login by a customer into the app. (i.e. The user has already onboarded and has uninstalled and reinstalled the APP on their device.)

2162
  1. The user clicks Login within the App.
    The App will request the following information from the user:
    • Customer email address
  2. The App will call the Request Magic Link email endpoint.
  3. This endpoint will send an email containing a magic link to the customer.
  4. After clicking it, the magic link will take the customer to the authentication endpoint via a browser and call the Redirect Magic Link to App endpoint, passing OTP and STATE. The customer will be redirected to the App using a deep linking scheme registered for the App.
  5. The App will then use the parameters from the redirect link to call the Issue Access and Refresh Tokens endpoint to get an Access and Refresh Tokens with email scope. This will allow the App to login and thus the ability to call authentication APIs with limited scope.
  6. The Customer will then input the passcode.
  7. With the access token from Step 5, the App can call the Request Passcode Challenge
    endpoint.
  8. With the access token from Step 5, the App will also call the Log-in User endpoint.
  9. That endpoint will respond with a new Access and Refresh Tokens with passcode scope, that will allow the App to call the GraphQL interface.

Passcode reset flow

This following authorisation flow relates to the any passcode reset initiated by the customer.

📘

Note

This flow should be also used for the first login for Customers created using the B2B Rest API. These customers have a random initial passcode assigned, and they will need to go through passcode reset to gain access to the app.

2162
  1. The Customer clicks Forgot Passcode within the App.
  2. The App will call the Request Magic Link email endpoint.
  3. This endpoint will send an email containing a magic link to the customer via SendGrid.
  4. After activating, the magic link will take the customer to the authentication endpoint via a browser and call the Redirect Magic Link to App endpoint, passing OTP and STATE. The customer will be redirected to the App using a deep linking scheme registered for the App.
  5. The App will then use the parameters from the redirect link to call the Issue Access and Refresh Tokens endpoint to get an Access and Refresh Tokens with email scope. This will allow the App to login and thus the ability to call authentication APIs with limited scope.
  6. The user will click “NEXT” on the the app, which will call the Start Forgotten Passcode flow
    endpoint. The authentication manger will return both a Forgotten Token and SMS (OTP) verification to the App and Customer respectively.
  7. The user will enter the OTP (contained within the SMS) in the App.
  8. The user will then enter and re-enter the new passcode within the App.
  9. The App will call the Reset the Passcode endpoint with the new passcode, authenticated with the Forgotten Token, to get an Access and Refresh Tokens with passcode scope.
  10. Once the passcode has been reset and the tokens received in Step 9, the App will have the ability to call the GraphQL interface.

Passcode elevation

Some transactions require proof-of-passcode to be submitted with the GraphQL call. These transactions are considered "elevated" as they typically try to access a higher risk resource e.g. transferring money, viewing card details etc. The passcode proof is passed in with the GraphQL query or mutation as an additional elevation HTTP header. This mechanism follows the HOTP (HMAC-based One-Time Password) standard.

When you retrieve auth tokens from our auth-server during the passcode login (Log-in User), the response contains two additional fields: hotp_counter and hotp_salt.

The HOTP algorithm utilises a secret key and a counter to create the OTP that is sent to the platform for validation. The secret key is generated using the hotp_counter and hotp_salt fields, as well as the user inputted passcode, and is then used to generate the passcode elevation header that is sent down in the proceeding GraphQL request that requires this step-up challenge. The key part of this exchange is that we maintain and increment the hotp_counter on both the app and auth-server. If a customer enters the passcode incorrectly, the elevated GraphQL operation will fail, and we will increment the hotp_counter on both app and server. The auth-server allows N + 10 unsuccessful validation attempts (known as the look-ahead window) before the counter becomes completely out of sync.

Sample flow

  1. Retrieve token details and initial HOTP salt and counter values during login - securely store this data on device.

🚧

Note

New access tokens can be retrieved using the Refresh Token, however the Refresh Token and HOTP values are only retrieved during initial login and the onus is on the client app to ensure the HOTP counter value is incremented each time a step-up challenge is needed.

A generated elevation header can be used only once.

  1. The app requests the passcode from the customer.
  2. The app uses the passcode to generate the elevation header.

Example

/**
 * Create SHA256 hash of the passcode.
 */
const createPasscodeHash = (passcode: string) => {
  return crypto.SHA256(passcode).toString();
};
/**
 * Using previously stored HMAC OTP and salt hash, generate a new hash with the previous passcode hash.
 */
const getNextPasscodeHotp = async (passcodeHash: string) => {
  const passcodeHotpData: any = await getStorageItem(STORAGE_KEYS.PASSCODE_HOTP_DATA_KEY);
  if (!passcodeHotpData) {
    return "";
  }
  const hotpCounter: any = passcodeHotpData.hotp_counter;
  const hotpSalt: any = passcodeHotpData.hotp_salt;
 
  return createPasscodeHotp(hotpCounter, passcodeHash, hotpSalt);
};
 
/**
 * Used to validate elevated actions i.e. payments, passcode change etc.
 * Create an elevation header HOTP using user provided passcode.
 */
const createElevationHeader = async (passcode: string) => {
  const passcodeHotp = await getNextPasscodeHotp(createPasscodeHash(passcode));
  const installationHandle = await getInstallationHandle();
 
  ELEVATION_HEADER = `${installationHandle}:${passcodeHotp}`;
 
  return ELEVATION_HEADER;
};
  1. Attach the generated header value to subsequent GraphQL request as an elevation HTTP header.
  2. When request has completed the client side app must increment the stored hotp_counter.