Authentication, Roles, and Profiles

[ This document was written for WCF Services Version 1 Service Pack 2 and might not be up to date Please see Release Notes or Changelog for a list of changes since WCF RIA Services ]

You add the authentication domain service to your Open Ria Services solution when you want to verify a user's credentials, restrict access for certain operations, or retain properties for each user from your client project. In a traditional ASP.NET Web application, you can use the ASP.NET Membership framework to perform these functions. Open Ria Services builds upon the ASP.NET Membership framework by exposing the Membership framework to rich Internet clients through the authentication domain service. After adding an authentication domain service, you can enable the following functions:

  • Authentication - to verify a user's credentials and mark the user as logged in or logged out.

  • Roles - to group users by responsibilities and grant resource permissions to authenticated members of a group.

  • Profiles - to retain properties for authenticated users and retrieve those properties in your application.

This topic introduces how you use authentication, roles, and profiles in a Open Ria Services solution.

Authentication Domain Service

Open Ria Services provides the Authentication Domain Service template to facilitate accessing authentication, roles, and profiles on the presentation tier. To create an authentication domain service, you simply create a new item in the server project and select the Authentication Domain Service template when creating the item.

When you add an authentication domain service, the Open Ria Services framework automatically adds two classes to the server project. The class that represents the authentication service derives from the AuthenticationBase\ class. The class that represents the user derives from the UserBase class. The user class contains the profile properties for an authenticated user.

When you build the solution, Open Ria Services automatically generates a WebContext class in the client project. The WebContext class enables you to access the authentication domain service and the user in your client project. You use the Current property to retrieve the current instance of the WebContext. The WebContext class derives from WebContextBase.

For an example of how to add an authentication domain service to a Open Ria Services solution, see Walkthrough: Using Authentication Service with Silverlight Navigation Application.

Silverlight Business Application and Authentication

When you select the Silverlight Business Application template to create a solution, the solution automatically includes an authentication domain service and controls to manage logging in and registering users. By default, the solution uses Forms authentication, but you can easily configure it for Windows authentication. Roles are enabled and one profile property is defined. For an example of the authentication features that are included by default in a Silverlight Business Application and how you change the configuration from Forms authentication to Windows authentication, see Walkthrough: Using the Silverlight Business Application Template. For an example of building upon the default features in a Silverlight Business Application, see Walkthrough: Using Authentication Service with Silverlight Business Application.

The following illustration shows the registration window, which is one of the default features included in the Silverlight Business Application.

Authentication

Open Ria Services provides classes that enable you to easily implement Forms authentication or Windows authentication in your solution. To use authentication in your Open Ria Services solution, you must configure the server project and the client project for authentication. For more information, see How to: Enable Authentication in Open Ria Services.

After configuring the server and client projects, you asynchronously log in users from your Silverlight application by calling the Login method on the WebContext object. When using Windows authentication or when retrieving a user with persisted credentials, you do not have to call the Login method. Instead, you call the LoadUser method to retrieve the user that is authenticated through Windows authentication or to load a user with persisted credentials.

The following methods and properties are typically used in the client project when you have implemented authentication.

Member

Code to use

Task

WebContext.Current.Authentication

To access the authentication service.

WebContext.Current.User

To access the object that contains the state of the user.

WebContext.Current.Authentication.Login(string, string)

-or-

WebContext.Current.Authentication.Login(LoginParameters)

-or-

WebContext.Current.Authentication.Login(LoginParameters, LoginOperation, object)

To asynchronously verify the user's credentials.

WebContext.Current.Authentication.Logout(boolean)

-or-

WebContext.Current.Authentication.Logout(LogoutOperation, object)

To asynchronously log out an authenticated user.

WebContext.Current.Authentication.LoadUser()

-or-

WebContext.Current.Authentication.LoadUser(LoadUserOperation, object)

To load an authenticated user, refresh user state, load persisted user authentication, or retrieve principal user object when used with Windows authentication.

The following example shows how to call the Login method from an event handler for a login button. A callback method is included to respond to the results of the login operation.

Private Sub LoginButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim lp As LoginParameters = New LoginParameters(UserName.Text, Password.Password)
    WebContext.Current.Authentication.Login(lp, AddressOf Me.LoginOperation_Completed, Nothing)
    LoginButton.IsEnabled = False
    LoginResult.Text = ""
End Sub

Private Sub LoginOperation_Completed(ByVal lo As LoginOperation)
    If (lo.HasError) Then
        LoginResult.Text = lo.Error.Message
        LoginResult.Visibility = System.Windows.Visibility.Visible
        lo.MarkErrorAsHandled()
    ElseIf (lo.LoginSuccess = False) Then
        LoginResult.Text = "Login failed. Please check user name and password."
        LoginResult.Visibility = System.Windows.Visibility.Visible
    ElseIf (lo.LoginSuccess = True) Then
        SetControlVisibility(True)
    End If
    LoginButton.IsEnabled = True
End Sub
private void LoginButton_Click(object sender, RoutedEventArgs e)
{
    LoginParameters lp = new LoginParameters(UserName.Text, Password.Password);
    WebContext.Current.Authentication.Login(lp, this.LoginOperation_Completed, null);
    LoginButton.IsEnabled = false;
    LoginResult.Text = "";
}

private void LoginOperation_Completed(LoginOperation lo)
{
    if (lo.HasError)
    {
        LoginResult.Text = lo.Error.Message;
        LoginResult.Visibility = System.Windows.Visibility.Visible;
        lo.MarkErrorAsHandled();
    }
    else if (lo.LoginSuccess == false)
    {
        LoginResult.Text = "Login failed. Please check user name and password.";
        LoginResult.Visibility = System.Windows.Visibility.Visible;
    }
    else if (lo.LoginSuccess == true)
    {
        SetControlVisibility(true);
    }
    LoginButton.IsEnabled = true;
}

Roles

After you have implemented authentication, you can configure your solution to use roles. With roles, you can assign users to groups. Then, you can specify that a particular domain operation is only available to members of that role. You restrict access to a domain operation by applying the RequiresRoleAttribute to the domain operation. For more information, see How to: Enable Roles in Open Ria Services.

The following methods and properties are typically used when you have implemented roles.

Member

Code to use

Task

WebContext.Current.User.Roles

To access the roles the user is assigned to.

WebContext.Current.User.IsInRole(string)

To determine whether the authenticated user is a member of a specified role.

The following example shows a domain operation with access restricted to members of a role named Managers.

<RequiresRole("Managers")> _
Public Function GetCustomers() As IQueryable(Of Customer)
    Return Me.ObjectContext.Customers
End Function
[RequiresRole("Managers")]
public IQueryable<Customer> GetCustomers()
{
    return this.ObjectContext.Customers;
}

If you call a domain operation when the user does not have the required credentials, the domain operation returns an exception. You can avoid this situation by checking the credentials before calling the domain operation. The following example shows how to check if the user is a member of the required role before loading the data.

Private Sub LoadRestrictedReports()
    Dim loadSales = context.Load(context.GetSalesOrderHeadersQuery().Take(numberOfRows))
    SalesOrdersGrid.ItemsSource = loadSales.Entities
    SalesOrdersGrid.Visibility = System.Windows.Visibility.Visible

    If (WebContext.Current.User.IsInRole("Managers")) Then
        Dim loadCustomers = context.Load(context.GetCustomersQuery().Take(numberOfRows))
        CustomersGrid.ItemsSource = loadCustomers.Entities
        CustomersGrid.Visibility = System.Windows.Visibility.Visible
    Else
        CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
    End If
End Sub
private void LoadRestrictedReports()
{
    LoadOperation<SalesOrderHeader> loadSales = context.Load(context.GetSalesOrderHeadersQuery().Take(numberOfRows));
    SalesOrdersGrid.ItemsSource = loadSales.Entities;
    SalesOrdersGrid.Visibility = System.Windows.Visibility.Visible;

    if (WebContext.Current.User.IsInRole("Managers"))
    {
        LoadOperation<Customer> loadCustomers = context.Load(context.GetCustomersQuery().Take(numberOfRows));
        CustomersGrid.ItemsSource = loadCustomers.Entities;
        CustomersGrid.Visibility = System.Windows.Visibility.Visible;
    }
    else
    {
        CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;
    }
}

Profiles

Profile properties enable you to save information about the user. You can use these properties to customize your application for each user. To use profiles in your solution, you must configure your solution for profiles. For more information, see How to: Enable Profiles in Open Ria Services.

The following methods and properties are typically used when you have implemented profiles.

Member

Code to use

Task

WebContext.Current.User

To access the object that contains all of the properties that you have added to the User class; for example, User.PhoneNumber.

WebContext.Current.Authentication.LoadUser()

-or-

WebContext.Current.Authentication.LoadUser(LoadUserOperation, object)

To refresh the state of the user.

WebContext.Current.Authentication.SaveUser(boolean)

-or-

WebContext.Current.Authentication.SaveUser(SaveUserOperation, object)

To save any changes in the user state; such as, after setting a profile property value.

The following example shows how to set a user property based on the value selected by the user.

Private Sub OKButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles OKButton.Click
    Dim newSelection = Integer.Parse(defaultRows.SelectionBoxItem.ToString())
    If (newSelection <> WebContext.Current.User.DefaultRows) Then
        WebContext.Current.User.DefaultRows = newSelection
        WebContext.Current.Authentication.SaveUser(True)
    End If
    Me.DialogResult = True
End Sub
private void OKButton_Click(object sender, RoutedEventArgs e)
{
    int newSelection = int.Parse(defaultRows.SelectionBoxItem.ToString());
    if (newSelection != WebContext.Current.User.DefaultRows)
    {
        WebContext.Current.User.DefaultRows = newSelection;
        WebContext.Current.Authentication.SaveUser(true);
    }
    this.DialogResult = true;
}

Handling Authentication Errors on the Client

You can handle errors that arise when logging in, logging out, loading, or saving users by providing a callback method as a parameter when you call those methods. In the callback method, you add code to handle the error and call the MarkErrorAsHandled method to specify that the framework will not throw an exception. The AuthenticationService class enables you to provide a callback method when you call the following methods:

The example in the previous "Authentication" section shows a callback method for the Login operation that handles errors.

For more information, see Error Handling on the Client.

Restricting Access to a Domain Service

After you have implemented authentication and roles, you can restrict access on a domain service to only particular users. You apply the following attributes to either the entire domain service or to individual operations on the service. When you apply an attribute to the entire service, it applies to all of the operations.

You can also create your own customized authorization attribute. For more information, see How to: Create a Custom Authorization Attribute.

The following example shows a domain service with three domain operations. The RequiresAuthenticationAttribute and RequiresRoleAttribute attributes are used to restrict access. The GetProducts domain operation is available to any user, GetSalesOrderHeaders is available to authenticated users, and GetCustomers is available to only users in the Managers role.

<EnableClientAccess()>  _
Public Class AdventureWorksDomainService
    Inherits LinqToEntitiesDomainService(Of AdventureWorksLT_DataEntities)

    <RequiresRole("Managers")> _
    Public Function GetCustomers() As IQueryable(Of Customer)
        Return Me.ObjectContext.Customers
    End Function

    Public Function GetProducts() As IQueryable(Of Product)
        Return Me.ObjectContext.Products
    End Function

    <RequiresAuthentication()> _
    Public Function GetSalesOrderHeaders() As IQueryable(Of SalesOrderHeader)
        Return Me.ObjectContext.SalesOrderHeaders
    End Function
End Class
[EnableClientAccess()]
public class AdventureWorksDomainService : LinqToEntitiesDomainService<AdventureWorksLT_DataEntities>
{
    [RequiresRole("Managers")]
    public IQueryable<Customer> GetCustomers()
    {
        return this.ObjectContext.Customers;
    }

    public IQueryable<Product> GetProducts()
    {
        return this.ObjectContext.Products;
    }

    [RequiresAuthentication()]
    public IQueryable<SalesOrderHeader> GetSalesOrderHeaders()
    {
        return this.ObjectContext.SalesOrderHeaders;
    }
}

See Also

Tasks

Walkthrough: Using Authentication Service with Silverlight Navigation Application

Walkthrough: Using Authentication Service with Silverlight Business Application

Concepts

Building Secure Applications with Open Ria Services

Last updated