Sunday 29 December 2013

Azure Active Directory and MVC application

The ASP.NET Web Application template in Visual Studio 2013 comes with a new feature that allows us to quickly set up basic wiring required for the web application’s authentication and authorization to work against different authentication options – No Authentication (that’s simple!), Individual User Accounts (which was essentially already present in ASP.NET MVC4 template of Visual Studio 2012), Windows Authentication (that’s simple, again) and Organizational Accounts (which promises to make the application work against Office 365, Active Directory or Windows Azure Active Directory).

Authentication options

Authentication options 2

“Organizational Accounts” option is something that i had not tried out before. So I thought of giving it a try and it did not turn out all that pleasant (for me, at least).
I have an Windows Azure account which comes with a default Active Directory. It took me couple of minutes to figure out the actual domain of the default active directory (I did not locate any place that specifies that domain name for the directory). It is actually pretty simple – <yoursigninemailaddresswithoutdotsanddotcom> @ onmicrosoft.com. You can also find it in the browser’s address bar once you have signed into https://manage.windowsazure.com.

Azure AD Address

So with high hopes, i entered the domain address and clicked the OK button. It launched another windows which asked me to authenticate. So far so good.

Sign in address

I entered the details and the browser window authenticated me and as expected it closed by itself after the authentication was complete.  But then i was presented with an error prompt.

Sign in address error

I checked the errors in event log and there was one interesting thing related to error encountered while querying GraphAPI. I figured this was because i had selected Single Sign On and Read data. If i chose Single Sign On option, then there is no error in event viewer but i still get the error prompt. 

Application: devenv.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.Data.Services.Client.DataServiceQueryException
Stack:
   at System.Data.Services.Client.DataServiceRequest.Execute[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]](System.Data.Services.Client.DataServiceContext, System.Data.Services.Client.QueryComponents)
   at System.Data.Services.Client.DataServiceQuery`1[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].Execute()
   at System.Data.Services.Client.DataServiceQuery`1[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].GetEnumerator()
   at System.Collections.Generic.List`1[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]..ctor(System.Collections.Generic.IEnumerable`1<System.__Canon>)
   at System.Linq.Enumerable.ToList[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]](System.Collections.Generic.IEnumerable`1<System.__Canon>)
   at Microsoft.VisualStudio.Web.AzureAD.ProvisionGraphHelper.GetUser(System.String)
   at Microsoft.VisualStudio.Web.AzureAD.ProvisionGraphHelper.HasProvisionRight(System.String)
   at Microsoft.VisualStudio.Web.AzureAD.ProvisionGraphHelper.AcquireToken(System.String, Boolean ByRef, System.String ByRef)
   at Microsoft.VisualStudio.Web.AzureAD.VsAzureADService.LoginToTenant(System.String, Boolean ByRef, System.String ByRef)
   at Microsoft.VisualStudio.Web.Project.AuthenticationDialogViewModel.BeforeCloseDialogByClickYes(Microsoft.VisualStudio.Web.AzureAD.Contracts.IVsAzureADService, Microsoft.VisualStudio.Web.AzureAD.UrlChecker, System.Action`1<System.String>)
   at Microsoft.VisualStudio.Web.Project.AuthenticationDialogWindow.OkAuthenticationButton_Click(System.Object, System.Windows.RoutedEventArgs)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(System.Object, System.Windows.RoutedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(System.Object, System.Windows.RoutedEventArgs, Boolean)
   at System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject, System.Windows.RoutedEventArgs)
   at System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs)
   at System.Windows.Controls.Primitives.ButtonBase.OnClick()
   at System.Windows.Controls.Button.OnClick()
   at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(System.Windows.Input.MouseButtonEventArgs)
   at System.Windows.UIElement.OnMouseLeftButtonUpThunk(System.Object, System.Windows.Input.MouseButtonEventArgs)
   at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(System.Delegate, System.Object)
   at System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate, System.Object)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(System.Object, System.Windows.RoutedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(System.Object, System.Windows.RoutedEventArgs, Boolean)
   at System.Windows.UIElement.ReRaiseEventAs(System.Windows.DependencyObject, System.Windows.RoutedEventArgs, System.Windows.RoutedEvent)
   at System.Windows.UIElement.OnMouseUpThunk(System.Object, System.Windows.Input.MouseButtonEventArgs)
   at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(System.Delegate, System.Object)
   at System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate, System.Object)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(System.Object, System.Windows.RoutedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(System.Object, System.Windows.RoutedEventArgs, Boolean)
   at System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject, System.Windows.RoutedEventArgs)
   at System.Windows.UIElement.RaiseTrustedEvent(System.Windows.RoutedEventArgs)
   at System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs, Boolean)
   at System.Windows.Input.InputManager.ProcessStagingArea()
   at System.Windows.Input.InputManager.ProcessInput(System.Windows.Input.InputEventArgs)
   at System.Windows.Input.InputProviderSite.ReportInput(System.Windows.Input.InputReport)
   at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr, System.Windows.Input.InputMode, Int32, System.Windows.Input.RawMouseActions, Int32, Int32, Int32)
   at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr, MS.Internal.Interop.WindowMessage, IntPtr, IntPtr, Boolean ByRef)
   at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)
   at MS.Win32.HwndWrapper.WndProc(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(System.Object)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, System.Object, Int32)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(System.Object, System.Delegate, System.Object, Int32, System.Delegate)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority, System.TimeSpan, System.Delegate, System.Object, Int32)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr, Int32, IntPtr, IntPtr)


And then i found another interesting thing. Once I clicked OK on the error prompt, the Create ASP.NET Web application wizard became useless –> You can choose any other option (I switched my selection to “No Authentication”) but it kept on restarting the Visual Studio.

Sign in address error 2

And here is the event log entry:

Application: devenv.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.InvalidOperationException
Stack:
   at System.Windows.Window.set_DialogResult(System.Nullable`1<Boolean>)
   at Microsoft.VisualStudio.Web.Project.AuthenticationDialogWindow.OkAuthenticationButton_Click(System.Object, System.Windows.RoutedEventArgs)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(System.Object, System.Windows.RoutedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(System.Object, System.Windows.RoutedEventArgs, Boolean)
   at System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject, System.Windows.RoutedEventArgs)
   at System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs)
   at System.Windows.Controls.Primitives.ButtonBase.OnClick()
   at System.Windows.Controls.Button.OnClick()
   at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(System.Windows.Input.MouseButtonEventArgs)
   at System.Windows.UIElement.OnMouseLeftButtonUpThunk(System.Object, System.Windows.Input.MouseButtonEventArgs)
   at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(System.Delegate, System.Object)
   at System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate, System.Object)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(System.Object, System.Windows.RoutedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(System.Object, System.Windows.RoutedEventArgs, Boolean)
   at System.Windows.UIElement.ReRaiseEventAs(System.Windows.DependencyObject, System.Windows.RoutedEventArgs, System.Windows.RoutedEvent)
   at System.Windows.UIElement.OnMouseUpThunk(System.Object, System.Windows.Input.MouseButtonEventArgs)
   at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(System.Delegate, System.Object)
   at System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate, System.Object)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(System.Object, System.Windows.RoutedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(System.Object, System.Windows.RoutedEventArgs, Boolean)
   at System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject, System.Windows.RoutedEventArgs)
   at System.Windows.UIElement.RaiseTrustedEvent(System.Windows.RoutedEventArgs)
   at System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs, Boolean)
   at System.Windows.Input.InputManager.ProcessStagingArea()
   at System.Windows.Input.InputManager.ProcessInput(System.Windows.Input.InputEventArgs)
   at System.Windows.Input.InputProviderSite.ReportInput(System.Windows.Input.InputReport)
   at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr, System.Windows.Input.InputMode, Int32, System.Windows.Input.RawMouseActions, Int32, Int32, Int32)
   at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr, MS.Internal.Interop.WindowMessage, IntPtr, IntPtr, Boolean ByRef)
   at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)
   at MS.Win32.HwndWrapper.WndProc(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(System.Object)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, System.Object, Int32)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(System.Object, System.Delegate, System.Object, Int32, System.Delegate)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority, System.TimeSpan, System.Delegate, System.Object, Int32)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr, Int32, IntPtr, IntPtr)


So I reverted to an older technique that made my ASP.NET website work with my Azure Active Directory. I used Azure management portal to add an application to my AD. I called it AzureADTesting and added my application’s URI and URL (I kept both as same https://localhost:44300/).

add applications
Copied the Federation Metadata document URL.

Azure AD Testing

Created an standard website in Visual Studio 2012 and used “Identity and Access Tool” for Visual Studio 2012 to point it to the Federation metadata endpoint of my Azure AD. It makes the required changes in web.config and adds necessary assembly references. Now when you run the web application, it redirects to Azure AD for authentication. Remember, you can not use your Microsoft Account to login.
Live account error

Users have to be in User role in AD. It is easy actually, you can create a new user in the AD and use the credentials to login.

Create an ASP.NET Web application in Visual Studio 2013 with “No Authentication” option. I used the same application name as i used when creating the ASP.NET MVC4 application.

Copied the web.config to new application from old application. Copied “System.IdentityModel.Tokens.ValidatingIssuerNameRegistry” assembly from the Packages folder from application created in step # 3 to new application and added a reference. That’s it. You are set now. It should work for you.
In order to verify that application was indeed signing in users using AD, I modified the Index view of Home controller and it showed the email address of the user in Name.

Change to Index view of Home controller

Email address

I will spend some more time on finding out the real reason why the same AD is not working with the default template of VS 2013. May be it is a known issue or may be i am missing something.

No comments:

Post a Comment