This blog walks through how to set up MSAL.JS to authenticate directly to ADFS 2019 Server using Authorization Code Grant flow to get an Access Token and then call a Web API with that Access Token. We will go over the following steps to get this the samples working:

  1. App Registrations for both the Single Page Application (SPA) client app and the web API app
  2. Enable Cross-origin Request Sharing (CORS) on ADFS 2019 Server
  3. Construct a SPA client application project
  4. Construct a web API application project

Let’s get into the detail for each of the above steps.

App Registrations

Follow steps 1 – 8 In the App Registration in ADFS Documentation to add a new Application Group. We will register a Native Public App type and a web API type in this Application Group:

Note: unlike Azure AD, ADFS does not have a concept of Single Page Application client (platform type) so we will treat a SPA app as a generic native client app. In order for one application to call another application with an Access Token, both of these applications have to be in the same Application Group.

I use the following settings in my App Registration:

Native application Redirect URI: http://localhost:3000

Take note of the Native application’s Client Id. We will need it later for our client project.

Web API Relying Party identifier: https://localhost:44320. Take note of this value since we will use it for both the web API sample and the SPA client app sample below.

Enable CORS feature

Refer to Customize HTTP security response headers with AD FS 2019 for more info.

CORS is disabled by default on ADFS 2019 Server. We need to enable CORS in order for our MSAL.JS V2 SPA application to authenticate to ADFS 2019 Server. Run the following PowerShell Command on the ADFS Server to enable CORS and set the trusted origin value to the domain of the application making the CORS request (http://localhost:3000 in this case for our SPA client app).

Set-AdfsResponseHeaders -EnableCORS $true
Set-AdfsResponseHeaders -CORSTrustedOrigins http://localhost:3000

SPA client app sample using MSAL.JS V2

The code for this application is derived from this sample. The complete sample is here on github. Modify the project’s App/AuthConfig.js file to put in your app registration info:

The values for msalConfig’s auth’s (below) clientId and redirectUri comes from Native App registration above. Fill in the authority and knownAuthorities values with your ADFS environment. For apiConfig’s scopes, the web API scope should be in this format: <web API Relying Party Identifier>/<scope name>, so I set this value to https://localhost:44320/openid (we enabled openid for permission during registration).

const msalConfig = {
    auth: {
        clientId: "<Native application client ID>",
        authority: "https://<FQDN_OF_ADFS_Server>/adfs/", // e.g https://contoso.com/adfs/
        knownAuthorities: ["https://<FQDN_OF_ADFS_Server>/adfs/"], // e.g https://contoso.com/adfs/
        redirectUri: "<Native Application's redirect URI>", // "http://localhost:3000" for this sample
        protocolMode: "OIDC"
    },
    cache: {
        cacheLocation: "localStorage", // This configures where your cache will be stored
        storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
    },
};

// Add here the endpoints and scopes for the web API you would like to use.
const apiConfig = {
    uri: "https://localhost:44321/api/values", // e.g. http://localhost:5000/api
    scopes: ["<fully qualified scope name for web API>"] // e.g. ["https://localhost:44320/openid"]
};

/**
 * Scopes you add here will be prompted for user consent during sign-in.
 * By default, MSAL.js will add OIDC scopes (openid, profile, email) to any login request.
 * For more information about OIDC scopes, visit: 
 * https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes
 */
const loginRequest = {
    scopes: ["openid", "profile"]
};

/**
 * Scopes you add here will be used to request a token from Azure AD to be used for accessing a protected resource.
 * To learn more about how to work with scopes and resources, see: 
 * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/resources-and-scopes.md
 */
const tokenRequest = {
    scopes: [...apiConfig.scopes],
};

ASP.Net Web API sample

This sample project is derived from this ADFS Web API project. The Web API project was extended to support CORS request for the SPA client. Guidance for enable CORS is here. The complete web API sample is here. Find the following entries in the web.config file and replace it with your web API registration info. Use the web API’s Relying party Identifier for the “ida:Audience” value and fill in the FQDN name of your ADFS Server for the “ida:AdfsMetadataEndpoint” value.

    <add key="ida:Audience" value="https://localhost:44320" />
    <add key="ida:AdfsMetadataEndpoint" value="https://[Enter your AD FS hostname]/federationmetadata/2007-06/federationmetadata.xml" />

The sample uses ActiveFederationServicesBearerAuthentication middleware to integrate with ADFS Authentication.

using Microsoft.Owin.Security.ActiveDirectory;
using Owin;
using System.Configuration;
using System.IdentityModel.Tokens;

namespace TodoListService
{
    public partial class Startup
    {
        // For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
        public void ConfigureAuth(IAppBuilder app)
        {
            app.UseActiveDirectoryFederationServicesBearerAuthentication(
                new ActiveDirectoryFederationServicesBearerAuthenticationOptions
                {
                    MetadataEndpoint = ConfigurationManager.AppSettings["ida:AdfsMetadataEndpoint"],
                    TokenValidationParameters = new TokenValidationParameters()
                    {
                        SaveSigninToken = true,
                        ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
                    }

                });
        }
    }
}

I decorate the controller with the following attribute to enable CORS request for the controller’s methods:

    [Authorize]
    [EnableCors(origins: "http://localhost:3000", headers: "*", methods: "*")]
    public class ValuesController : ApiController
    {

Running the sample projects

  1. Build and run the web API. It will open a browser and navigate to https://localhost:44321/
  2. Run the SPA application: npm install followed by npm start to start the node server
  3. Open a web browser and navigate to http://localhost:3000/
  4. Click ‘Sign-in’ button on top right to sign in with your account at the ADFS sign in page
  5. If sign in is successful the page should display the signed-in name, id token, and access token (this is not the web API access token)
  6. Click the ‘Call API’ button and it should display the Access Token for the web API and the response from the web API as followed:
0 0 votes
Article Rating
Subscribe
Notify of
guest
4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Danny Wang
Danny Wang
January 6, 2022 5:52 am

I changed the code a little bit to authenticate against AAD, however, i found the AAD oauth2.0 token endpoint not suport CORS, so the response with the fetched access token was blocked by browser. I wonder how to enable CORS for AAD token endpoint. BTW, the app was registrated as SPA.

Sam
Sam
January 20, 2022 10:27 am

Hi,
Thanks for the post.
Any ideas if this is possible on Windows server 2016 or does it need to be on Windows server 2019?

I am currently fighting to get this to work for Windows server 2016 but feel like I am fighting a losing battle.