npm package: @fewlines/connect-management
Description: Management flow for the Connect JS SDK
Author: Fewlines
Latest version: 0.8.1

= Fewlines Connect-js Management
:toc: preamble

**Management** is part of the **Fewlines Connect-js SDK**.

It provides a list of functions to handle all the user data flows related to Connect, and some useful tools regarding Provider Applications.

== Installation

[source, shell]
----
yarn add @fewlines/connect-management
----

== Usage

Apart from the arguments specific to the query/command needed to fetch **Connect**, you will need to pass your **Management** credentials each time, in the form of an object, called `ManagementCredentials`.

[source, typescript]
----
type ManagementCredentials = {
  URI: string;
  APIKey: string;
}

const managementCredentials: ManagementCredentials = {
  URI: "URI",
  APIKey: "APIKey";
}
----

== Important types

=== Identities

**Identities** are one of the building blocks of **Connect-js Management**.

[source, typescript]
----
enum IdentityTypes {
  APPLE = "APPLE",
  EMAIL = "EMAIL",
  FACEBOOK = "FACEBOOK",
  GITHUB = "GITHUB",
  GOOGLE = "GOOGLE",
  KAKAO_TALK = "KAKAO_TALK",
  LINE = "LINE",
  MICROSOFT = "MICROSOFT",
  NAVER = "NAVER",
  PAYPAL = "PAYPAL",
  PHONE = "PHONE",
  PROVIDER = "PROVIDER",
  STRAVA = "STRAVA",
  VKONTAKTE = "VKONTAKTE",
}

type Identity = {
  id: string;
  primary: boolean;
  status: "validated" | "unvalidated";
  type: IdentityTypes;
  value: string;
};
----

=== Provider Application

**Provider Applications** are the entities between the **Connect Provider** and your web application.

[source, typescript]
----
type ProviderApplication = {
  id: string;
  name: string;
  description: string;
  defaultHomePage: string;
  redirectUris: string[];
};
----

== Queries

=== `checkVerificationCode`

Used to verify the verification code input by the user. The function returns an object, composed of:

- The type of the Identity
- The value of the Identity
- The status of the Identity
- The nonce

[source, typescript]
----
import { checkVerificationCode } from "@fewlines/connect-management";

const input = {
  code: "288761",
  eventId: "ec1ee772-3249-4e5a-ad85-2b18d13f6f73",
};

const { identityType, identityValue, nonce, status } =
  await checkVerificationCode(managementCredentials, input);
----

'''

=== `getProviderApplication`

Used to get the information from the Connect Application. The function returns the Application data. Refer to the [Provider Application](#Provider) to understand the returned data structure.

[source, typescript]
----
import { getProviderApplication } from "@fewlines/connect-management";

const { id, defaultHomePage, redirectUris, name, description } =
  await getProviderApplication(
    managementCredentials,
    "a3e64872-6326-4813-948d-db8d8fc81bc8",
  );
----

'''

=== `getIdentity`

Used to retrieve an Identity for a particular user using both the user and the Identity `id`. Refer to the [Identity section](#Identities) to understand the returned data structure.

[source, typescript]
----
import { getIdentity } from "@fewlines/connect-management";

const input = {
  userId: "b4e8bec6-3156-43c4-aaa8-9632c1c160b3",
  identityId: "9a60bc4c-82dc-42c5-8bac-8b051340d2ac",
};

const { id, primary, status, type, value } = await getIdentity(
  managementCredentials,
  input,
);
----

'''

=== `getIdentities`

Used to retrieve all the Identities for a particular user. The function returns a list of Identity. Refer to the [Identity section](#Identities) to understand the returned data structure.

[source, typescript]
----
import { getIdentities } from "@fewlines/connect-management";

const identities = await getIdentities(
  managementCredentials,
  "d96ee314-31b2-4e19-88b7-63734b90d1d4",
);
----

'''

=== `getProviderName`

Used to retrieve the name of the current Provider.

[source, typescript]
----
import { getProviderName } from "@fewlines/connect-management";

const providerName = await getProviderName(managementCredentials);
----

'''

=== `getUserIdFromIdentityValue`

Used to retrieve the user `id` by passing an Identity value as input.

[source, typescript]
----
import { getUserIdFromIdentityValue } from "@fewlines/connect-management";

const userID = await getUserIdFromIdentityValue(
  managementCredentials,
  "foo@fewlines.co",
);
----

'''

=== `isUserPasswordSet`

Used to check if the user has already set his password. The function returns a boolean.

[source, typescript]
----
import { isUserPasswordSet } from "@fewlines/connect-management";

const isPasswordSet = await isUserPasswordSet(
  managementCredentials,
  "16071981-1536-4eb2-a33e-892dc84c14a4",
);
----

== Commands

=== `addIdentityToUser`

Used to add a new Identity to the user. It also allows the use of multiple event IDs. The function returns the newly added Identity.

[source, typescript]
----
import { addIdentityToUser } from "@fewlines/connect-management";

const input = {
  userIds: [
    "d96ee314-31b2-4e19-88b7-63734b90d1d4",
    "5f42e01d-3b41-485e-8749-975cb693a3aa",
  ],
  type: "EMAIL",
  value: "foo@fewlines.co",
};

await addIdentityToUser(managementCredentials, input);
----

'''

=== `createOrUpdatePassword`

Used to create or update a User password. The function returns the User `id`.

[source, typescript]
----
import { createOrUpdatePassword } from "@fewlines/connect-management";

const input = {
  cleartextPassword: "cleartextPassword",
  userId: "d8959bfd9-aab8-4de2-81bb-cbd9ea1a4191",
};

const isPasswordSet = await createOrUpdatePassword(
  managementCredentials,
  input,
);
----

If the `cleartextPassword` input doesn't meet the Provider defined rules, the function will throw a specific error containing the `rules` waited for the password to be valid.

[source, typescript]
----
import {
  createOrUpdatePassword,
  InvalidPasswordInputError,
} from "@fewlines/connect-management";

const input = {
  cleartextPassword: "42",
  userId: "d8959bfd9-aab8-4de2-81bb-cbd9ea1a4191",
};

try {
  await createOrUpdatePassword(managementCredentials, input);
} catch (error) {
  if (error instanceof InvalidPasswordInputError) {
    const { rules } = error;
    // ...
  }
}
----

'''

=== `createUserWithIdentities`

Create a new User with a list of Identities for the current Provider. The list of identities passed as input cannot be empty. The function returns the User `id`.

[source, typescript]
----
import { createUserWithIdentities } from "@fewlines/connect-management";

const input = {
  identities: [
    {
      id: "d4e5e5d5-4fd3-49af-8ee4-7e28c824bb3c",
      type: "EMAIL",
      value: "foo@fewlines.co",
      status: "validated",
      primary: true,
    },
  ],
  localeCode: "en-EN",
};

const userId = await createUserWithIdentities(managementCredentials, input);
----

'''

=== `deleteUser`

Used to delete a User. Return the string `"DISPATCHED"` to signify that the delete event has been sent to all the services.

[source, typescript]
----
import { deleteUser } from "@fewlines/connect-management";

const deleteStatus = await deleteUser(
  managementCredentials,
  "f084749a-2e90-4891-a26f-65e08c4f4e69",
);
----

'''

=== `markIdentityAsPrimary`

Used to mark an Identity as `primary`. Will set the previous primary identity as non primary.
The function returns the Identity. Refer to the [Identity section](#Identities) to understand the returned data structure.

[source, typescript]
----
import { markIdentityAsPrimary } from "@fewlines/connect-management";

const newPrimaryIdentity = await markIdentityAsPrimary(
  managementCredentials,
  "504c741c-f9dd-425c-912a-03fe051b0e6e",
);
----

'''

=== `removeIdentityFromUser`

Used to remove an Identity from a User. The function returns true if the removal worked.

[source, typescript]
----
import { removeIdentityFromUser } from "@fewlines/connect-management";

const input = {
  userId: "4a5324f7-9390-41ab-a94d-2ab3198f1a8c",
  type: "EMAIL",
  value: "foo@fewlines.co",
};

const isIdentityRemove = await removeIdentityFromUser(
  managementCredentials,
  input,
);
----

'''

=== `sendIdentityValidationCode`

Used to send an Identity Validation Code to the User. The function returns an object, composed of:

- The event id, used to verify the Validation Code.
- The callback URL
- The locale code
- The nonce

[source, typescript]
----
import { sendIdentityValidationCode } from "@fewlines/connect-management";

const input = {
  callbackUrl: "/",
  identity: {
    id: "12488dfe-8e46-4391-a8bb-f0db41078942",
    type: "EMAIL",
    value: "foo@fewlines.co",
    status: "validated",
    primary: true,
  },
  userId: "37b21863-3f38-4d20-848d-3108337a0b8b",
};

const { callbackUrl, localeCode, eventId, nonce } =
  await sendIdentityValidationCode(managementCredentials, input);
----

If the Identity `value` input is blank or is identical to an already validated Identity for the current Provider, the function will throw specific errors corresponding to each case.

[source, typescript]
----
import {
  sendIdentityValidationCode,
  IdentityAlreadyUsedError,
  IdentityValueCantBeBlankError,
} from "@fewlines/connect-management";

const input = {
  callbackUrl: "/",
  identity: {
    id: "12488dfe-8e46-4391-a8bb-f0db41078942",
    type: "EMAIL",
    value: "",
    status: "validated",
    primary: true,
  },
  userId: "37b21863-3f38-4d20-848d-3108337a0b8b",
};

try {
  const { callbackUrl, localeCode, eventId, nonce } =
    await sendIdentityValidationCode(managementCredentials, input);
} catch (error) {
  if (error instanceof IdentityValueCantBeBlankError) {
    // ...
  }

  if (error instanceof IdentityAlreadyUsedError) {
    // ...
  }
}
----

'''

=== `updateProviderApplication`

Used to update the Provider Application. The function returns the Application data. Refer to the [Provider Application](#Provider) to understand the returned data structure.

[source, typescript]
----
import { updateProviderApplication } from "@fewlines/connect-management";

const input = {
  id: "d1e34015-4ba0-44a3-8171-15ed6979b86d",
  description: "Connect JS Management test environment",
  name: "Connect JS Management",
  redirectUris: [
    "http://localhost:3000/oauth/callback",
    "https://connect-management.local:3001/oauth/callback",
  ],
  defaultHomePage: "https://www.fewlines.co",
};

const { id, description, redirectUris, name, defaultHomePage } =
  await updateProviderApplication(managementCredentials, input);
----

'''

### `updateIdentityFromUser`

Used to update an Identity. Here are the props needed, in order:

- managementCredentials: URI and API Key of Connect.
- userId: ID or sub of the current user.
- eventIds: List of event ID generated at the start of the Identity validation flow, and populated when the user re-send a validation code.
- validationCode: Code input from the User during the Identity validation flow.
- identityValue: Identity value that will replace the current Identity.
- identityToUpdateId: ID of the previous Identity to update.
- maxRetry: optional number argument, set by default to `2`. It determines the max number of retries the function will do if a server exception is raised during the flow. You can pass `0` to disable the retry feature.

[source, typescript]
----
import { updateIdentity } from "@fewlines/connect-management";

await updateIdentityFromUser(
  managementCredentials,
  userId,
  eventIds,
  validationCode,
  identityValue,
  identityToUpdateId,
);
----

The function will do a rollback of any added Identity and primary Identity status in case of a failure. It also allows the use of multiple event IDs. The function will also perform a number of retry if an exception occurs during the call (triggered only on exceptions corresponding to server errors). The number of retries is determined by the `maxRetry` argument passed to the function (with a default value set at `2`). Each retry call will happen after a short increasing delay, with a maximum of 1000ms.

Here are the expected exception raised in case of a failure:

- `ConnectUnreachableError`
- `GraphqlErrors`
- `IdentityNotFoundError`
- `InvalidValidationCodeError`
- `UnhandledIdentityType`

'''

=== `sendTwoFAVerificationCode`

Used to send a Verification Code to the User. The function returns an object, composed of:

- The event id associated with the Verification Code.
- The callback URL
- The locale code
- The nonce

[source, typescript]
----
import { sendTwoFAVerificationCode } from "@fewlines/connect-management";

const input = {
  callbackUrl: "/",
  identity: {
    id: "12488dfe-8e46-4391-a8bb-f0db41078942",
    type: "PHONE",
    value: "+33642424242",
    status: "validated",
    primary: true,
  },
  userId: "37b21863-3f38-4d20-848d-3108337a0b8b",
};

const { callbackUrl, localeCode, eventId, nonce } =
  await sendTwoFAVerificationCode(managementCredentials, input);
----

If the Identity `type` isn't a valid one or if the provided Identity is not associated with the `userId`, the function will throw specific errors corresponding to each case.

[source, typescript]
----
import {
  sendTwoFAVerificationCode,
  InvalidIdentityTypeError,
  IdentityNotFoundError,
} from "@fewlines/connect-management";

const input = {
  callbackUrl: "/",
  identity: {
    id: "12488dfe-8e46-4391-a8bb-f0db41078942",
    type: "EMAIL",
    value: "foo@fewlines.co",
    status: "validated",
    primary: true,
  },
  userId: "37b21863-3f38-4d20-848d-3108337a0b8b",
};

try {
  const { callbackUrl, localeCode, eventId, nonce } =
    await sendTwoFAVerificationCode(managementCredentials, input);
} catch (error) {
  if (error instanceof InvalidIdentityTypeError) {
    // ...
  }

  if (error instanceof IdentityNotFoundError) {
    // ...
  }
}
----