Calling Microsoft Graph Endpoint with Delegated Implicit Authentication Does Not Include Azure AD Roles

Recently I was working with a Microsoft Graph partner and ran into an interesting scenario around calling Microsoft Graph endpoints from SharePoint Framework (SPFx) web parts using delegated permissions that I want to share.

Scenario

The partner was building a SPFx web part that was making calls to Microsoft Graph using the MSGraphClient. While making calls to specific endpoints on Microsoft Graph they were receiving a 403 Forbidden error response. We checked the permissions granted and consented and everything appeared in order.

403 Forbidden error screenshot.

Digging deeper into the MSGraphClient implementation I found that it uses an ImplicitMSALAuthenticationProvider for acquiring the authentication token. Implicit authentication is important to keep in mind in this scenario.

Use the MSGraphClient to connect to Microsoft Graph https://docs.microsoft.com/en-us/sharepoint/dev/spfx/use-msgraph

Microsoft Graph JavaScript Client Library – Authenticate for the Microsoft Graph service https://www.npmjs.com/package/@microsoft/microsoft-graph-client#2-authenticate-for-the-microsoft-graph-service

I used https://jwt.ms (provided by the Microsoft Identity Platform team) to decode a sample token from the partner and then again to decode an access token I had acquired in my lab environment. I noticed that the partner’s access token did not have the “wids” claim while my lab access token did have that claim.

Thanks to a contact in O365 software engineering who was able to confirm that the “wids” claim contains the tenant-wide roles assigned to the user. As noted in the documentation implicit authentication flows may not return the “wids” claim due to token length concerns.

Screenshot of documentation on "wids" claim.  Highlight that this claim might not be returned for implicit authentication flow.

Microsoft identity platform access tokens – payload claims

https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens#payload-claims

Looking at one of the Microsoft Graph endpoints that the partner was calling (getOffice365GroupsActivityDetail) we found the below note explaining that when using delegated permissions (which the partner was using) the user context must also be assigned to an appropriate Azure AD limited administrator role.

Note: For delegated permissions to allow apps to read service usage reports on behalf of a user, the tenant administrator must have assigned the user the appropriate Azure AD limited administrator role. For more details, see Authorization for APIs to read Microsoft 365 usage reports.

https://docs.microsoft.com/en-us/graph/api/reportroot-getoffice365groupsactivitydetail?view=graph-rest-1.0#permissions

Putting the pieces together, the query was failing an authentication check because the access token passed to the endpoint did not have the necessary claim containing the assigned Azure AD roles. Hence the “invalid permissions” response.

Conclusion

This is an edge case scenario that took some collaboration with various groups within Microsoft to track down. Many thanks to my peers who helped with identifying additional information as we investigated. I submitted a pull request to the SPFx documentation that has been merged to call out this behavior (see Known Issues on this link). So far that I can tell only the Microsoft 365 usage reports endpoints on Microsoft Graph may have an Azure AD role requirement.

Authorization for APIs to read Microsoft 365 usage reports
https://docs.microsoft.com/en-us/graph/reportroot-authorization

Hopefully this post helps others who may run into this scenario. If you find additional similar scenarios feel free to let me know in the comments.

-Frog Out

Find Azure AD Error Descriptions

 

Recently I was working with a customer to troubleshoot Azure AD authentication errors logging into a custom application.  I knew that there is a support page for Azure AD Authentication and authorization error codes, but as the article points out “[e]rror codes and messages are subject to change”.  More interestingly they also linked to a page where you can get current information on error codes: https://login.microsoftonline.com/error.

 

Sample response:

AADErrorCodes1

 

Programmatic Response

If you would like to programmatically retrieve the output you can pass in the code…:

  1. as a query string parameter
  2. as a form-data submission on the body of the request.

See the sample screenshot below.  Only one option is necessary.

AADErrorCodes2

As you may notice, the response after submission is an HTML response and not JSON, XML, or another text format.  If you would like to see alternate output formats please upvote this Azure feedback suggestion to add JSON support.

Error details website – JSON support
https://feedback.azure.com/forums/169401-azure-active-directory/suggestions/40310266-error-details-website-json-support

 

Conclusion

In this post I showed a quick tip on how to retrieve current information on Azure AD authentication / authorization error codes.  Additionally you can retrieve it programmatically if needed as well as a feedback suggestion to upvote for additional output formats.  Hopefully this can help save you time troubleshooting scenarios should you need it.

-Frog Out

“Requested registry access is not allowed” error on .Net / SharePoint application

    The error message “Requested registry access is not allowed” coming from a .Net / SharePoint application can be slightly misleading as I will explain in this post.  Today I ran into this error message while trying to log into a Forms Based Authentication (FBA) SharePoint site at my client.  The hosting web front end (WFE) server was recently rebuilt from scratch (re: fresh OS) so first thoughts pointed towards a permissions error relating to IIS and the registry.  Sadly that started to take me down the wrong path.  Luckily my coworker Kelly Jones overheard me talking about this error and pointed me to an article he remembered from a previous project with the same error.  Read this Microsoft support article for info about the true error with the event log.

    As part of the custom applications we are hosting on SharePoint we also built a logging component that writes out to the event log when errors occur.  In order to distinguish our event log entries we created a new event log source under the Application event log.  The code to do add this event log source is very simple (see below) but it must be executed as a user with administrative access because it requires writing to the registry.  Ahh, so our old friendly but misleading error message from above about the registry did have a hand in our problem, but that wasn’t readily apparent at first.

if (!EventLog.SourceExists("MyNewEventLogSource"))

{

    EventLog.CreateEventSource("MyNewEventLogSource", "EventLog");

}

 

     Here’s the long explanation of why this error occurred in the first place.  Typically our custom event log source is created during installation of the WSP that handles our logging, or just before a new event log entry is written if it doesn’t already exist.  When we rebuilt the primary WFE we rebuilt the entire server from scratch (re: blank OS).  When that WFE rejoined the farm it didn’t contain the custom event log source.  The WSP for logging wasn’t reinstalled (because it was already installed on the farm) so it didn’t fire the code to add the custom event log source back to the primary WFE.  After the primary WFE took back hosting duties and encountered the login warning the application pool identity attempted to write out to our custom event log source.  Our application pool identity doesn’t have local admin permissions on that server so it failed to created the event log source.  Consequently that failed attempt at event log source creation throws an exception that wasn’t properly handled (can’t write out the exception leading to a chicken and egg scenario) so it bubbled up to the user.

     Long story short, I wrote a very simplistic console app to run on any WFE that may be affected by this situation in the future.  Below is a bare bones version of the method but you’ll get the idea.  We are looking into a more permanent solution on how to avoid this situation in the future, but the need to be run as a local admin account adds some extra kinks.  For now this console app will get us through.

static public void MyEventLogInstaller(string EventSourceName)

{

    if (!EventLog.SourceExists(EventSourceName))

    {

        Console.WriteLine("Source doesn't exist, attempting create");

 

        try

        {

            EventLog.CreateEventSource(EventSourceName, "Application");

        }

        catch (Exception ex)

        {

            Console.WriteLine("Error creating event log source: " + ex.Message);

        }

    }

    else

    {

        Console.WriteLine("Source already exists, not performing any actions");

    }

}

     Hopefully this post sheds some light into the partially cryptic error message I saw and saves you from wasting time looking into registry permissions as I did at first.  Enjoy.

 

    -Frog Out

Single Sign-On Across .Net Web Apps Using Forms Based Authentication

     <foreword>Please read this explanation of Forms Based Authentication (FBA) first if you are unfamiliar with how FBA works or is implemented. </foreword>

    Achieving single sign-on capabilities using Forms Based Authentication is much easier than I had initially expected, yet it took awhile to find the exact settings required.  To get FBA working in SharePoint click here for a good starter from the SharePoint team blog.  I also used this article from MSDN as a reference for ultimately determining what settings were necessary in our environment.

    Small back story for what we are doing.  We have a SharePoint farm that has split authentication: Windows Integrated for users on the network at the office and FBA for users out in the field on the extranet.  Separate from our SharePoint farm is a .Net web application to assist with password reset and registration.  When the user logs in to SharePoint via FBA they are redirected to the external application if there is any problem with their account.  We wanted to implement single sign-on between the applications so that a user wasn’t re-prompted for credentials after the redirect.

     The general solution involved the following main items:

  • Changes to both applications’ web.config files
  • Configuring host headers for each app to be in the same domain lookup
  • 1 line of code to read authentication cookie

 

    So let’s review what was added to get this working starting with the web.config entries.

<system.web>
   <authentication mode="Forms">
      <forms name="nameOfCookie"
         loginUrl="loginPage.aspx"
         path="/"
         domain="mydomain.com"
         protection="All"/>
   </authentication>
   <machineKey validationKey="value" decryptionKey="value"
       validation="value" decryption="value" />
</system.web>

    For the web.config entries you’ll need to ensure that both apps are set to Forms authentication.  Next specify a “name” value which will be used to identify your authentication cookie on the receiving application.  By default this value is “.ASPXAUTH”.  Specify the domain that each web application will be run under.  Using host headers your site URLs might look like “site1.mydomain.com” and “site2.mydomain.com”.  Specify protection=”All” so that the authentication cookie is encrypted using the machineKey value that you specify.  Lastly provide identical machineKey entries on each web application so that both can read the authentication cookie provided by each other.  I used a free random machine key generator in my development environment, but you’ll probably want your security team to handle generating and keeping track of keys for non-development environments.

    The next step involved configuring each site to use host headers assigned to the same domain.  This article from TechNet gives a brief intro to get you started.  As stated just above, I configured the web app URLs to appear as “site1.mydomain.com” and “site2.mydomain.com” to match my entries in the web.config.
    Lastly is the code required to read the authentication cookie.  If you read the explanation article linked in the foreword above you’ll read that after logging in using forms authentication, the authentication cookie containing the currently authenticated user (along with other data) is passed along with responses back to the server.  Due to this fact, on the receiving page in the other web application we can decrypt the cookie and read who is currently authentication even though they never logged in again to the the other application.  Here is the code to explicitly reference that cookie.
string loggedInUser = FormsAuthentication.Decrypt(Request.Cookies.Get("cookieName").Value).Name
    And that wraps up the changes to my web apps to allow single sign-on for forms based authentication.  If your web applications are SSL enabled I believe there are a few additional settings you’ll need, but they should be covered in the links I provided.  I hope this helps you or at least gives you some good resources to refer to for FBA.  If you have any questions, comments, or other feedback feel free to leave it below.
 
    -Frog Out