Integrate in React Native

MIRACL offers a JS client library which implements the client side operations of the M-Pin authentication protocol.

To get access to the MIRACL JS client library, please send an inquiry to sales@miracl.com

Configuration & setup

Before using the library you need to first create a configured instance of the client:

new Client({
  customerId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
  clientId: 'xxxxxxxxxxxxx',
  redirectUri: 'https://example.com/login',
  seed: hexSeed,
  userStorage: storage,
});

The customerId and clientId you can obtain by following the Get started guide. We recommend setting up a separate redirectUri for the application in the MIRACL Trust Console, although this will not be used for OIDC redirection.

The seed for the random number generator

The seed is used for initializing the random number generator necessary for the security of the authentication protocol. Let us use the react-native-securerandom library in order to generate the seed:

import { generateSecureRandom } from 'react-native-securerandom';

export async function generateHexSeed() {
  const randomBytes = await generateSecureRandom(8);

  let hexSeed = "";
  randomBytes.forEach(b => {
    hexSeed += b.toString(16);
  });

  return hexSeed;
}

The storage

The MIRACL client needs persistent storage for the identity data stored during registration and used for authentication. The client does not support asynchronous storage and expects an object, which has the following methods implemented:

  • setItem(name, value)
    name: string
    value: string
  • getItem(name)
    name: string
    returns string

In order to implement both this interface and avoid the asynchronous nature of most persistent storage mechanisms available in React Native, we can create a wrapping class. The class will keep an in-memory cache of the data in storage and maintain consistency by synchronising with the async storage. Here is an example using @react-native-community/async-storage:

import AsyncStorage from '@react-native-community/async-storage';

export default class UserStorage {
  storage = {};
  initialized = false;

  // The instance needs to be initialised asynchronously
  // before passing it to the MIRACL client
  async init() {
    if (this.initialized) {
      return this;
    }

    this.storage.mfa = await AsyncStorage.getItem('mfa');
    this.initialized = true;

    return this;
  }

  setItem(name, value) {
    this.storage[name] = value;
    return AsyncStorage.setItem(name, value);
  }

  getItem(name) {
    return this.storage[name] ? this.storage[name] : null;
  }
}

Although MIRACL authentication does not rely on the security of the storage, we recommend using a secure storage alternative. You can read more in the Secure Storage section of the React Native official documentation.

Creating only one instance

As we don’t want to create multiple unnecessary instances of the MIRACL client, we can use an instance manager pattern, which will create and configure just one instance of the client and then pass that instance to any requester.

import Client from '@miracl/client-js';

const Manager = {
  instance: null,

  configure: function (config) {
    this.instance = new Client(config);
  },

  getInstance: function () {
    if (!this.instance) {
      throw new Error('Not configured. First call the configure method.');
    }

    return this.instance;
  },
};

export default Manager;

This pattern works due to the ES6 module system. This module (where the object is defined) will only be loaded once, even if it’s imported in multiple places. This loosely ensures that there will only be one instance of the manager and so only one instance of the MIRACL client. This also gives the benefit of configuring the client in just one place and then reusing it where needed.

Higher-order component

In order to use the MIRACL client in your components you can utilise the higher-order component (HOC) technique. Here is an example of a HOC implementation:

import React, {Component} from 'react';

export function withMiracl(WrappedComponent) {
  return class extends Component {
    render() {
      return (
        <WrappedComponent
          miracl={Manager.getInstance()}
          {...this.props}
        />
      );
    }
  };
}

Although you can use the manager directly in any component that needs an instance to the MIRACL client, the HOC technique has the benefit of configuring and injecting the client in just one place. You can see an example in the following section.

Tie it all together

With all of the setup we’ve done in the previous sections our App.js can now tie everything together:

import React, {Component} from 'react';
import UserStorage from './miracl/UserStorage';
import Miracl, {withMiracl, generateHexSeed} from './miracl/Miracl';

import HomeScreen from './screens/Home';
import LoadingScreen from './screens/Loading';

// Create the HOC that will have the
// MIRACL client instance as a property
const HomeScreenWithMiracl = withMiracl(HomeScreen);

export default class App extends Component {
  state = {
    ready: false,
  };

  // We can use the async support for the lifecycle methods
  // to ensure that all of the setup is complete before
  // attempting any operations
  async componentDidMount() {
    const storage = await new UserStorage().init();
    const hexSeed = await generateHexSeed();

    Miracl.configure({
      customerId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
      clientId: 'xxxxxxxxxxxxx',
      redirectUri: 'https://example.com/login',
      seed: hexSeed,
      userStorage: storage,
    });

    // Change the state after everything is configured
    this.setState({ready: true});
  }

  render() {
    // Don't render the home screen until setup is complete
    if (this.state.ready) {
      return <HomeScreenWithMiracl />;
    } else {
      return <LoadingScreen />;
    }
  }
}

Now we can use the MIRACL client in the Home screen:

import React, {Component} from 'react';

export default class Home extends Component {
  componentDidMount() {
    // Get the injected MIRACL client instance
    const miracl = this.props.miracl;

    // Print a list of registered users
    console.log(miracl.users.list());
  }
}

Identity verification

In order to register an identity, you must first obtain a registration code from MIRACL Trust. You can do this by using either the default MIRACL Trust verification process or using pluggable verification.

Pluggable verification

If you have custom(pluggable) verification configured for your project, you can follow the Pluggable Verification guide to obtain the registration code. Then you can simply call the register method from a component as described in the Registration section.

Default verification

If you are using the default (email) verification, you can start the process by calling the verify method:

import React, {Component} from 'react';

export default class Verification extends Component {
  verify(emailAddress) {
    this.props.miracl.verify(emailAddress, (err, data) => {
        if (err) {
          return console.error(JSON.stringify(err));
        }

        console.log(data);
      }
    );
  }
}

This will send an email to the provided address with a verification link. The verification link needs to be handled as a deep link by your application. You can follow the React Native linking documentation to see how to set up the handling of that link. When the user opens the email link and the app is launched, you need to call the confirm method in order to finish the verification process and obtain a registration code:

import React, {Component} from 'react';

export default class Confirmation extends Component {
  // Parse the deep link URI and return the query parameters
  parseParams(URI) {
    const regex = /[?&]([^=#]+)=([^&#]*)/g;

    let params = {},

    var match;
    while (match = regex.exec(url)) {
      params[match[1]] = match[2];
    }

    return params;
  }

  confirm(initialURL) {
    const params = this.parseParams(initialURL);

    this.props.miracl.confirm(params, (err, data) => {
      if (err) {
        return console.error(JSON.stringify(err));
      }

      console.log(data);
    });
  }
}

Registration

After you have the registration code, you only need to call the register method in order to create and store the authentication identity on the device.

import React, {Component} from 'react';

export default class Registration extends Component {
  register(userId, registrationCode) {
    this.props.miracl.register(
      userId,
      registrationCode,
      (passPin) => {
        // Here you should prompt the user to enter a PIN
        passPin('1111');
      },
      (err, data) => {
        if (err) {
          return console.error(JSON.stringify(err));
        }

        console.log(data);
      }
    );
  }
}

Authentication

After we have a registered identity, authenticating is as simple as calling the authenticate method:

import React, {Component} from 'react';

export default class Authentication extends Component {
  authenticate(userId, pin) {
    this.props.miracl.authenticate(userId, pin, (err, data) => {
      if (err) {
        console.log(JSON.stringify(err));
      }

      console.log(JSON.stringify(data));
    });
  }
}