Affinidi Login with React

In this guide, learn how to enable passwordless login in your application with the React component.

In this lab, we will use the Create React App to create a basic application and import the affinidi-react-auth and passport-affinidi libraries developed by Affinidi Team.

Before you begin
  1. Set up Affinidi Vault account. Follow the guide below if you haven’t set it up yet.
Set up Affinidi Vault
  1. Set up an Affinidi Vault account using the web app or install the mobile app .

The same installation steps for mobile app.

  1. Click on Start if you are creating a new account, or click on Restore from Backup if you have an existing backup of your Affinidi Vault.

Use this guide to learn how to migrate your existing Affinidi Vault account.

Affinidi Vault Setup
  1. Secure your Vault by providing a passphrase. Use this passphrase to unlock your Vault.
Affinidi Vault Passphrase
  1. Provide your Email Address to verify with OTP.
Affinidi Vault Email Verification

After successfully providing the OTP, you are redirected to your Affinidi Vault dashboard.

  1. Install the Affinidi CLI. Follow the guide below if it hasn’t been installed.
Set up Affinidi CLI
  1. Download and install NodeJS on your machine if you haven’t set it up yet.
  1. Install Affinidi CLI using Node Package Manager (npm).
npm install -g @affinidi/cli
  1. Verify that the installation is successful.
affinidi --version

Download Application

You can download the Client App using React and Server App using Express for the backend and explore how to easily integrate Affinidi Login to provide a passwordless login experience for your end-users.

How it Works

Using the two libraries, we can effortlessly enable the passwordless login experience for the end-users into your applications.

affinidi-react-auth

It is a React component library that provides the login button to enable the passwordless login through Affinidi Login into the React frontend applications.

passport-affinidi

It is a library that leverages Passport to enable OpenID Connect authentication flow into the applications that support Connect-style middleware and seamlessly integrates with Affinidi Login.

It provides functionality to initialise the authentication flow using the client credentials generated from the Login Configuration and parse the idToken received from the Affinidi Login after successful user authentication.

  • Initialization Route: A GET route (/api/affinidi-auth/init) that returns the Affinidi authorisation URL and redirects the frontend applications to Affinidi Login flow.

  • Completion Route: A POST route (/api/affinidi-auth/complete) that processes the response (code and state) from Affinidi Login flow and performs the exchange for the ID Token that returns the user’s information.

Create a React Application

To start quickly with our lab, we will use the Create React App tool to create a basic application.

npx create-react-app client-app

Once the application is generated, go to the client-app directory.

Run npm start to start the application.

Enable Affinidi Login

Once we confirm that the generated application is working, we will enable the Affinidi Login through the affinidi-react-auth library.

  1. Install the affinidi-react-auth library from NPM.
npm install @affinidi/affinidi-react-auth
  1. To proxy unknown requests to your API server during development, add a proxy field to your package.json like this:
 "proxy": "http://localhost:3001"

You can find the sample package.json here

After installing the library and configuring the package.json, we modify the src/App.js file and paste the following codes to enable Affinidi Login component.

  1. Import the env variable and libraries required.
import logo from './logo.svg';
import './App.css';
import React from "react"

import { AffinidiLoginButton, useAffinidiProfile } from '@affinidi/affinidi-react-auth'

const apiBaseUrl = process.env.REACT_APP_SERVER_URI || '';
  1. Retrieve the user information after successful authentication using useAffinidiProfile hook and add the logout function.
const { isLoading, error, profile, handleLogout } = useAffinidiProfile({
     authCompleteUrl: `${apiBaseUrl}/api/affinidi-auth/complete`
  })

async function logout() {
    //clear session cookie
    handleLogout();
    window.location.href = "/";
}
  1. Display the Affinidi Login button, loading indicator, user profile, and error messages using the below code.
{!profile && <>
    <AffinidiLoginButton authInitUrl={`${apiBaseUrl}/api/affinidi-auth/init`} />
</>}

{isLoading && <p>Loading...</p>}

{profile && <>
    <button style={{ marginRight: 10 }} onClick={logout}>
    Logout
    </button>

    <h3>User Profile</h3>
    <pre style={{ textAlign: "left" }}>{JSON.stringify(profile, null, 4)}</pre>
</>}

{error && <><h2>error</h2>{error}</>}

After implementing the required codes, restart the application. The Affinidi Login button shows on the homepage.

In cases where proxy settings is not working on the app with your hosting provider, define the REACT_APP_SERVER_URI in the .env with the base url of your Express server.

You can find the sample App.js implementation here .

Create an Express Server

We will use the Express framework as the backend to enable OIDC flow to integrate with Affinidi Login.

  1. Create a folder named server-app and navigate to it.
mkdir server-app
cd server-app
  1. Initialize the project by creating a package.json file.
npm init -y

You can find the sample package.json here

  1. Install the required packages.
  • express for creating the server.
  • dotenv for managing environment variables.
  • nodemon for automatic reloading.
npm install express dotenv nodemon cors
  1. Create an index.js file and set up a basic Express server.
var express = require('express');
var cors = require('cors');
require('dotenv').config()

var app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

const PORT = process.env.PORT || 3001;

const initializeServer = async () => {

    app.get('/', function (req, res, next) {
        res.json({ success: 'Express' });
    });

    app.use(cors({ credentials: true, origin: true }));
    app.set('trust proxy', 1);

    app.listen(PORT, () => {
        console.log(`Server listening on ${PORT}`);
    });

}

initializeServer();
  1. Add the following script to your package.json file to start the server with nodemon.
"start": "nodemon index.js"

Start the server using the following command:

npm start

Enable Affinidi Provider

To integrate Affinidi Login to the Express Server app that we just created, we have to install the passport-affinidi library and implement the affinidiProvider.

  1. Create a .env file in the Express Server app and set the Login Configuration auth details created previously.
AFFINIDI_CLIENT_ID="<AUTH.CLIENT_ID>"
AFFINIDI_CLIENT_SECRET="<AUTH.CLIENT_SECRET>"
AFFINIDI_ISSUER="<AUTH.ISSUER>"
  1. Install the passport-affinidi library in the Express Server app.
npm install @affinidi/passport-affinidi
  1. Modify the index.js file to import the required module.
const { affinidiProvider } = require('@affinidi/passport-affinidi')
  1. Initialise the Affinidi Provider inside the initializeServer async function before the app.listen call.
await affinidiProvider(app, {
    id: "affinidi",
    issuer: process.env.AFFINIDI_ISSUER,
    client_id: process.env.AFFINIDI_CLIENT_ID,
    client_secret: process.env.AFFINIDI_CLIENT_SECRET,
    redirect_uris: ['http://localhost:3000/auth/callback']
});

You can find the sample index.js implementation here .

Create a Login Configuration

To create a Login Configuration, you can either use Affinidi CLI or  Affinidi Portal.

Expand the section below for your preferred method:

Name: Affinidi Login App

Redirect URIs: http://localhost:3000/auth/callback

Using Affinidi CLI
  1. Log in to Affinidi CLI by running:
affinidi start
  1. Once you have successfully logged in, create the Login Configuration by running:
affinidi login create-config \
--name='Affinidi Login App' \
--redirect-uris='http://localhost:3000/auth/callback'
  • --name is what you want your login configuration to be called.
  • --redirect-uris is the URL on your application where the user gets redirected after the successful authentication.

Sample response:

{
  "ari": "ari:identity:ap-southeast-1:687b8872-a618-dt63-8978-e72ac32daeb1:login_configuration/c4f74d936cd31bde1c1fd3c1050bb76s",
  "projectId": "687b8872-a618-4e52-8978-e72ac32daec2",
  "configurationId": "c4f74d936cd31bde1c1fd3c1050bb62d",
  "name": "...",
  "auth": {
    "clientId": "<AUTH.CLIENT_ID>",
    "clientSecret": "<AUTH.CLIENT_SECRET>",
    "issuer": "https://<PROJECT_ID>.apse1.login.affinidi.io"
  },
  "redirectUris": [
    "..."
  ],
  "clientMetadata": {
    "name": "Login Config Name",
    "logo": "https://login.affinidi.com/default-client-logo.svg",
    "origin": "https://example.com"
  },
  "creationDate": "2023-08-11T06:26:37Z",
  "tokenEndpointAuthMethod": "client_secret_post"
}

Learn more on how to manage your Login Configurations using Affinidi CLI.

Using Affinidi Portal
Create new Login Configuratioin
  1. Go to  Affinidi Login under the Services section.

  2. Click on the Create Login Configuration and provide the required details.

  • Name is the string that describes your login configuration.
  • Redirect URIs is the URL on your application where the user gets redirected after the successful authentication.
  1. Click on create and confirm if all the details are correct.
Login Configuratation new client
  1. After confirming the details, another popup shows the Client ID and Client Secret for your Login Configuration. Copy the generated Client Credentials and use them to integrate with Affinidi Login.

  2. After copying the Client ID and Client Secret and closing the popup, you are redirected back to the Affinidi Login page.

Login Configuration uses the default Presentation Definition (presentationDefinition) and ID Token Mapping (idTokenMapping) that is used to request the user’s email address during the authentication flow.

Update Client Credentials

After generating the Login Configuration successfully, go back to the Express server and update the .env file that was created previously.

  • AFFINIDI_CLIENT_ID is the auth.clientId value from the Login Configuration.
  • AFFINIDI_CLIENT_SECRET is the auth.clientSecret value from the Login Configuration.
  • AFFINIDI_ISSUER is the auth.issuer value from the Login Configuration.

After completing the setup on the Express Server, restart the application.

Testing Affinidi Login Integration

After completing the setup on both the Client App (React) and Server App (Express), we can access the Client App and click on the Affinidi Login button to trigger the OID4VP authentication flow.

Make sure both application is running to test the implementation.

If the integration is successful, it should go through the Affinidi Login flow, where you will be requested to share your Email VC from the Vault to verify your identity. After you consent to share your Email VC, the user is redirected to the Client App with the information.

Learn more about how Affinidi Login works here.

Retrieving Profile with Affinidi Login

By default, Affinidi Login request for the Email VC that is available in the user’s Vault. To retrieve additional information like User Profile from the Vault, we will update the default Presentation Definition and ID Token Mapping of the Login Configuration that we have created previously.

  1. Download the JSON payload that contains the updated presentationDefinition and idTokenMapping that request for the user profile from the Vault aside from the default Email Address.

  2. Update the login config using CLI, execute the below command:

affinidi login update-config \
--id=<LOGIN_CONFIG_ID> \
--file=<PATH_TO_JSON_FILE>/profile-pex.json

Where the LOGIN_CONFIG_ID is the ID of the Login Configuration we have created previously and PATH_TO_JSON_FILE where we saved the JSON payload we downloaded from the previous step.

Once you successfully update the Login Configuration, you can go to the Client App again and trigger the Affinidi Login.

After the successful authentication, you should receive the user profile data and email address.