The backapi is a simple NodeJS application that reads a local json file to return a set of profile data to a client.
For this sample to work, deploy AppServiceEasyAuth/BackendNodejs/
to an App Service. Alternatively, you can deploy your own code for the backend API.
To secure your backend API app deployed in App Service, simply enable EasyAuth. From the App Service blade, under the Setting section, click Authentication / Authorization. Then set the following:
- App Service Authentication to On.
- Action to take when request is not authenticated to Login with Azure Active Directory.
- Authentication Providers, configure Azure Active Directory.
- Management mode set to Express and provide a unique name for your Azure AD App.
- Make sure you write down the AppID, you will need this later.
Click Ok and make sure to click Save before moving on and for changes to apply. You can now navigate to your app's URL and instead of getting your app, you are now presented with an authentication prompt before getting to your app.
The frontend is composed of two applications. The first one is Frontsrv (AppServiceEasyAuth/FrontendNodejs/
), which is a NodeJS app that is used to deliver assets to browser. The second one is Frontapp (AppServiceEasyAuth/FrontendAngular/
), which is an Angular application (the assets). We used Azure pipelines to automate the build of the entire frontend.
This sample assumes the Angular app (Frontapp) is in the frontend\dist\frontend
folder in the root of the NodeJS app (Frontapp)
You can verify the app is working correctly by navigating to its URL before configuring EasyAuth.
This configuration is exactly the same as for the backend API, however, make sure to specify a new unique name when you set the Management mode to Express.
We now need to ensure that the frontend app registration has access to the backend app registration. To do this, from the Azure portal, launch Azure Active Directory. From its blade, under the General section click on App Registration and find your App (which was automatically created by App Service when enabling EasyAuth).
On the App Registration blade, under the General section, click on API Permissions. Now click on Add a permission select My APIs and find and select your backend app registration.
Before our frontend can access our backend app, we need to additional parameters to our app. This is easily done using Resource Explorer. Navigate to your App Serivce app and on the blade, under the Development section, click on Resource Explorer and then on Go. This will launch the portal and take you to the specific instance of the service.
Expand the config property, and click on authsetting. Enable editing by clicking Edit on the top section, and change "additionalLoginParams": null
to "additionalLoginParams": ["response_type=code id_token","resource=<back_end_application_id>"]
. Click on PUT on the top section to save the changes.
Now that EasyAuth is configured in for both apps, you can now make secure calls from your frontend to your backend. To do this, all calls to the backend need to have the header Authorization: Bearer + access_token
set.
For your frontend app, which runs and executes client side, to access the token store created by EasyAuth, your app needs to send a GET
to /.auth/me
. This endpoint is only exposed when EasyAuth is enabled, and it returns information about the user logged in.
App Service frontend secured with MSAL with secure calls to App Service backend API using PassportJS
The important part to remember it's that: MSAL JS is client side only and that for a NodeJS backend API a third-party library needs to be used at this time. We are using Passport-Azure-AD
Angular app is using MSAL-Angular for authorization
export const protectedResourceMap:[string, string[]][]=[
['https://my-api.site.com/api/getitems',['app://<appID>/items.read', 'app://<appID>>/items.edit']],
['https://my-api.site.com/api/getprofiles',['app://<appID>/profiles.read', 'app://<appID>>/profiles.edit']]
];
.....
@NgModule({
declarations: [ ... ],
imports: [ ... ,
MsalModule.forRoot ({ // Configure MSAL module to use our AAD, consent, and protected resources
clientID: environment.aad.clientID,
authority: environment.aad.authority,
consentScopes: [ 'app://<appID>/profiles.read', 'app://<appID>>/profiles.edit' ],
protectedResourceMap: protectedResourceMap,
logger: loggerCallback,
level: LogLevel.Verbose,
piiLoggingEnabled: true
})
],
providers: [ ... ,
{
provide: HTTP_INTERCEPTORS, // MSAL HTTP interceptor
useClass: MsalInterceptor,
multi: true
}
],
...
All routes that are defined with canActivate: MSALGurad
will require authentication / authorization. Our app only requires it for our only route
const routes: Routes = [
{ path: 'profiles', component: ProfilesComponent, canActivate: [MsalGuard]}
];
This service calls our backend API and append the access_token
in the Authorization header
getProfiles(token): Observable<any> {
return this.http.get(environment.apps.backend.url,
{headers: {'Authorization': 'Bearer ' + token, 'X-ZUMO-AUTH': token},
observe: 'response'});
}
Our backend api app is being protected from unauthorized access using Passport-Azure-AD with the Bearer Strategy. All the backend app will do, it's verify the token it has received.
Passport-Azure-Add documentation
The library will automatically obtain the Authentication
header from the request and verify the token attached.
app.get('/api/profiles', passport.authenticate('oauth-bearer', { session: false }), function(req, res, next) {
console.log('Headers are: ' + JSON.stringify(req.headers));
fs.readFile('../BackendNodejs/profiles.json', (err, jsonData) => {
if (err) {
console.log("Error reading file: ", err)
return
}
jsonProfiles = JSON.parse(jsonData);
res.send(jsonProfiles);
})
});