New Ways to do Familiar Things
In 5 simple steps, get started with the Affinidi Trust Network for enhanced User Identity Management and Data Privacy.
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 OpenID Connect 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
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
- Set up Affinidi Vault browser extension. 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
- Install the Affinidi Vault Browser Extension via Google Chrome store.
Affinidi Vault
- Click the Affinidi Vault Chrome extension found in the Extensions of the Chrome browser. Click START and provide a valid email address. If you have a backup of your Vault created previously, you can use RESTORE VAULT FROM BACKUP.
- Enter the 6-digit verification code sent to the email address you provided in the previous step and click verify.
- Secure your Vault by providing a secure passphrase. Use this passphrase to unlock your Vault.
Finally, you should see on the screen that you have successfully registered to the Affinidi Vault.
Vault Passphrase
Remember to keep your passphrase in a secure location since it cannot be retrieved if forgotten, even with your backup. If you forget it, you must reinstall and reconfigure your Vault.
- Install Node.js : This is an open source JavaScript runtime environment, much like the runtime that your browser uses to render JavaScript applications.
- Optionally, install the Affinidi CLI. Follow the guide below if it hasn’t been installed.
Set up Affinidi CLI
- Download and install NodeJS on your machine if you haven’t set it up yet.
Node Version
Affinidi CLI requires Node version 18 and above.
- Install Affinidi CLI using Node Package Manager (npm).
npm install -g @affinidi/cli
- 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
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
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
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();
const app = express();
app.use(session({
secret: 'my-secret',
resave: false ,
saveUninitialized: false
}));
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");
})
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=
client_id=
client_secret=
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.
Issuer.discover(process.env.issuer).then(function (oidcIssuer) {
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'
});
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
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();
const app = express();
app.use(session({
secret: 'my-secret',
resave: false ,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
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));
})
const httpServer = http.createServer(app)
httpServer.listen(8080, () => {
console.log(`Hello World - Affinidi Login : Up and running on 8080`)
})
Issuer.discover(process.env.issuer).then(function (oidcIssuer) {
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'
});
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
- Log in to Affinidi CLI by running:
affinidi start
- 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
- Go to Affinidi Login under the Services section.
- 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.
- Click on create and confirm if all the details are correct.
- 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.
- 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 specific 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.