0

I need help on using ADFS as the identity provider to authenticate an user from MS Office. What I want to achieve is to be able to directly open the credentials page from the ADFS server in MS Office and authenticate the user. Please bear with me through the long explanation.

We have a WebDAV implementation as part of our web application. Authentication is needed to access different resources through WebDAV.

We added support for MS-OFBA (Microsoft Office Form Based Authentication). When opening a DAV URL, in MS Word for instance, an embedded browser is used by Word to open our login web page.

This is done by a custom middleware that checks the User-Agent header, and if it contains "Microsoft Office", further checks if (Request.User == null || !Request.User.Identity.IsAuthenticated) and if so, responds with HTTP 403 Forbidden and sets two special headers as indicated here:

Response.StatusCode = 403;
Response.Headers.Add("X-FORMS_BASED_AUTH_REQUIRED", new[] { string.Format("{0}?ReturnUrl={1}", loginUri, successUri) });
Response.Headers.Add("X-FORMS_BASED_AUTH_RETURN_URL", new[] { successUri });

This works fine, but form-based authentication is restricted in Office apps. We need to set a list of trusted locations for sign-in prompts and due to various deployment scenarios this seems to be complicated.

We also use an ADFS server as an identity provider to authenticate users, and this server doesn't seem to need to be listed in the list of hosts allowed to show sign-in prompts, so we're trying to use it directly from Office.

For the ADFS server, we register an OpenID Connect middleware in passive mode, which is challenged when the user clicks on a button in the login page.

The OIDC middleware sees that:

  • the status was set to 401 Unauthorized
  • there is an AuthenticationResponseChallenge with its type

so it reacts by:

This answer is not suited for MS Office, since it requires the data as described above (403, custom headers).

It's not clear how can I challenge the ADFS OIDC middleware so that MS Office will properly load its credentials page.

What I did so far is to add two new middlewares:

  • one is registered after registering the OIDC middleware and it challenges this OIDC middleware when it detects an WebDAV URL request from an unauthenticated user
  • one is registered before the OIDC middleware and, acts after the OIDC acted, by checking if the OIDC middleware modified the response, then re-modifies it to suit MS Office

This way, MS Office requests the credentials page from the ADFS provider, but the reply is 401 instead of 200 and the page itself, which results in the browser showing the Windows credentials popup that doesn't lead me anywhere. I suspect that the 401 from ADFS is due to the Set-Cookie: OpenIdConnect.nonce... missing. I watched the requests with Fiddler and this header is not sent by MS Office.

Here's some code:

// This is the "MS Office friend" middleware:
app.Use(async (context, next) =>
{
    await next.Invoke();

    // This runs after the OIDC middleware for ADFS modified the response.
    // We check to see if it was challenged, hence it set the Location.
    var provider = "ADFS";
    var challenge = context.Authentication.AuthenticationResponseChallenge;
    bool challengeHasAuthenticationTypes = challenge != null && challenge.AuthenticationTypes != null && challenge.AuthenticationTypes.Length != 0;
    bool providerHasChallenge = challengeHasAuthenticationTypes && challenge.AuthenticationTypes.Any(at => string.Equals(at, provider, StringComparison.Ordinal));
    if (providerHasChallenge)
    {
        // At this point, there is a also the Set-Cookie header set: OpenIdConnect.nonce...
        // We're modififying the response so it suits MS Office.
        var successUri = string.Format("{0}://{1}{2}", context.Request.Scheme, context.Request.Host.ToUriComponent(), "/");
        var loginUri = context.Response.Headers["Location"];
        context.Response.StatusCode = 403;
        context.Response.Headers.Add("X-FORMS_BASED_AUTH_REQUIRED", new[] { string.Format("{0}?{1}={2}", loginUri, "ReturnUrl", successUri) });
        context.Response.Headers.Add("X-FORMS_BASED_AUTH_RETURN_URL", new[] { successUri });
        context.Response.Headers.Add("X-FORMS_BASED_AUTH_DIALOG_SIZE", new[] { string.Format("{0}x{1}", 800, 600) });
    }
});


// This is the OIDC middleware.
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions("ADFS") 
{
  // Options not included here for clarity
});



// This is the "challenger" middleware:
app.Use(async (context, next) =>
{
    await next.Invoke();

    if (context.Request.User != null && context.Request.User.Identity.IsAuthenticated
        && IsWebDav(context.Request))
    {
        // This is going to set an AuthenticationResponseChallenge for the OIDC middleware, which will make it run its logic.
        context.Response.StatusCode = 401;
        context.Authentication.Challenge("ADFS");
    }
});

Is it possible to make MS Office send the Set-Cookie header as well? Or maybe there is another way to achieve what I need, that is to authenticate MS Office with an ADFS provider?

Thanks.

Paul
  • 1,224
  • 2
  • 14
  • 31
  • Microsoft Authentication is not OAUTH. See https://learn.microsoft.com/en-us/iis/configuration/system.webserver/security/authentication/ – jdweng Sep 09 '22 at 15:32
  • How does this apply to my question? And what is "Microsoft Authentication"? – Paul Sep 09 '22 at 18:12
  • Authentication is the Windows process for passing credentials from a client to a server. OAUTH is industries standard for passing credentials that is usually used with Linux. – jdweng Sep 09 '22 at 21:07
  • ADFS looks like windows and not OAUTH. I'm not sure. See following and like in the paragraph (which are your headers). It looks like ADFS controls what is in the HTTP headers, but not the login credentials. OAUTH is the login credentials. I'm a little confused and I think you are also. https://learn.microsoft.com/en-us/windows-server/identity/ad-fs/overview/whats-new-active-directory-federation-services-windows-server#additional-security-improvements – jdweng Sep 09 '22 at 21:15
  • I am indeed confused, but not necessarily by the things you mentioned. We have a working authentication setup using OpenID Connect and our identity provider is an ADFS server. Please note that you're mixing concerns. OpenID Connect offers a protocol to offload user authentication to a centralized provider and it is built on top of OAuth2, which is an authorization framework. [ADFS supports the OpenID Connect protocol](https://learn.microsoft.com/en-us/windows-server/identity/ad-fs/overview/ad-fs-openid-connect-oauth-flows-scenarios). – Paul Sep 10 '22 at 07:15
  • If you want to go deeper into these topics I recommend the book [Modern Authentication with Azure Active Directory for Web Applications](https://www.microsoftpressstore.com/store/modern-authentication-with-azure-active-directory-for-9780735696945) by Vittorio Bertocci. What I need help with is, as per my question, accessing the credentials page and authenticating in MS Office using the OpenID Connect middleware. – Paul Sep 10 '22 at 07:15
  • The page may help : https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens – jdweng Sep 10 '22 at 08:58

0 Answers0