Affinidi Login with NextJS
The Affinidi Login can be integrated with any application that supports OIDC flow.
This lab uses NextJS and NextAuth.js as the framework to implement Affinidi Login.
Before you begin
- Set up Affinidi Vault account. Follow the guide below if you haven’t set it up yet.
Get the Redirect URI of your application for OIDC. This is the URI configured on your Login Configuration to receive the idToken after successful authorisation.
Optionally, install the Affinidi CLI. Follow the guide below if it hasn’t been installed.
Download Application
You can clone this sample application using Next.js framework from our Github and start exploring how to integrate Affinidi Login to provide a passwordless login experience for your end-users.
Important Note
The downloadable sample application is provided only as a guide to quickly explore and learn how to integrate the components of Affinidi Trust Network into your application. This is NOT a Production-ready implementation. Do not deploy this to a production environment.Running the Application
- Suppose this is your first time downloading the sample application. Run the following command to install the dependencies.
npm install- Create the
.envfile in the sample application by running the following command.
cp .env.example .envIf you open the sample application in a Code Editor, duplicate the
.env.exampleand rename it.env.
- After installing the dependencies, run the application by running the command.
npm run devRunning the application for the first time without configuring the
.envfile will throw an exception error. We will configure the required environment variables in the following steps of this guide.
You may refer to the included README.md file for more details.
Create 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: My first config
Redirect URIs: http://localhost:3000/api/auth/callback/affinidi
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.
Important
Safeguard the Client ID and Client Secret diligently; you'll need them for setting up your IdP or OIDC-compliant applications. Remember, the Client Secret will be provided only once.
Set up the Sample Application
In this guide, learn how to integrate Affinidi Login using NextJS and NextAuth.js framework.

Configure .env file
Set the environment variables based on the auth credentials received from the Login Configuration created earlier:
{
"auth": {
"clientId": "<AUTH.CLIENT_ID>",
"clientSecret": "<AUTH.CLIENT_SECRET>",
"issuer": "https://<PROJECT_ID>.apse1.login.affinidi.io"
}
}Set the following fields in the .env file
PROVIDER_CLIENT_ID="<AUTH.CLIENT_ID>"
PROVIDER_CLIENT_SECRET="<AUTH.CLIENT_SECRET>"
PROVIDER_ISSUER="<AUTH.CLIENT_ISSUER>"Configure NextAuth for Affinidi Login
Using the NextAuth.js library, configure the nextauth file to enable Affinidi Login as a login provider and set up the JWT and Session from the idToken sent by the Affinidi Login after the user successfully authenticates.
Add Affinidi Login as a Login Provider
In the code below, Affinidi Login is added as one of the Login Providers for NextAuth.js. The credentials generated by the Login Configuration are configured here and sent to the Affinidi Login service as part of the payload.
File path:
src/lib/auth/auth-provider.ts
export const provider: Provider = {
id: "affinidi",
name: "Affinidi",
clientId: providerClientId,
clientSecret: providerClientSecret,
type: "oauth",
wellKnown: `${providerIssuer}/.well-known/openid-configuration`,
authorization: {
params: {
prompt: "login",
scope: "openid offline_access",
},
},
client: {
token_endpoint_auth_method: "client_secret_post",
},
idToken: true,
profile(profile) {
return {
id: profile.sub,
email: profile.custom?.find((i: any) => typeof i.email === "string")
?.email,
};
},
};Generate JWT and Session from the idToken
In the code below, the JWT and the session are generated from the idToken received from Affinidi Login following successful user authentication.
File path:
src/lib/auth/auth-options.ts
async jwt({ token, account, profile }) {
const profileItems = (profile as any)?.[PROVIDER_ATTRIBUTES_KEY];
if (profile && profileItems) {
let userDID: string;
let user: UserInfo = {};
userDID = profileItems.find(
(item: any) => typeof item.did === "string"
)?.did;
user.email = profileItems.find(
(item: any) => typeof item.email === "string"
)?.email;
user.country = profileItems.find(
(item: any) => typeof item.address === "object"
)?.address?.country;
token = {
...token,
user,
...(userDID && { userId: userDID }),
};
}
if (account) {
token = {
...token,
...(account?.access_token && { accessToken: account.access_token }),
...(account?.id_token && { idToken: account.id_token }),
};
}
return token;
},
// session is persisted as an HttpOnly cookie
async session({ session, token }) {
return {
...session,
...(token.user && { user: { ...session.user, ...token.user } }),
...(token.accessToken && { accessToken: token.accessToken }),
...(token.idToken && { idToken: token.idToken }),
...(token.userId && { userId: token.userId }),
};
},The above files are then declared in the NextAuth file to register the login provider and the functions to handle the idToken response from Affinidi Login.
File path:
src/pages/api/auth/[...nextauth].ts
import { authOptions } from "src/lib/auth/auth-options";
import NextAuth from "next-auth";
export default NextAuth(authOptions);Additionally, you will notice in line 31 that we are parsing the country field. This optional process requires updating the Presentation Definition and ID Token Mapping of the Login Configuration to request the user profile from the Vault.
user.country = profileItems.find(
(item: any) => typeof item.address === "object"
)?.address?.country;You can refer to this guide to learn how to update the Presentation Definition and ID Token Mapping.
Enable Affinidi Login
In the code below, we are enabling Affinidi Login into the Login page of the sample app and referencing the NextAuth provider configured earlier.
File path:
src/lib/auth/client-login.ts
import { signIn } from "next-auth/react";
import { hostUrl } from "src/lib/variables";
export async function clientLogin() {
await signIn("affinidi", { callbackUrl: hostUrl });
}We include the above file in the SignIn page of the sample app and attach the event in the Login button to initiate the authentication flow.
<Button variant="primary" onClick={clientLogin}>
<Image src={LogoAffinidi} alt="logo affinidi" />
Affinidi Login
</Button>Summary
sequenceDiagram
actor User
participant Website
participant Affinidi Login
participant Affinidi Vault
participant Affinidi Verifier
User->>Website: My Login
Website->>Affinidi Login: Authenticate user
Note over Website, Affinidi Login: login_challenge
Affinidi Login->>Affinidi Vault: Verify user identity
Note over Affinidi Login, Affinidi Vault: presentationDefinition
Affinidi Vault->>User: Request user confirmation to share Email VC
User->>Affinidi Vault: User confirmed consent to share Email VC
Affinidi Vault->>Affinidi Vault: Generate VP Token from VC
Affinidi Vault->>Affinidi Login: Send Email VP Token
Affinidi Login->>Affinidi Verifier: Validate VP Token
Note over Affinidi Login, Affinidi Verifier: vp_token, presentation_submission, presentation_definition
Affinidi Login->>Affinidi Login: Generate idToken
Affinidi Login->>Website: Send generated idToken from VP
Website->>User: Provide access to the userUsing the sample application, we have configured it to integrate with Affinidi Login as the OIDC provider and parse the idToken sent by the Affinidi Login to confirm the user’s successful authentication using the Affinidi Vault.
Glad to hear it! Please tell us how we can improve more.
Sorry to hear that. Please tell us how we can improve.
Thank you for sharing your feedback so we can improve your experience.




