Updates to the Custom and Authenticating URI Mappers for Silverlight 3 RTW

by StefanOlson 3. August 2009 08:51

Update: I'd forgotten to update the sample that goes along with the download. It has now been updated to support the .net RIA services July CTP.

I’ve received a couple of requests in the past few days asking if there will be an update to the URI mappers (Updates to custom routing using the Uri Mapper in Silverlight 3 and Displaying a login dialog based on page attributes in Silverlight 3) that work with the Silverlight 3 RTW & .net RIA services July CTP. 

The CustomUriMapper works just fine with Silverlight 3. However, I have fixed one bug and provided a project you can include in your solution to make it easy to use it. You can find it under UriMapper\AuthenticatingUriMapper\CustomUriMapper.csproj.

Unfortunately I had forgotten that changes were required to get the AuthenticatingUriMapper working with the July CTP and had neglected to update it on the website.

Download

You can download the latest source code and example here.

…Stefan

Tags:

.net ria services | Silverlight

Displaying a login dialog based on page attributes in Silverlight 3

by StefanOlson 19. June 2009 15:44

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:

image

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:

image

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

Tags:

Silverlight | .net ria services

Login with e-mail address using .net RIA services

by StefanOlson 14. May 2009 09:16

I'm currently working a very interesting project using Silverlight 3 and the new .net RIA services. Basically we're building the whole e-commerce type website using Silverlight 3. If you don't have Silverlight 3 installed you'll be presented with HTML versions of the pages that you can browse and if you need to login or use any of the interactive features then we will require you to install Silverlight 3.

I still have concerns about the install base of Silverlight. The site is entirely focused on New Zealand users According to riastats.com, 62.5% of New Zealanders do not have Silverlight installed. This differs from the statistics provided by some developers who work for ACP media, where they said around 80% don't have Silverlight installed (ACP Media Development Leads To Pure Direction).  Either way, there is still a lot of people who do not have Silverlight installed, although it is reducing over time. This won't be a long-term problem, but is a major challenge for Microsoft when it comes to websites adopting Silverlight.

The website makes extensive use of the new .net RIA services, which is currently in preview. If you haven't heard about this, it provides a way to access your database information from a Silverlight application. For a good overview see Brad Abrams blog, here.

For a preview release, the .net RIA services seem incredibly stable. I haven't encountered any serious bugs, at this stage.

Because many users quickly forget the username for a specific site I want people to be able to login using their e-mail address. I recently started a thread on the Silverlight.net RIA services forum to try and find out if this was possible.  What I wanted to be able to do was to override the login function on the authentication service and replace the given username, which was an e-mail address with the user's actual username. The code would've looked something like this:

public class AuthenticationWithEmailLogin<T> : AuthenticationBase<T> where T : UserBase, new()
{
    public override User Login(string userName, string password, bool isPersistent)
    {
        string newUserName = Membership.GetUserNameByEmail(userName);
        if (newUserName != null) // if the user has provided an e-mail address use the username associated with that
        {
            userName = newUserName;
        }
        return base.Login(userName, password, isPersistent);
    }
} 

Unfortunately, Login is not virtual, so I can't use that option.

Eventually, Kyle suggested that I override several other functions in order to make it do what I want.  Here's what I ended up with ( simplified after some further suggestions from Kyle):

public class AuthenticationWithEmailLogin<T> : AuthenticationBase<T> where T : UserBase, new()
{
    protected virtual string GetUserNameToUse(string userName)
    {
        string newUserName = Membership.GetUserNameByEmail(userName);
        if (newUserName != null)
        {
            userName = newUserName;
        }
        return userName;
    }

    protected virtual IPrincipal GetPrincipalWithCorrectName(IPrincipal principal)
    {
        return new GenericPrincipal(
                new GenericIdentity(GetUserNameToUse(principal.Identity.Name), principal.Identity.AuthenticationType),
                new string[0]);
    }

    protected override bool ValidateUser(string userName, string password)
    {
        return base.ValidateUser(GetUserNameToUse(userName), password);
    }

    protected override T GetAuthenticatedUser(IPrincipal principal)
    {
        return base.GetAuthenticatedUser(GetPrincipalWithCorrectName(principal));
    }

    protected override void IssueAuthenticationToken(IPrincipal principal, bool isPersistent)
    {
        base.IssueAuthenticationToken(GetPrincipalWithCorrectName(principal), isPersistent);
    }
}

You can download my complete class from here

Hopefully it’ll help someone.

…Stefan

Tags:

Silverlight | .net ria services

About the author

Stefan Olson is the Managing Director of Olson Software.  He has been developing software using Microsoft Technologies for nearly 20 years.

He is currently working on building the next generation Virtual Tour software in WPF and Silverlight for www.palacevirtualtours.com.