Integrating Single Sign-On (SSO) with Azure Active Directory (Azure AD)
In this post, I will be explaining how to Integrate SPA(Single Page Application) Single Sign-On (SSO) with Azure Active Directory (Azure AD).
- 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 onNew 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
orSingle 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 aaud
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 validatingAccess 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 theMy 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.