0

I'm developing a UWP app that needs to authenticate against an on-premise ADFS 2016 instance, but using Windows integrated authentication.

I'm using ADAL 3.19.8. The app is running on a Windows 10 device which is domain joined. The app has the Enterprise Authentication, Private Network (Client & Server), and Shared User Certificates capabilities enabled as mentioned here: https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/uwp-specificities

I'm setting the UseCorporateNetwork flag to true. Windows Integrated Authentication is enabled in Internet Options, and I've added the ADFS server to the Local Intranet zone.

Here's how I'm trying to authenticate:

string authority = "https://xxxx/adfs/oauth2";
const bool useCorporateNetwork = true;
var authContext = new AuthenticationContext(authority, false);
var authResult = await authContext.AcquireTokenAsync(
        resourceURI, 
        clientID, 
        new Uri(clientReturnURI), 
        new PlatformParameters(PromptBehavior.Auto, useCorporateNetwork));

The authentication against ADFS is successful and I get the access and id tokens. However the app always presents the ADFS login screen. To proceed I enter the same username & password credentials I used to sign into Windows. Clearly this is not ideal and is not the behaviour users of the app would like to see.

Using Fiddler I see the UWP app calls https://xxxx/adfs/oauth2/authorize.

I can get the SSO behaviour I expect if I use the above code but within a WinForms app (although there is no useCorporateNetwork overload). Using Fiddler the WinForms app calls https://xxxx/adfs/oauth2/authorize/wia

What am I missing?

TylerH
  • 20,799
  • 66
  • 75
  • 101
TK1
  • 1
  • 3

2 Answers2

0

If ADAL.NET has acquired a token for a user for a Web API, it caches it, along with a Refresh token. Next time the application wants a token, it can first call AcquireTokenSilentAsync to verify if an acceptable token is in the cache.

AuthenticationContext ac = new AuthenticationContext(authority);
AuthenticationResult result=null;
try
{
 result = await ac.AcquireTokenSilentAsync(resource, clientId);
}
catch (AdalException adalException)
{
 if (adalException.ErrorCode == AdalError.FailedToAcquireTokenSilently
     || adalException.ErrorCode == AdalError.InteractionRequired)
  {
   result = await ac.AcquireTokenAsync(resource, clientId, redirectUri,
                                       new PlatformParameters(PromptBehavior.Auto));
  }
}

For more please refer this.

Nico Zhu
  • 32,367
  • 2
  • 15
  • 36
  • Acquiring a token silently will always throw an exception until i have successfully authenticated via the AcquireTokenAsync method. The problem is the call to AcquireTokenAsync presents a dialog asking for credentials: I want this to use Integrated Windows Authentication instead i.e. no dialog. – TK1 Jul 16 '18 at 16:38
  • Yep, if you have successfully authenticated with `AcquireTokenAsync` method, you could call `AcquireTokenSilentAsync` to verify next time. it will not display the dialog, please make sure your application will not be cleared next start. – Nico Zhu Jul 17 '18 at 01:06
  • Note, you need uncheck uninstall and then reinstall my package option for Visual Studio. – Nico Zhu Jul 17 '18 at 01:15
0

The bit I was missing turned out to be you need get hold of the client redirect Uri from the WebAuthenticationBroker instead of setting it to any arbitrary string:

Uri clientReturnURI = Windows.Security.Authentication.Web
    .WebAuthenticationBroker.GetCurrentApplicationCallbackUri();

This returns a URI such as ms-app://s-1-15-2-1352796503-54529114-405753024-3540103335-3203256200-511895534-1429095407/ and this needs to be registered in ADFS against the native application.

This article was helpful: https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/wiki/Acquiring-tokens-interactively---Public-client-application-flows

This is the relevant section:

Properties of PlatformParameter specific to WinRT and UWP (Corporate network)

The WinRT (until ADAL 3.x) and UWP platforms have the following property UseCorporateNetwork is a boolean which enables the Win8.1 and UWP application to benefit from windows integrated authentication (and therefore SSO with the user signed-in with the operating system) if this user is signed-in with an account in a federated Azure AD tenant. This leverages WAB (Web Authentication Broker).

Important: Setting this property to true assumes that the application developer has enabled Windows Integrated Authentication (WIA) in the application. For this:

In the Package.appxmanifest for your UWP application, in the Capabilities tab, enable the following capabilities: Enterprise Authentication Private Networks (Client & Server) Shared User Certificate WIA is not enabled by default because applications requesting the Enterprise Authentication or Shared User Certificates capabilities require a higher level of verification to be accepted into the Windows Store, and not all developers may wish to perform the higher level of verification. Note that the underlying implementation on the UWP platform (WAB) does not work correctly in Enterprise scenarios where Conditional Access was enabled. The symptom is that the user tries to sign-in with Windows hello, and is proposed to choose a certificate, but the certificate for the pin is not found, or the user chooses it, but never get prompted for the Pin. A workaround is to use an alternative method (username/password + phone authentication), but the experience is not good. In the future, ADAL and MSAL will need to leverage WAM, which will solve the problem.

Getting the Redirect URI in the case of windows 8.1 store applications

Note: Support for Win8.1 and Windows Phone 8.1 stopped in ADAL 4.x. Windows 10 application (UWP) are still supported

In the case of windows store applications, you will need to discover the callback uri for your Windows Phone app. The simplest way to do it is to add a line in the Initialization method (for instance in the MainPage) and set a breakpoint on this line in the method:

var redirectURI = Windows.Security.Authentication.Web.WebAuthenticationBroker.GetCurrentApplicationCallbackUri();

then, run the app, and copy aside the value of redirectUri when the breakpoint is hit. It should look something like ms-app://s-1-15-2-1352796503-54529114-405753024-3540103335-3203256200-511895534-1429095407/ Back on the ReplyURLs tab of your application in the Azure portal, add this value.

Hopefully this proves useful to anybody else struggling with the same problem!

TK1
  • 1
  • 3