Note: this was done with Silverlight 5 and Prism 4
In the past month our team started a new project and after many discussions of how to build it right, decided to use the Prism framework (you an read more on Prism here). I was put in charge of providing a service (as in a Silverlight utility class) for Authentication and Authorization using Windows Authentication and Active Directory groups.
The way we handled that in our previous project was using the built in services in RIA services (the only use we had for RIA Services). But that implementation used:
- Code in the main Silverlight Application project (not in Class Library projects)
- Configuration in App.Xaml
- Configuration in web.config
So I tried for two days to create the code in a Silverlight Application project that is not the main project (not the one referenced in the ASPX page), with no success and many errors. I found many forum posts that claim it is possible but all the examples were either Forms Authentication or used the main Silverlight Application (like this one or this one). The main problem I found is the configuration that should be put in the App.Xaml:
- <Application.ApplicationLifetimeObjects>
- <app:WebContext>
- <app:WebContext.Authentication>
- <appsvc:WindowsAuthentication/>
- </app:WebContext.Authentication>
- </app:WebContext>
- </Application.ApplicationLifetimeObjects>
The app preface is from the generated code that is generated in a Module Silverlight Application, but the App.Xaml is in the Main Silverlight Application and it just doesn’t work to use it outside of the project it was generated at. Adding App.xaml in the Module Silverlight Application won’t work either because Prism doesn’t call the App.Xaml of inner Modules (though having that Module run as it’s own main application does work).
In the end I decided to try to implement the RIA Authentication myself using WCF Services (me and my team leader have burned to much time on RIA by that point… and since we use it for just authentication it didn’t seem justified).
The WCF Services web site must be configured in IIS to use Windows Authentication. The code for the service is:
- [ServiceContract]
- public interface IAuthenticationService
- {
- [OperationContract]
- User GetUser();
- }
- [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
- public class AuthenticationService : IAuthenticationService
- {
- public User GetUser()
- {
- var result = new User { Name = OperationContext.Current.ServiceSecurityContext.WindowsIdentity.Name };
- if (OperationContext.Current.ServiceSecurityContext.WindowsIdentity.Groups != null)
- {
- result.Groups = new List<string>();
- foreach (var userGroup in OperationContext.Current.ServiceSecurityContext.WindowsIdentity.Groups)
- {
- result.Groups.Add(userGroup.Translate(typeof(NTAccount)).Value);
- }
- }
- return result;
- }
- }
The user groups default values are in SID format so the code:
- userGroup.Translate(typeof(NTAccount)).Value
translate it to readable text.
User is defined as:
- [DataContract]
- public class User
- {
- [DataMember]
- public string Name { get; set; }
- [DataMember]
- public IList<string> Groups { get; set; }
- }
Web.Config code:
- <configuration>
- <system.web>
- <compilation debug="true" targetFramework="4.0" />
- <authentication mode="Windows" />
- </system.web>
- <system.serviceModel>
- <behaviors>
- <serviceBehaviors>
- <behavior name="AllServicesBehavior">
- <serviceMetadata httpGetEnabled="true" />
- <serviceDebug includeExceptionDetailInFaults="true" />
- <dataContractSerializer maxItemsInObjectGraph="2147483647" />
- </behavior>
- <behavior name="">
- <serviceMetadata httpGetEnabled="true" />
- <serviceDebug includeExceptionDetailInFaults="false" />
- </behavior>
- </serviceBehaviors>
- </behaviors>
- <bindings>
- <customBinding>
- <binding name="customBinaryEncodedBinding">
- <binaryMessageEncoding/>
- <httpTransport authenticationScheme="Negotiate"/>
- </binding>
- </customBinding>
- </bindings>
- <services>
- <service behaviorConfiguration="AllServicesBehavior" name="DllShepherd.AuthenticationService">
- <endpoint address="" binding="customBinding" bindingConfiguration="customBinaryEncodedBinding" contract="DllShepherd.IAuthenticationService"/>
- </service>
- </services>
- </system.serviceModel>
- </configuration>
The important part is:
- <httpTransport authenticationScheme="Negotiate"/>
and:
- <authentication mode="Windows" />
that enables the authentication and binaryMessageEncoding for Silverlight.
In the Silverlight Services Class Library project (Services is part of the Prism terminology for client services, usually Singleton utility classes), add the service reference (I do this part usually in code) and consume it using usual Silverlight code:
- public string UserName { get; private set; }
- public event EventHandler UserLoaded;
- public void GetUser()
- {
- Client.Begin("GetUser", GetUserCompleted, null, null);
- }
- private void GetUserCompleted(object sender, ObjectClient<DllShepherd.IAuthenticationService>.ClientEventArgs e)
- {
- Dispatcher.BeginInvoke(() =>
- {
- var result = e.LoadResult<User>();
- UserName = result.Name;
- AuthorizationService.Roles = result.Groups;
- if (UserLoaded != null)
- UserLoaded(this, null);
- });
- }
AuthorizationService will later use the Groups to check if the user is authorized to view Modules, Views or data.
In the Prism way the Modules now know only the interface for this Silverlight service (the interface is in the Infrastructure project which everyone knows), which look something like:
- public interface IAuthenticationService
- {
- #region EVENTS
- event EventHandler UserLoaded;
- #endregion
- #region METHODS
- void GetUser();
- #endregion
- string UserName { get; }
- }
But the implementation is only known by the Application (not the Modules which just use IAuthenticationService with a Prism Import) and there is no special code or configuration needed in the Application.
Resources:
Silverlight.Net Forum - Prism, RIA & Authentication (VS 2010)
Silverlight.Net Forum - PRISM 4 and RIA services
Prism for Silverlight/MEF in Easy Samples. Part 1 - Prism Modules (there are three parts)