Integrating Single Sign-On (SSO) with Azure Active Directory (Azure AD)

Gnanabillian
Dev Genius
Published in
5 min readJun 21, 2023

--

In this post, I will be explaining how to Integrate SPA(Single Page Application) Single Sign-On (SSO) with Azure Active Directory (Azure AD).

  1. Registering the SPA in Azure AD
  • Log in to the Azure portal (portal.azure.com) and navigate the Azure Active Directory section.
  • Select App Registrations and click on New registration.
  • Provide a name for your application and choose the appropriate account types.
  • Set the Redirect URI to the URL where your SPA is hosted.
  • After registration, note down the Client ID generated by Azure AD. You’ll need it later.

2. Create a secret key in Azure AD

  • In the application overview page, navigate to the Certificates & Secrets section.
  • Click on the New client secret button.
  • Describe the secret and choose the expiration duration (e.g., 1 or 2 years).
  • Click on the Add button.
  • Once the secret is generated, make sure to note it down(Value) immediately as it will not be visible again

3. Add an optional claim in Azure AD

  • In the application’s blade, select Token configuration or Single sign-on depending on the type of application you are working with.
  • In the Optional claims panel, you will see a list of available optional claims. Click on “Add optional claim.”
  • Choose the claim type Access from the drop-down list. After that select a aud claim.
  • Click “Add” to add the optional claim.

4. Create a scope to define the permissions for your API

  • In the Azure portal, navigate to the Expose an API section within your registered application.
  • Delete the default API permission of Microsoft Graph to avoid the invalid signature error while validating Access token.
  • Click on the Add a scope button.
  • Define a scope name, admin consent display name, and admin consent description for the scope.
  • Select an Admin and users option for Who can consent?
  • Click Add scope to add the scope configuration.

5. Add API permissions in Azure AD

  • Click on the API permissions option.
  • In the “Configured permissions” tab, click on the Add a permissio button.
  • In the Request API permissions panel click on the My APIs tab.
  • If you have already registered your API in Azure AD, you should see it listed. Select your API from the list to give grant permissions to your API registered in Azure AD
  • Once you’ve selected your API, you will see the available permissions for your API. Choose the specific permissions required by your application from the list.
  • Click on the Add permissions button to add the selected permissions to your application.

Overall architecture:

The following diagram shows the entire implicit sign-in flow.

  • The user initiates the sign-in process by clicking on a sign-in button or a protected resource.
  • The application redirects the user to the identity provider’s authentication endpoint.
  • The user authenticates with the identity provider (e.g., Azure AD) by providing their credentials.
  • Upon successful authentication, the identity provider issues an authorization response containing an access token and/or an ID token.
  • Validate the access token to make authorized requests to protected web API.

Setup the frontend app in Node.js for integrating Single Sign-On (SSO) with Azure Active Directory (Azure AD):

Clone the code from the below link:

https://github.com/david-billian/Azure-AD-SSO/tree/master/SPA

Note: Please provide your azure AD application details in authConfig.js

Get the access token after you signed in. Set this access token as an Authorization header while calling the backend API.

Validate the Azure AD access token in NestJs (Backend):

Step 1: Install Dependencies

  • Create a new Nest.js project or navigate to your existing project.
  • Install the required dependencies for Azure AD integration:
npm install passport passport-azure-ad @nestjs/passport

Step 2: Configure the Azure AD strategy in your Nest.js application

// azure-ad.strategy.ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { BearerStrategy } from 'passport-azure-ad';

const config = {
credentials: {
tenantID: 'Enter_your_tenant_id',
clientID: 'Enter_your_client_id',
audience: 'Enter_your_client_id',
},
metadata: {
authority: 'login.microsoftonline.com',
discovery: '.well-known/openid-configuration',
version: 'v2.0',
},
settings: {
validateIssuer: true,
passReqToCallback: false,
loggingLevel: 'info',
},
};
const EXPOSED_SCOPES = ['Files.Read'];//provide a scope of your azure AD

@Injectable()
export class AzureAdStrategy extends PassportStrategy(
BearerStrategy,
'azure-ad',
) {
constructor() {
super({
identityMetadata: `https://${config.metadata.authority}/${config.credentials.tenantID}/${config.metadata.version}/${config.metadata.discovery}`,
issuer: `https://${config.metadata.authority}/${config.credentials.tenantID}/${config.metadata.version}`,
clientID: config.credentials.clientID,
audience: config.credentials.audience,
validateIssuer: config.settings.validateIssuer,
passReqToCallback: config.settings.passReqToCallback,
loggingLevel: config.settings.loggingLevel,
scope: EXPOSED_SCOPES,
loggingNoPII: false,
});
}

async validate(profile: any): Promise<any> {
// Implement user validation and extraction of necessary user information from profile
// Example: Extract and store user details in a session
return profile;
}
}
// auth.module.ts
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { AzureAdStrategy } from './azure-ad.strategy';

@Module({
imports: [PassportModule.register({ defaultStrategy: 'azure-ad' })],
providers: [AzureAdStrategy],
})
export class AuthModule {}
//app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthModule } from './auth.module';

@Module({
imports: [AuthModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
// app.controller.ts
import { Controller, Get, Req, Res, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Request, Response } from 'express';

@Controller('user')
export class AppController {
@Get('details')
@UseGuards(AuthGuard('azure-ad'))
login(@Req() req: Request, @Res() reply: Response) {
console.log('signed-in user:', req['user']);
reply.status(200).send({ message: 'access token validated successfully' });
}
}

Conclusion

I hope you have successfully integrated Single Sign-On (SSO) with Azure Active Directory (Azure AD) in your Nest.js application. Users can now authenticate using their Azure AD credentials, providing a seamless and secure login experience. Feel free to extend this implementation to suit your specific application requirements and enhance the user authentication flow further.

--

--

Software Engineer | Node.js | Javascript | Typescript | Fastify | NestJS | Sequelize | Prisma | PostgreSQL, I love open source and startups.