Sunday, September 17, 2023

call an Azure Active Directory protected API from an Angular 16 SPA using MSAL V2

Overwiev

This post is an upgrade of the great post of Alex Pshul on the same topic.
This helped me a lot, thanks to him!
I had to ugrade Alex's port and source code because:
  • The new MS doc for MSAL V2 ( @azure/msal-angular V3 ) shows new code for configuration in app.module.ts
  • The way on configuring AAD apps in Azure Portal has changed too
  • Last, I couldn't complie with Angular 16 Alex's code and other codes found that were all written at leaast 2 years ago.
    No post on this topic found for 2 years
If you are not familiar with MSAL and authentication flow for SPA and Azure API, you should first read my previous post that is detailling all that and presents a simple JavaScript solution that allows you to test your Azure configuration.
Regarding angular 16 SPA, Azure API and MSAL V2, steps are the following:

Azure config and Angular code for SPA authentication

Create an AAD app for your Angular SPA with a redirect url set to http://localhost:4200
Copy the AAD app client ID and your tenant id
Update your @NgModule app.module.ts with this code while replacing the client id and tenaant id by your own values:
@NgModule({
  declarations: [AppComponent, HomeComponent, ProfileComponent],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    AppRoutingModule,
    MatButtonModule,
    MatToolbarModule,
    MatListModule,
    HttpClientModule,
    MsalModule.forRoot(
      // MSAL Configuration V2. Mandatory! V1 outside NgModule no longer works
      new PublicClientApplication({
        auth: {
          clientId: "9fefb62c-5e82-4a09-acc3-ea077f8164f3", // Application (client) ID from the app registration
          authority:
            "https://login.microsoftonline.com/994d9022-89d7-44f4-91e2-3851290dc50d", // The Azure cloud instance and the app's sign-in audience (tenant ID, common, organizations, or consumers)
          redirectUri: "http://localhost:4200/", // This is your redirect URI,
        },
        cache: {
          cacheLocation: BrowserCacheLocation.LocalStorage,
          storeAuthStateInCookie: isIE,
        },
      }),
      //MSAL Guard V2. 
      null,
      null,
    ),
  ],
  providers:
    [],
  bootstrap: [AppComponent, MsalRedirectComponent],
})
export class AppModule { }
Get the code
I let this post id values in the code to help you.
Microsoft doc on the topic can also help: SPA that signs in users and calls a Web API

Normally, if you configured well, you should be able to sign in with AAD to your Angular SPA:

Azure config and Angular code for API authentication (SSO)

If you don't have any API for testing you can quickly get one by following the steps described in this previous post
From the API configuuration in Azure portal click authentication in the left menu. Then click "add identity provider"
Choose Microsoft as provider, give a name to the AAD app.
It is better to create new app for an api, because, doing this way, Azure will configure your app service to be called and to allow user authentication without problem.
If you choose an existing AAD app, you may have other configurtaions, not done by Azure, to perform yourself at the app service level (json config, web config, etc.)
This is the screenshot of the finalized API identity provider
If you click the link of the app name you will be redirected to the app in Azure Active Directory.
We are now going to let this API app authorize access to user authenticated from the SPA.
So click "expose an API" from the left menu, and in the "Expose an API" pane, click "add a client application"
Fill the client id fiield with the client id of the SPA AAD app you previously put in your angular code.
check the scope checkbox that is a delagated permission for users authenticated with the SPA to access the API without needing to sign in again (SSO).
Go back to the API configuration in Azure portal to set CORS for http://localhost:4200
Then, update your ngmodule to add MSAL Guard and MSAL Interceptor:
  @NgModule({
  declarations: [AppComponent, HomeComponent, ProfileComponent],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    AppRoutingModule,
    MatButtonModule,
    MatToolbarModule,
    MatListModule,
    HttpClientModule,
    MsalModule.forRoot(
      // MSAL Configuration V2. Mandatory! V1 outside NgModule no longer works
      new PublicClientApplication({
        auth: {
          clientId: "9fefb62c-5e82-4a09-acc3-ea077f8164f3", // Application (client) ID from the app registration
          authority:
            "https://login.microsoftonline.com/994d9022-89d7-44f4-91e2-3851290dc50d", // The Azure cloud instance and the app's sign-in audience (tenant ID, common, organizations, or consumers)
          redirectUri: "http://localhost:4200/", // This is your redirect URI,
        },
        cache: {
          cacheLocation: BrowserCacheLocation.LocalStorage,
          storeAuthStateInCookie: isIE,
        },
      }),
      //MSAL guard
      {
        interactionType: InteractionType.Redirect,
        authRequest: {
          scopes: ["api://a30c6d65-3d0b-415e-9dc7-4587fae74e2e/user_impersonation"],
        },
      },
      //interceptor Configuration V2. V1 outside NgModule still works but you must add declaration in providers
      {
        interactionType: InteractionType.Redirect,
        protectedResourceMap: new Map([
          ["https://helloworldfunction1123.azurewebsites.net/", [
                        {
                httpMethod: "GET",
                scopes: ["api://a30c6d65-3d0b-415e-9dc7-4587fae74e2e/user_impersonation"],
            }
        ]]]),
      }
    ),
  ],
  providers:
    [   {
      provide: HTTP_INTERCEPTORS,
      useClass: MsalInterceptor,
      multi: true
    } ],
  bootstrap: [AppComponent, MsalRedirectComponent],
})
MSAL INTERCEPTOR will generate a token and attach it automatically to the http request when your code will call the API
Nothing to do while coding, just use an http-client to call the API!
Don't be bothered by the token generation, header setting and all that stuff. It is like magic!

Get the code
I let anyway commented code in this source code if you want to generate the token and attach it manually to a request.
  //with http Interceptor of MSAL Angular the token is genrated automatically and attached to 
  //the http requast so no need to generate it
  //uncomment the lines if you want to use the token and see it anyway
  async getFunctionResponse() {

    let url = "https://helloworldfunction1123.azurewebsites.net/api/powershell-azure-function-helloworldHttpTrigger?";
    //let bearer2 = this.accessToken;
    let headers = new HttpHeaders()
      .set('Content-Type', 'text/html')
      //.set('Authorization', 'Bearer ' + bearer2)

    console.log("headers:")
    console.log(headers)

    return this.http.get(url, { headers, responseType: 'text' }).toPromise()
  }

I let the values of clizent id , tenant id, url of this post in my source code to help you.
Here is the MS doc on this part

If everything is configured properly you should have a response from an API in your SPA:
Well done!

Saturday, September 16, 2023

Call an Azure AD protected API from a SPA using MSAL JS

Overview

This is an upgrade of this great post from Janne Hansen. Thanks to him. He helped me alot!
The goal is to autenticate a user with Azure AD on a Single Page Application made with JavaScript, and, from this page, let the user call an Azure API protected with Azure AD while the user has no need to sign in for the API. There is also a notion of Single Sign On (SSO) in there. At the first sign-in user authenticates with Azure AD, then, is also authenticated for the API
The code of Jane Hansen is available at GitHub:jannehansen/SinglePageAndAAD
It uses MSAL browser V2, and stil works perferctly well.
I had to update Janne Hansen's post rather for the steps of Azure cofiguration.

Authentication Flow - First step

First, to well configure everything for the solution in Azure Portal and understand the code used for MSAL (either JavaScript or Angular) you need to figure out how the double authentication flow is working.
For authenticating a user to Azure AD, you have to use Azure AD Applications. Azure AD applications are like super heroes that receives from a user a demand to be authenticated and let the user pass through Azure AD. But as every super heroes their powers are limited. They can act only for a certain scope: after being authenticated, you can be redirected only for the demanded scope, SPA, API, MS online product, SharePoint online, Outlook online etc...
So, for our solution, first we need two super heroes:
  • one application for the scope of our Single Page Application
  • another application for the scope of our API

  • Testing this first step

    Clone with git or dwonload the code GitHub:jannehansen/SinglePageAndAAD
    Install NPM http-server
    Run a command, navigate to the code repository to the folder functioncall and serve the SPA locally by:
    • running http-server
    • calling http://localhost:8080 in a browser

    Configuring the portal for the SPA

    We are now going to create our first superhero for the SPA authentication.
    In Azure Portal, go to Azure Directory.
    Then, in the left menu, click on "App Registrations", then, click to New Registration:
    Then fill the required fields:
    • The App name
    • Choose SPA as a platform
    • and http://localhost:8080 as the redirect url
    Once App is created you can see the App overview. Note cerefully the Client Id and the tenant Id.
    Then, go back to VS Code and update the client Id and the tenant id in your code:
    then, click the link to sign in. You will have a pop-up to fill your Azure login and password, and will receive the app demand for permission consent.
    By the way, you will see the App name and you will be sure you were calling the good App.
    Finally you get signed in and the SPA displays your AAD account information.
    Important: It is super difficult to get rid of cache with this solution using http-server, thus, use systmatically private/incognito windows while doing your tests.
    And yes, I know, you will have to sign in for every test, and you may get nervous.

    Configuring the portal for the API

    Now, let's do the part dedicated to the API.
    This time, we are not going to use the Azure Active Directory. We are going to use the configurtaion panes of an existing API within Azure portal.
    If you have an API available in your Azure tenant, go to it.
    If you haven't, you can use my previous post to quickly build an API based on PowerShell Azure function, then come back to this post.

    Go to your API configuration within Azure Portal and click on authentication in the left menu
    then click on "add identity provider" button
    then choose Microsof as prodider, and let create new app registration checked.
    Type your AAD app name.
    Important:It is better to create the API and create the Azure Active Directory App from the API Authentication pane, than create the AAD app first then use it for the API authentification because Azure will configure everything for your API.
    Let the other choices by default, even redirect is said not be recommended for API, because we are going to use the redirect later.
    Then your authentication is set API side in the Azure portal.
    Now click the name of your AAD app newly created, and you will be redirected to this new app in Azure AD
    and if you click authentication in the left menu you will see that the redirect link for sign in has been created automatically!
    Now's the time to test that, so paste your API url in a browser and you will be asked to sign in.
    then, once signed, you will see the app permission consent pop-up and will be able to check that you called the good AAD app to authentify:
    and finally, after having consent, you'll see your API display its content in the browser.

    Single Sign On cofiguration


    You might say, well, that's fine all that, but if, as you say, the superheroes are stuck in their own perimeters, how can I log in with the SPA superhero and then, be authenticated for the API with the other superhero without signing in a second time?
    That's because Microsoft Azure Active Directory allows superheroes to delegate permissions!
    Ok, that's funny, but let's be serious, how are you setting that, seriously ?
    It's elementary, my dear Watson
    Go back to the API superhero in AAD and go to Expose API pane. Microsoft has planned this, as you can tell an api what is its client application:
    Click on the "+"
    check the proposed scope and paste the client id of the SPA AAD app
    Here is the final screenshot before saving

    CORS

    To access an API from a SPA, this not the only configurations you have to do. You also have to allow the url of the SPA to call the API. This is CORS (Cross-Origin Resource Sharing). Normally an API can only be accessed by an url with the same domain. Here, were are wrong. SPA is http://localhost and API is https://helloworldfunction1123.azurewebsites.net. So we have to authorize http://localhost to call https://helloworldfunction1123.azurewebsites.net.
    So, go back to Azure Portal in the API configurtaion, locate CORS in the left menu and enter http://localhost:8080 as an authorized domain for CORS.

    JavaScript MSAL code side and testing

    Now, everything in Azure is configured properly, we have to replicate this configuration in our MSAL code.
    You have only 2 params to chang in authRedirect.js The value of the only entry for the scopes table and the url of the API you want to call.
    Get the code
    So now, after the code modification, stop the http-server with ctrl C. Restart it. Open a new inPrivate/Incognito browser window.
    Sign in again to the SPA in if you click the link to the API while signed in:
    you will have the response of your API displayed:

    To get further

    You noticed I have displayed the second authorization token in the browser console. You can decrypt it using JWT.io.
    You will notice we retrieve the authentication origin with the spa client id and the authorization scope for the API.
    The token is saying: "I come from the spa and request authentication for the API"

    Friday, September 8, 2023

    Get quickly an Azure API (PowerShell-function)

    For a lot of reasons, you need sometimes to have an API for testing. I had this need recently for testing MSAL with Anguler.
    So, here is a first way to quickly get an Hello World API working in Azure. It is based on Azure function, PowerShell stack.

    Prerequisites

    • Access as contributor to an Azure subscription
    • Visual Studio Code installed within your device
    • Git installed within your device

    Overview

    We are going to use local Azure Function Core Tools to emulate a function locally and deploy it to azure.
    For doing that, we need to:

    Install .Net 6

    Go to the .Net 6 download page
    Install .Net 6

    Install Azure Function Core Tools

    Click this link to download the installation package

    Clone the Hello World PowerShell function code from Github

    Open a command prompt, locate your DEV directory and execute :
    git clone https://github.com/dfinke/powershell-azure-function-helloworld.git
    

    Let VS code install the Azure extension

    You can then open it in Visual Studio code.

    Visual studio will ask you to install the extension "Azure Function". Agree, and the extension will be installed and also the other extensions "Azure Resources" and "Azure Account", that allow you to:
  • sign in to Azure from Visual Studio Code
  • create new resources into Azure from Visual Studio Code
  • deploy code to Azure, like this one of the Azure Function PowerShell Hello World


  • Create an empty function into Azure

    Then, sign in to Azure using the Azure Account extension.


    The Azure Resources extension, then, will show you the subscriptions of your tenant.
    You can click on the "+" to create a new resource

    However, we are rather going to use the portal to create the function, because an Azure function needs to have an unqiue name and the portal can validate if our name is unique.

    So, go to the portal, display the rezsources of one of your subscription and start creating a function by clicking the "+" sign.
    Then search for function app.

    Choose a name that doesn't exist, select powershell for the stack, then accept all the following tabs.
    When your function creation is complete, click "go to the resource"
    You will find the Url of your Function App. And if you click it, it will respond while there is not working API yet.

    execute the function locally


    Go back to your local environment, and, from the command prompt navigate to the code directory
    cd "powershell-azure-function-helloworld"
    
    launch the function
    func start
    
    If you paste into a browser `http://localhost:7071/api/powershell-azure-function-helloworldHttpTrigger` you will see the function API working locally:

    Deploy the function code into Azure

    Now that we have an empty function within Azure, we can use VS code to deploy our local code into it.
    Go back to VS code and right click the subfolder of the code of the PowerShell function : powershell-azure-function-helloworldHttpTrigger
    Then, at the bottom of the context menu: "deploy to function App"
    You can then, choose the subscription where the Azure function you want to deploy to, is:
    Then, we locate the helloworldfunction1123 we have previously created in Azure:
    We accept to deploy:
    And, finally, we can see the deployment in action:
    And follow it in the Output pane of VS code untill it is complete:
    BTW: I had to deploy twice to get it complete. It failled the first time
    Now, if we go back to Azure portal, we can see our PowerShell function avaolable:
    If we click on the Name Link of our function, we are redirected to a view where we can get the function Url to call it:
    and if we paste the link in a browser, our function is responding and sends us the content:
    We have now an API in Azure based on PowerShell function that we can use for testing purpose, like authentication, MSAL, etc.
    Well done!