Update: The latest version of the code is available for download here. This page has been re-written for the .net ria Services July CTP.
One of the feature requests I described in my post Issues with the Silverlight Navigation architecture was the ability to use an attribute to declare the required roles to display a particular page. e.g:
[RequiresAuthentication]
public partial class CategoryPage : Page
Unfortunately this is not part of Silverlight 3, so I set about implementing it myself based on the custom URI mapper I have developed in previous posts.
Let me introduce the AuthenticatingUriMapper! You can download the source code and example here.
All source code described below is part of the sample. It shows how you can provide attributes on a page to automatically have a login dialog displayed when navigating to it if required! The sample is based on the Silverlight business application template, that is installed as part of the .net RIA services. Sample requires the .net RIA services to be installed.
e.g:
Using it couldn't be simpler! Just like the custom URI mapper, you add it as your uriMapper (in the Frame or as a StaticResource):
<Navigation2:AuthenticatingUriMapper DefaultPage="/Views/EmptyPage.xaml">
<Navigation2:CustomUriMapper.CustomUriMappings>
<UriMappingNavigationSample:AboutBoxRoute/>
<routing:CustomRoute/>
</Navigation2:CustomUriMapper.CustomUriMappings>
</Navigation2:AuthenticatingUriMapper>
See my previous post for details on the default page attribute used above.
Then in your mainpage constructor, you tell it about the frame and handle any authentication requests:
public MainPage()
{
InitializeComponent();
this.loginContainer.Child = new LoginControl();
(ContentFrame.UriMapper as AuthenticatingUriMapper).Frame = frame;
(ContentFrame.UriMapper as AuthenticatingUriMapper).AuthenticationRequired += MainPage_AuthenticationRequired;
}
In the MainPage_AuthenticationRequired function, you simply display the login dialog. We are passing the event arguments so that later on, when login has been completed, we can verify , that , the new login credentials are valid for the page the user wishes to go to.
void MainPage_AuthenticationRequired(object sender, AuthenticationRequiredEventArgs e)
{
LoginWindow dialog = new LoginWindow { AuthenticationRequiredEventArgs = e};
dialog.Show();
}
Then, in the pages we wish to require authentication for, add either of the following two attributes:
[RequiresAuthentication]
public partial class UserPage : Page
Or, if you need specific roles:
[RequiresRoles("Administrator")]
public partial class AdminPage : Page
In the login dialog we can make some changes to make use of the AuthenticationRequiredEventArgs.
When the login is successful , we need to verify that now that the user is logged in they passed the authentication requirements of the page they are going to . Here's how we do this:
else if (loginOp.LoginSuccess)
{
DialogResult = true;
if (AuthenticationRequiredEventArgs != null)
{
AuthorizationAttribute attribute = AuthenticationRequiredEventArgs.RecheckAttributes();
// verify the newly logged in account meets the requirements of the page we are visiting
if (attribute == null)
{
// now we know we've passed the requirements, we can navigate to the page the user
// originally wanted to go to
AuthenticationRequiredEventArgs.Frame.Navigate(AuthenticationRequiredEventArgs.UnmappedUri);
_authOp=null;
DialogResult=false;
return;
}
if (attribute is RequiresRolesAttribute && RiaContext.Current.User.IsAuthenticated)
{
MessageBox.Show("This account does not have authorization to view the page you are requesting.");
DialogResult = false;
_authOp=null;
return;
}
}
}
For this demo we're going to display a message if you are logged in using an account that doesn't have the appropriate level of credentials. For example, if you need administrator credentials to access a page but don't currently have them we wish the login dialog to indicate that. In the picture below, we are currently logged on under the user account, but we actually need administrative privileges, so you can see that a message is displayed to that effect:
The code to achieve this is pretty simple, We just display the yellow box if appropriate when the event arguments are set:
private AuthenticationRequiredEventArgs _authenticationRequiredEventArgs;
public AuthenticationRequiredEventArgs AuthenticationRequiredEventArgs
{
get { return _authenticationRequiredEventArgs; }
set { _authenticationRequiredEventArgs = value;
if (AuthenticationRequiredEventArgs.AuthorizationAttribute is RequiresRolesAttribute && RiaContext.Current.User.IsAuthenticated)
{
_RoleRequired.Visibility = Visibility.Visible;
}
}
}
You can try this all out yourself by downloading the source code and making use of it in your own applications!
…Stefan