5 Simple Steps to Integrate Your App with Affinidi Login

In 5 simple steps, get started with the Affinidi Trust Network for enhanced User Identity Management and Data Privacy.

New Ways to do Familiar Things

What are We Building

A simple javascript application where an authenticated user would be able to view the identity token (idToken) issued to the application by the identity provider in this process.

What is Familiar

This application would use protocol to reach out to the identity provider, much like Social Logins (Login with Google/Facebook/…). The application would receive the idToken from the identity provider, the way OIDC guarantees.

What is New

Familiar ways to do new things

The identity provider for this application would be its USERS, not a third-party system. When a user requests sign-in; they would be empowered to prove who they are, using their own identity without relying on passwords.

This Secure, Efficient, Privacy Preserving NEW way would enable this application to provide a better personal experience to the user; since the application would not just sign-in a user but can also sign them up without any forms.

Before you begin
  1. Set up Affinidi Vault account. This vault would enable users of our application to collect, store and share their identity; their data and to act as their own identity provider
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 : This is an open source Javascript runtime environment, much like the runtime that your browser uses to render Javascript applications.

  2. Optionally, 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

#Lets Build

Step 1 : Set up Your Application

In this step, we would create our package structure and then turn this application into a Node.js application. We will then create the file (index.js) which would represent our entire application. We will also create an environment file to hold all the secrets.

And finally we will install the node packages, which would help us build the required components.

mkdir hello-world-affinidi-login
cd hello-world-affinidi-login

npm init -y
touch index.js
touch .env

npm install express dotenv passport openid-client express-session

# Why we do need each of these modules
#
# express is a minimalist web framework for Node.js
# dotenv helps loads environment variables from .env, created above
# passport is express-compatible authentication framework for Node.js
# openid-client is Relying-Party implementation for Node.js, which is passport compatible
# express-session is session middleware for express, required by passport

To create a application with authentication support, let’s list down the required structure. We shall build on this as we go.

File Path : index.js

//  1. Import required Libraries
//  2. Create an express application and setup session
//  3. Define application end-points
//  4. Start the http server
//  5. Integrate Authentication

Step 2 : Implement Core Logic for the Application

In this step, we will build the core logic of our application following the requirements that we created above

File Path : index.js

//  1. Import required Libraries
const express = require('express');
const passport = require('passport');
const session = require('express-session');
const { Issuer, Strategy } = require('openid-client');
const http = require("http");
require("dotenv").config();

//  2. Create an express application and setup session
const app = express();
app.use(session({
    secret: 'my-secret',
    resave: false ,
    saveUninitialized: false
}));

// 3. Define application end-points
app.get("/", (req, res) => {
    res.send(" Lets build a Unified Digital Identity <br/><br/><a href='/login'> Affinidi Login</a>");
})

app.get('/login',
    function (req, res, next) {
        next();
    },
);

app.get('/login/callback', (req, res, next) => {})

app.get("/protected", (req, res) => {
    res.header("Content-Type", 'application/json');
    res.end("Protected Content for Authenticated Users");
})

// 4. Start the http server
const httpServer = http.createServer(app)
httpServer.listen(8080, () => {
    console.log(`Hello World - Affinidi Login : Up and running on 8080`)
})

For better housekeeping, let’s first create 3 environment variables

File Path : .env

issuer= #Verifiable Identifier for the issuer of claims, in this case, Affinidi Login 
client_id=  #Public Identifier for our application, issued by Affinidi Login
client_secret= #Secret known to our application

Step 3 : Integrate Authentication to the Application

In this step, we would create the authentication strategy ‘affinidi-login’ for our application using openid-client. This strategy would then be provided to the passport middleware.

// 5. Integrate Authentication
// 5a. Discover Affinidi Login - Issuer
Issuer.discover(process.env.issuer).then(function (oidcIssuer) {
  // 5b. Create a RP-client which can initiate an OIDC flow
  var client = new oidcIssuer.Client({
    client_id: process.env.client_id,
    client_secret: process.env.client_secret,
    redirect_uris: ["http://localhost:8080/login/callback"],
    response_types: ['code'],
    token_endpoint_auth_method: 'client_secret_post'
  });

  // 5c. Provide this strategy to the passport middleware
  passport.use(
    'affinidi-login', new Strategy({ client, passReqToCallback: true }, (req, tokenSet, userinfo, done) => {
      req.session.tokenSet = tokenSet;
      req.session.userinfo = userinfo;
      return done(null, tokenSet.claims());
    }));
});

passport.serializeUser(function (user, done) {
  done(null, user);
});
passport.deserializeUser(function (user, done) {
  done(null, user);
});

Step 4 : Create Application Routes to use Authentication

In this step, we will integrate the authentication from last steps into our application routes. From the landing page(/), we will invoke (/login), utilising the configured passport strategy to authenticate users.

Here is what the final index.js would look like

FilePath : index.js

//  1. Import required Libraries
const express = require('express');
const passport = require('passport');
const session = require('express-session');
const { Issuer, Strategy } = require('openid-client');
const http = require("http");
require("dotenv").config();

//  2. Create an express application and setup session
const app = express();
app.use(session({
    secret: 'my-secret',
    resave: false ,
    saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());

// 3. Define application end-points
app.get("/", (req, res) => {
    res.send(" Lets build a Unified Digital Identity <br/><br/><a href='/login'> Affinidi Login</a>");
})

app.get('/login',
    function (req, res, next) {
        next();
    },
    passport.authenticate('affinidi-login',{scope:'openid'})
);

app.get('/login/callback', (req, res, next) => {
    passport.authenticate('affinidi-login', {successRedirect: '/protected', failureRedirect: '/'})(req,res,next)
})

app.get("/protected", (req, res) => {
    res.header("Content-Type", 'application/json');
    res.end(JSON.stringify(req.user, null, 4));
})

// 4. Start the http server
const httpServer = http.createServer(app)
httpServer.listen(8080, () => {
    console.log(`Hello World - Affinidi Login : Up and running on 8080`)
})

// 5. Integrate Authentication
// 5a. Discover Affinidi Login - Issuer
Issuer.discover(process.env.issuer).then(function (oidcIssuer) {
    // 5b. Create a RP-client which can initiate an OIDC flow
    var client = new oidcIssuer.Client({
      client_id: process.env.client_id,
      client_secret: process.env.client_secret,
      redirect_uris: ["http://localhost:8080/login/callback"],
      response_types: ['code'],
      token_endpoint_auth_method: 'client_secret_post'
    });
  
    // 5c. Provide this strategy to the passport middleware
    passport.use(
      'affinidi-login', new Strategy({ client, passReqToCallback: true }, (req, tokenSet, userinfo, done) => {
        req.session.tokenSet = tokenSet;
        req.session.userinfo = userinfo;
        return done(null, tokenSet.claims());
      }));
  });
  
  passport.serializeUser(function (user, done) {
    done(null, user);
  });
  passport.deserializeUser(function (user, done) {
    done(null, user);
  });

Step 5 : Integrate Affinidi Login and Run Your Application

What we have built so far is an application that can initiate OIDC flow with an OpenIDProvider, authenticate the users and receive an identity token back. This token is then displayed on the (/protected) page.

In order for this application to utiliise Affinidi Login, we need to create a Login Configuration. For this, you can either use Affinidi CLI or  Affinidi Portal.

While creating the login-configuration, use redirect-uri : http://localhost:8080/login/callback. You may provide a name of your choice and leave all other options as default

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='Hello World App' \
--redirect-uris='http://localhost:8080/login/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": "My Basic App",
  "auth": {
    "clientId": "<AUTH.CLIENT_ID>",
    "clientSecret": "<AUTH.CLIENT_SECRET>",
    "issuer": "https://<PROJECT_ID>.apse1.login.affinidi.io"
  },
  "redirectUris": [
    "..."
  ],
  "clientMetadata": {
    "name": "My Basic App",
    "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.

After using either of these methods, you would receive Client Id, Client Secret and Issuer(auth.issuer) from the response. Replace these values in .env file, that we created in Step 2

File Path : .env

client_id=cliEnt0101-07d5-xxxx-1111-d2de03f62456
client_secret=mYsEcReTmYsEcReT
issuer=https://<PROJECT_ID>.apse1.login.affinidi.io

Run the application and test the integration at http://localhost:8080

node index.js

Summary

After the user authentication is complete, the home page (/protected) shows the identity token that was issued for the application usage. This token was generated based on the login configuration we created in the final step above.

Here is the sample identity token. Under the custom node are the claims that are returned to you through user’s consent as zero-party data. You receive this data straight from the users of your applications, not from 3rd party systems.

{
    "acr": "0",
    "at_hash": "0A1HHZ4xu0Cpv0gLKqnZ_Q",
    "aud": [
        "eed5f015-07d5-1111-893c-d2de03f62456"
    ],
    "auth_time": 1698111980,
    "custom": [
        {
            "type": [
                "VerifiableCredential",
                "Email"
            ]
        },
        {
            "email": "test@gmail.com"
        },
        {
            "did": "did:key:..."
        }
    ],
    "exp": 1698112882,
    "iat": 1698111982,
    "iss": "https://<PROJECT_ID>.apse1.login.affinidi.io",
    "jti": "d96ea1d4-f8d1-aaBB-ccDD-e3d627d1bde9",
    "rat": 1698111949,
    "sid": "26c7c401-xxYY-4cb1-880c-435599ad6b1a",
    "sub": "did:key:..."
}

Bonus

Let’s say you want to know more about your users in order to provide better services without the users having to fill out repeated forms and get more accurate data.

Instead of relying on the B2B integrations that you do today, limited by the data offered by the 3rd party system; let’s see how you can do the same in this new world.

All you need to do is modify the Login Configuration created above (i.e. modify the presentation definition and the idTokenMapping) or refer to this documentation on how to customise the Presentation Definition and ID Token Mapping for your Login requirements.

And ? That’s it, that’s all that you need.

Download the sample JSON file here . This file defines a login configuration where an application requires multiple user’s identity attribute from user’s vault and in a specfic defined formal of idToken.

affinidi login update-config --id=<login-config-id-created-above> --file="profile-pex.json"

Run the application again to see the difference in the custom token that now comes back to this application.

In this simple Hello-World, we created a application, integrated it with Affinidi Login and received zero party data from the users in just 5 simple steps. As a developer, this enables you to build privacy preserving applications which are secure and which respects user’s ownership of their identity. Better yet, we did all this in a way which does not require us to change the way we do things. Keep building.