The package @axinom/mosaic-user-auth allows React based frontends to access the Mosaic User Service for authentication and authorization needs (end-user facing)

@axinom/mosaic-user-auth

Introduction

@axinom/mosaic-user-auth is a react library that wraps the core functionality in the User Service that is related to frontend end-user applications. Developers can use the methods provided by this library to perform all end-user authenticated operations such as signing in, signing out, access token handling etc.

UserServiceProvider

UserServiceProvider is a React Provider which has 3 properties. It provides the User Service Client which can be retrieved using the useUserService hook.

  • userAuthConfig - Configuration for the User Service Auth API. This is an object of UserAuthConfig type.

  • userServiceConfig - Configuration for the User Service GraphQL API. This is an object of UserServiceConfig type.

  • tokenRenewalMethod - Token Renewal method the application want to use. This is a value from TokenRenewalMethod enumeration.

UserAuthConfig

interface UserAuthConfig {
  userAuthEndpointUrl: string;
  tenantId: string;
  environmentId: string;
  applicationId: string;
}
Property Description

userAuthEndpointUrl

URL of the User Service authentication endpoint.

tenantId

The tenant ID connected to the end-user application. This is the tenant which the User Service Application is registered against.

environmentId

The environment ID connected to the end-user application. This is the environment which the User Service Application is registered against.

applicationId

The ID of the Application created in the User Service configuration.

UserServiceConfig

interface UserAuthConfig {
  userServiceEndpointUrl: string;
}
Property Description

userServiceEndpointUrl

URL of the User Service GraphQL endpoint.

TokenRenewalMethod

Value Description

PRE_EMPTIVE

The token is continuously renewed just before expiry. A TokenChangedCallback can be registered using addTokenChangedHandler method to get notified when the token changes.

ON_DEMAND

The token is renewed only if needed upon calling getToken method.

Usage

The UserServiceProvider should be used as a context provider for the react app, at the top of the component hierarchy. This way we can ensure that all child components will have access to the UserServiceClient. Ideally, these properties can be retrieved through environment variables.

const userAuthConfig = {
    userAuthEndpointUrl: 'https://id.ottstream.com',
    tenantId: 'ace73f47-b654-43e3-bc23-a7ba83c6ea63',
    environmentId: '559d0282-2e64-430c-8c4d-93f1777bba07'
    applicationId: 'd5091350-4868-4efa-b508-f7438d910a70'
}

const userServiceConfig = {
    userServiceEndpointUrl = 'https://user.service.eu.axinom.net'
}

<UserServiceProvider
    userAuthConfig={userAuthConfig}
    userServiceConfig={userServiceConfig}
    tokenRenewalMethod={TokenRenewalMethod.PRE_EMPTIVE}
>
    <App />
</UserServiceProvider>

UserServiceClient

UserServiceClient is an object instance created through the UserServiceProvider which provides a set of methods to perform tasks related to the User Service such as sign in, sign out, create user profile etc.

The instance created through the UserServiceProvider can be retrieved through the hook useUserService.

Interfaces

The library makes use of the following interfaces to structure data in its methods.

Interface Name Description

UserAuthConfig

Configuration for the user service auth API.

UserServiceConfig

Configuration for the user service API.

UserToken

Represents the information in a User Token.

TokenResponse

Response for a getToken request.

ROPCSignInRequest

Represents the information required to perform a ROPC Sign In flow.

UserSignUpRequest

Represents the information required for a new user sign up.

CheckUserSignUpOtpRequest

Represents information required to check if a User Sign Up OTP is valid.

CompleteUserSignUpRequest

Represents the information required to verify a new user sign up.

CheckPasswordResetOtpRequest

Represents information required to check if a Password Reset OTP is valid.

CheckPasswordResetOtpRequest

Represents the require information for Complete Password Reset request.

AuthenticateEndUserApplicationRequest

Represents the information required to authenticate an End-User Application.

EndUserApplicationToken

Represents an End-User Application Access Token.

AuthenticateEndUserApplicationResponse

Response for a authenticateEndUserApplication request.

UserAuthConfig

UserAuthConfig interface holds the configuration for the User Service Auth API.

interface UserAuthConfig {
  userAuthEndpointUrl: string;
  tenantId: string;
  environmentId: string;
  applicationId: string;
}

UserServiceConfig

UserServiceConfig interface holds the configuration for the User Service API.

interface UserServiceConfig {
  userServiceEndpointUrl: string;
}

UserToken

UserToken interface holds the information related to a User Token that is returned by the User Service Auth API.

interface UserToken {
  tenantId: string;
  environmentId: string;
  applicationId: string;
  userId: string;
  profileId: string;
  email: string | null;
  name: string | null;
  extensions?: unknown;
  accessToken: string;
  expiresInSeconds: number;
}

UserProfile

UserProfile interface contains information related to a User Profile.

interface UserProfile {
  id: string;
  displayName: string;
  profilePictureUrl?: string;
  profileData?: unknown;
  defaultProfile: boolean;
}

TokenResponse

TokenResponse interface stores the information that will be returned by a call to getToken method.

interface TokenResponse {
  code: 'SUCCESS' | 'ERROR';
  message?: string;
  userToken?: UserToken;
  userProfile?: UserProfile;
  /**
   * This will indicate the next automatic token renewal timestamp when the
   * `PRE_EMPTIVE` token renewal method is configured on the `UserServiceProvider`.
   *
   * For `ON_DEMAND` token renewal method, this value can be dismissed.
   */
  nextTokenRenewalAt?: Date;
}

ROPCSignInRequest

ROPCSignInRequest interface represents the information required by the ropcSignIn method to perform a Sign In request using the ROPC flow.

interface ROPCSignInRequest {
  email: string;
  password: string;
}

UserSignUpRequest

UserSignUpRequest interface represents the information required by the initiateUserSignUp method which will start the user sign up process.

interface UserSignUpRequest {
  email: string;
  password: string;
}

CheckUserSignUpOtpRequest

CheckUserSignUpOtpRequest interface represents the information required by checkUserSignUpOTP method which will check if a given User Sign Up OTP is valid.

interface CheckUserSignUpOtpRequest {
  signUpOtp: string;
}

CompleteUserSignUpRequest

CompleteUserSignUpRequest interface represents the information required by completeUserSignUp method which will complete the User Sign Up flow.

interface CompleteUserSignUpRequest {
  signUpOtp: string;
  password?: string;
}

CheckPasswordResetOtpRequest

CheckPasswordResetOtpRequest interface represents the information required by checkResetPasswordOTP method which will check if a given Reset Password OTP is valid.

interface CheckPasswordResetOtpRequest {
  resetOtp: string;
}

CompletePasswordResetRequest

CompletePasswordResetRequest interface represents the information required by completeResetPassword method which will complete the Reset Password flow.

interface CompletePasswordResetRequest {
  resetOtp: string;
  newPassword: string;
}

AuthenticateEndUserApplicationRequest

AuthenticateEndUserApplicationRequest interface represents the information required by authenticateEndUserApplication method to authenticate an end-user application with User Service.

interface AuthenticateEndUserApplicationRequest {
  tenantId: string;
  environmentId: string;
  applicationId: string;
  applicationKey: string;
}

EndUserApplicationToken

EndUserApplicationToken holds the information related to an End-User Application Access Token.

interface EndUserApplicationToken {
  accessToken: string;
  expiresInSeconds: number;
  tokenType: string;
}

AuthenticateEndUserApplicationResponse

AuthenticateEndUserApplicationResponse interface represents the response data returned by authenticateEndUserApplication method.

interface AuthenticateEndUserApplicationResponse {
  code: 'SUCCESS' | 'ERROR';
  message?: string;
  endUserApplicationToken?: EndUserApplicationToken;
}

Methods

The UserServiceClient provides the following methods.

Method Name Description

getToken

Returns a access token. The method will ensure to avoid unnecessary API calls by caching valid tokens. It is not recommended to save the token for later use. When in need of a token, call this method to get a new token.

addTokenChangedHandler

Adds an event handler that will be invoked whenever a new token response is loaded from the backend.

removeTokenChangedHandler

Removes an event handler for the TokenChanged event

getIdpConfigurations

Returns an array of IDP Configurations that are configured for the application. The authentication URL for IDPs will be sent along with this, which the frontend app should navigate to perform the sign in. This list will exclude connections for provider id AX_AUTH as it is used for user sign-up and ROPC flows.

logoutUser

Logs out the user.

initiateUserSignUp

Initiates the process to register a new user for the application. To use this flow, an IDP connection for the provider managed IDP AxAuth must be configured.

checkUserSignUpOTP

Checks if a given User Sign-Up OTP Code is valid.

completeUserSignUp

Completes a user sign up process using the AxAuth IDP.The user needs to input the OTP along with a password (if not provided earlier) to finish the process.

ropcSignIn

Starts the Sign-In flow for signed up users using the AxAuth IDP configured for the application. This sign in is executed using ROPC flow.

initiateResetPassword

Initiates the process of resetting the password for a self signed up user through AxAuth. This will initiate the call to the webhook configured in AxAuth to send the generated OTP.

checkResetPasswordOTP

Checks if a given Reset Password OTP Code is valid.

completeResetPassword

Completes the password reset flow for a user registered using the AxAuth IDP. The user needs to input the OTP along with a new password to finish the process.

setActiveProfile

Sets a given profile ID as the active profile.

getUserProfile

Returns user profile details for a given ID.

getUserProfiles

Returns all user profiles for a given user.

createUserProfile

Creates a new user profile for a user.

updateUserProfile

Update user profile for a given ID.

deleteUserProfile

Deletes a user profile for a given ID.

authenticateEndUserApplication

Authenticates an end-user application and returns an Application Token. This token can be used within the application to make requests to end-user facing APIs exposed by Mosaic Services. (i.e. query subscription plans from the Billing Service.)

getToken

getToken method can be used to fetch the user access token from User Service after the authentication is completed. If a User Token Enrichment Webhook is defined for the Application, that will be called at this point, and the returned user access token will contain the additional information added under the extensions property.

Usage
import { TokenResponse, useUserService } from '@axinom/mosaic-user-auth';

const {getToken} = useUserService();
const [tokenResponse, setTokenResponse] = useState<TokenResponse | null>(
    null,
  );
setTokenResponse(await getToken());

getToken returns a TokenResponse object.

addTokenChangedHandler

addTokenChangeHandler can be used as an event listener for the frontend to be notified whenever a new token is loaded from User Service.

Usage
import { TokenResponse, useUserService } from '@axinom/mosaic-user-auth';

const {getToken, addTokenChangedHandler} = useUserService();
const [tokenResponse, setTokenResponse] = useState<TokenResponse | null>(
    null,
  );
useEffect(() => {
    (async () => {
      setTokenResponse(await getToken());
    })();
  }, [addTokenChangedHandler, getToken]);

Using addTokenChangeHandler in the dependency list like above when calling getToken from within a useEffect block will make the getToken method to be called whenever the token is changed and refresh it.

removeTokenChangedHandler

removeTokenChangedHandler can be used to remove the event handler from the TokenChanged event.

Usage
import { TokenResponse, useUserService } from '@axinom/mosaic-user-auth';

const {getToken, removeTokenChangedHandler} = useUserService();
const [tokenResponse, setTokenResponse] = useState<TokenResponse | null>(
    null,
  );
useEffect(() => {
    (async () => {
      setTokenResponse(await getToken());
    })();
  }, [removeTokenChangedHandler, getToken]);

getIdpConfigurations

An end-user application may retrieve all the available IDP Connections by calling getIdpConfigurations method.

Usage
import { IdpConfiguration, useUserService } from '@axinom/mosaic-user-auth';

const { getIdpConfigurations } = useUserService();
const [idpConfigurations, setIDPConfigurations] = useState<
    IdpConfiguration[]
  >([]);

useEffect(() => {
    (async () => {
      const idpConfigs = await getIdpConfigurations(
        `app.ottstream.com`,
      );
      setIDPConfigurations(idpConfigs);
    })();
  }, [getIdpConfigurations]);

logoutUser

When logoutUser is called from an end-user application, it will remove any authentication related cookies(AX_REFRESH_TOKEN) from the browser and remove the user token from User Service backend.

Usage
import { useUserService } from '@axinom/mosaic-user-auth';

const { logoutUser } = useUserService();

await logoutUser();

logoutUser can be ideally called from a onClick event of Sign Out button.

initiateUserSignUp

The user sign up flow consists of two steps. First the application needs to make an initiateUserSignUp request, which will create a record with the user’s email address and generate a Sign Up OTP.

initiateUserSignUp returns a UserSignUpResponse object.

Usage
import { useUserService } from '@axinom/mosaic-user-auth';

const { initiateUserSignUp } = useUserService();

const signUpResponse = await initiateUserSignUp(`app.ottstream.com/signup`, {email, password});

checkUserSignUpOTP

After initiateUserSignUp is called, the AxAuth Service generates Sign Up OTP for against the user’s email address. Frontend applications can use checkUserSignUpOTP method to check if any given OTP code is valid, before making the call to completeUserSignUp method.

This is not a mandatory call, and can be done at the developer’s discretion to make the user experience more seamless.

checkUserSignUpOTP returns a CheckUserSignUpOtpResponse where the isOtpValid property can be extracted from.

Usage
import { useUserService } from '@axinom/mosaic-user-auth';

const { checkUserSignUpOTP } = useUserService();

const { isOtpValid } = await checkUserSignUpOTP('app.ottstream.com', { signUpOtp });

completeUserSignUp

The second and final step of the user sign up flow is to call completeUserSignUp method. This call takes the Sign Up OTP as an argument, and a password if a password was not provided at the initiateUserSignUp stage. (If no password was provided at either in the first step or this, an error will be raised.) This call will mark the newly sign up user account as verified in the backend, and make it available for future use.

Usage
import { useUserService } from '@axinom/mosaic-user-auth';

const { completeUserSignUp } = useUserService();

const completeUserSignUpResponse = await completeUserSignUp({signUpOtp, password});

ropcSignIn

The ropcSignIn method must be used when a user that was registered through AxAuth is signing in to the application.

Usage
import { useUserService } from '@axinom/mosaic-user-auth';

const { ropcSignIn } = useUserService();

const handleROPCSignIn = async(event: React.FormEvent<HTMLFormElement>): Promise<void> => {
    event.preventDefault();
    const ropcSignInResponse = await ropcSignIn({
      email: email,
      password: password
    });
    if (ropcSignInResponse.code === ROPCSignInResponseCode.SUCCESS) {
      window.location.assign('/');
    } else {
      setError(`Unable to Sign In. ${ropcSignInResponse.details?.error ?? ropcSignInResponse.message}`)
    }
  }

The handleROPCSignIn method in the example could be called from the onClick event of the Sign-In button.

initiateResetPassword

The application developers can use initiateResetPassword method when implementing password reset feature for AxAuth users. This is the first step of a two step process where a Password Reset OTP would be generated against the user’s email.

Usage
import { useUserService } from '@axinom/mosaic-user-auth';

const { initiateResetPassword } = useUserService();

const initiatePasswordReset = async(event: React.FormEvent<HTMLFormElement>): Promise<void> => {
  event.preventDefault();
  const initiatePasswordResetResponse = await initiateResetPassword(`${ottstream_APP_BASE_URL}/reset-password`, email);
  if (initiatePasswordResetResponse.code === ROPCPasswordResponseCode.SUCCESS) {
    window.location.assign('/complete-reset-password');
  } else {
    setError(initiatePasswordResetResponse.message ?? 'Error resetting password.');
  }
}

The initiatePasswordReset method in the example could be called from the onClick event of the Forgot Password button.

checkResetPasswordOTP

Application developers may use the checkResetPasswordOTP method to validate if a given Password Reset OTP is valid. checkResetPasswordOTP returns a CheckPasswordResetOtpResponse where the isOtpValid property can be extracted from.

Usage
import { useUserService } from '@axinom/mosaic-user-auth';

const { checkResetPasswordOTP } = useUserService();

const { isOtpValid } = await checkResetPasswordOTP('app.ottstream.com', { resetOtp });

completeResetPassword

The second and final step of password reset flow is to call the completeResetPassword method, along with the Password Reset OTP.

Usage
import { useUserService } from '@axinom/mosaic-user-auth';

const { completeResetPassword } = useUserService();

const completePasswordReset = async(event: React.FormEvent<HTMLFormElement>): Promise<void> => {
  event.preventDefault();
  const completePasswordResetResponse = await completeResetPassword({newPassword: password, resetOtp: otp});
  if (completePasswordResetResponse.code === ROPCPasswordResponseCode.SUCCESS) {
    window.location.assign('/login');
  } else {
    setError(completePasswordResetResponse.message ?? 'Error resetting password.');
  }
}

The completePasswordReset method in the example could be called from the onClick event of the Confirm Password button.

setActiveProfile

In a application where a user can have multiple user profiles, the setActiveProfile method can be used to set the active profile for a given session. This profile will be attached to access token and will be sent along with any subsequent requests to end-user services.

Usage
import { useUserService } from '@axinom/mosaic-user-auth';

const { setActiveProfile } = useUserService();

const handleSelectProfileClick = async (profileId: string) => {
  await setActiveProfile(profileId);
  history.replace('/');
};

The handleSelectProfileClick method in the example could be called from the onClick event of a Select Profile button.

getUserProfile

Application developers can use getUserProfile to retrieve profile related information by providing a profileId. getUserProfile returns a UserProfile object.

Usage
import { useUserService } from '@axinom/mosaic-user-auth';

const { getUserProfile } = useUserService();

const userProfile = await getUserProfile(profileId);

getUserProfiles

getUserProfiles method can be used to retrieved all user profiles for a specific signed in user. getUserProfiles returns an array of UserProfile objects.

Usage
import { useUserService } from '@axinom/mosaic-user-auth';

const { getUserProfiles } = useUserService();

const userProfiles = await getUserProfiles();

createUserProfile

This method can be used to create a new user profile for a user.

Usage
import { useUserService } from '@axinom/mosaic-user-auth';

const { createUserProfile } = useUserService();

const handleAddProfileClick = async () => {
  await createUserProfile(displayName);
};

updateUserProfile

This method can be used to update any profile related information for a user.

Usage
import { useUserService } from '@axinom/mosaic-user-auth';

const { updateUserProfile } = useUserService();

const [userProfiles, setUserProfiles] = useState<UserProfile[] | undefined>(
    undefined,
  );

const handleEditProfileClick = async (userProfileToUpdate: UserProfile) => {
  const profileIndex = userProfiles?.findIndex(
    (profile) => profile.id === userProfileToUpdate.id,
  );

  if (profileIndex !== undefined) {
    const updatedUserProfiles = [...(userProfiles ?? [])];
    updatedUserProfiles[profileIndex] = {
      ...updatedUserProfiles[profileIndex],
      ...userProfileToUpdate,
      profilePictureUrl: `https://avatars.dicebear.com/api/bottts/${tokenResponse?.userToken?.userId}-${userProfileToUpdate.displayName}.svg`,
    };

    setUserProfiles(updatedUserProfiles);
  }

  await updateUserProfile(userProfileToUpdate);
};

deleteUserProfile

This method can be used to delete a user profile.

Usage
import { useUserService } from '@axinom/mosaic-user-auth';

const { updateUserProfile } = useUserService();

const [userProfiles, setUserProfiles] = useState<UserProfile[] | undefined>(
    undefined,
  );

const handleDeleteProfileClick = async (profileIdToDelete: string) => {
  setUserProfiles(
    userProfiles?.filter((profile) => profile.id !== profileIdToDelete),
  );

  await deleteUserProfile(profileIdToDelete);
};

authenticateEndUserApplication

There can be instances where the application developers need to display certain information retrieved from an end-user service, without having a signed in user.

For example, the application needs to list down all the available subscription plans a user can select from when a user registers for the service. At this point, there is no user signed in, but a valid authorization token is required to make the GraphQL call to the Subscription service. Application Tokens can be used for this purpose.

The idea is that just like a user is authenticated, an Application registered in the User Service can be authenticated and in return an access token tailor made for an end-user application can be received. This token can then be used to call any end-user facing GraphQL services, that does not require a signed in user.

authenticateEndUserApplication returns an AuthenticateEndUserApplicationResponse. A EndUserApplicationToken object can be extracted through the endUserApplicationToken property.

Usage
import { useUserService } from '@axinom/mosaic-user-auth';

const { authenticateEndUserApplication } = useUserService();

const {endUserApplicationToken} = await authenticateEndUserApplication({tenantId, environmentId, applicationId, applicationKey});