RIA Services and Authentication

An in-depth look at authentication with RIA Services using the Book Club application as a sample, using the User object across both client and server, as well as implementing custom authentication.

Authentication is the third in a series of posts covering the key concepts of RIA Services using the Book Club application to digger deeper and go beyond the basics. Links to the first two posts on validation and authorization as well as an overview of the application/source code are at the end of this post.

Authentication Overview

Like authorization, RIA Services provides a higher level programming model, and out-of-the-box, but extensible solution. Authentication answers the question:

"Do these credentials represent a valid user?"

Credentials might be user name and password, or any other piece of data that can be used to verify that the user is who he/she says they are. Generally, a side-effect of authentication is to produce a representation of the user, usually represented as an IPrincipal, as well as establishing an authenticated session for the client to use in making subsequent requests.

RIA Service defines an authentication service as a domain service that implements IAuthetication<TUser> where TUser is application's notion of a user that brings together identity, roles and settings that span across client and server.

RIA Services also provides an out-of-box implementation based on the standard asp.net membership, roles and profile infrastructure services. If you use the business application template, this is all setup for you by default. However RIA Services also lets you implement your own authentication service when you want to use your own custom credential store, or a different authentication mechanism such as OpenID.

This post covers using authentication and the User object on client and server, as well as building a custom forms authentication service that works against the application's data model.


Using Authentication on the Client

Login Control I created an inplace-LoginControl with a number of visual states (Unauthenticated, CredentialInput, Authenticating and Authenticated) as shown here.

Authentication functionality is accessed through a class called WebContext on the client. WebContext represents the functionality provided by the home web server to the client application. This is how WebContext is initialized in the application:

public partial class WebContext {

    partial void OnCreated() {
        Authentication = new FormsAuthentication() {
            DomainContext = new AuthenticationContext()
        };
    }
}

The FormsAuthentication implementation uses a DomainContext to work against the corresponding DomainService implementing IAuthentication on the server. We'll see this service implementation below.

Looking at LoginControl implementation, the authentication-related functionality is invoked when the Submit and Logout buttons are clicked:

private void OnSubmitButtonClick(object sender, RoutedEventArgs e) {
    LoginParameters loginParameters =
        new LoginParameters(userNameTextBox.Text, passwordBox.Password);

    WebContextBase.Current.Authentication.Login(loginParameters,
        delegate(LoginOperation operation) {
            if (operation.HasError || (operation.LoginSuccess == false)) {
                MessageBox.Show(...);
                operation.MarkErrorAsHandled();
                return;
            }

            // Use the resulting User object
            userLabel.Text = "Welcome " + WebContext.Current.User.DisplayName;
        }, null);
}

private void OnLogoutButtonClick(object sender, RoutedEventArgs e) {
    WebContextBase.Current.Authentication.Logout(/* throwOnError */ false);
}

The resulting User object can be used imperatively as shown in the code above, or declaratively via data-bindings.


Using Authentication on the Server

The User object can also be used on the server, for example by other domain services. Here is a snippet of code from the BookShelfService:

[RequiresAuthentication]
public class BookShelfService : LinqToEntitiesDomainServiceEx<BookClubEntities> {
    private User _user;

    public BookShelfService(User user) {
        _user = user;
    }

    public IQueryable<Book> GetBooks() {
        return ObjectContext.Books.Where(b => b.MemberID == _user.MemberID).
                                    OrderByDescending(b => b.AddedDate);
    }

    public override void Initialize(DomainServiceContext context) {
        base.Initialize(context);

        // This allows authorization rules to get access to the current user
        AuthorizationContext customAuthContext =
            new AuthorizationContext(context);
        customAuthContext.Items[typeof(User)] = _user;

        AuthorizationContext = customAuthContext;
    }
}

The domain service has been marked with the authorization rule stating the service requires authentication. Consequently, RIA Services will ensure that there is a valid IPrincipal whenever this service is invoked. However, the service also requires a User object to be passed in into constructor, so it can use it when retrieving shared by the current user, or when initializing an AuthorizationContext to pass along the User in into authorization rules.

In order to pass in custom values through the constructor, one needs to implement a custom DomainServiceFactory. The default one only works for domain services with default parameterless constructors. In the application, I have implemented a simple domain service factory, that uses hardcoded logic, but in a larger application, the general idea is to use an IoC container and call into that from the custom DomainServiceFactory.

internal sealed class DomainServiceFactory : IDomainServiceFactory {
    private IDomainServiceFactory _defaultFactory;

    public DomainServiceFactory(IDomainServiceFactory defaultFactory) {
        _defaultFactory = defaultFactory;
    }

    public DomainService CreateDomainService(Type domainServiceType, DomainServiceContext context) {
        if ((domainServiceType == typeof(BookShelfService)) ||
            (domainServiceType == typeof(BookClubService))) {
            User currentUser =
                Authentication.GetUser<User, AuthenticationService>(context);

            DomainService domainService =
                (DomainService)Activator.CreateInstance(domainServiceType,
                                                        currentUser);
            domainService.Initialize(context);

            return domainService;
        }

        return _defaultFactory.CreateDomainService(domainServiceType, context);
    }

    public void ReleaseDomainService(DomainService domainService) {
        if ((domainService is BookShelfService) ||
            (domainService is BookClubService)) {
            domainService.Dispose();
        }
        else {
            _defaultFactory.ReleaseDomainService(domainService);
        }
    }

    public static void Setup() {
        // Call this from Global.asax.cs in the Application's Start event handler
        if (!(DomainService.Factory is DomainServiceFactory)) {
            DomainService.Factory =
                new DomainServiceFactory(DomainService.Factory);
        }
    }
}

Notice the use of the helper class, Authentication, that allows you to correctly instantiate the authentication service on the server, invoke it and retrieve the User object corresponding to the current authenticated principal. This helper class is in the RIAEssentials framework (link below). The resulting User object is passed in when constructing BookShelfService.


Book Club Data Model

Before we see the actual Authentication service implementation It is worth taking a quick look at the data model behind the Book Club application:

Book Club Data Model

As you see, one of the tables is the Member table. It is intrinsically tied to various other tables by virtue of multiple associations. It also contains information about the user, such as display name, last login date, joined date etc. As such, I'd like my authentication implementation to use this Member table from my data model directly.


The User Object

One of the key things to think about in your application is what constitutes the User object than spans across client and server. What is the state you want to expose to the client for visualization purposes, and what is the state you want to have quick access to within the context of an authenticated session? These are the questions to ask.

The UserBase object provides the ability to track Name and Roles. The User object in the application is defined as follows to add additional properties:

public class User : UserBase {
    public string DisplayName { get; set; }
    public int MemberID { get; set; }
}

I added DisplayName, so it can be used on the client in the UI. I also added the MemberID, so this information can be used on the server on subsequent requests (for example, when doing authorization to ensure a user can only update a book they own - see the authorization post for that example). The User object will be populated from the Member entity from the data model.


Implementing Authentication

Authentication builds on the same domain services infrastructure you use to define your operations/tasks in your application. There are two approaches to implementing authentication:

  1. Derive from the out-of-box AuthenticationService base class. This provides a default implementation that leverages the asp.net membership, roles and profile infrastructure.
  2. Implement IAuthentication on your domain service and do whatever you like based on your unique scenarios when implementing Login, Logout and other methods of the interface. This is especially useful if you're not using the asp.net infrastructure services or want to use some custom auth mechanism.

In my case, I want to use my own DAL and its notion of members, so I am going to build my own custom authentication service. However, I don't really want to implement the intricacies of forms authentication, which aren't specific to my application, such as setting up HTTP cookies. I only want to write the parts that interact with my data model (very much in spirit with the rest of RIA Services - focus on your application logic, and not the on-wire or service plumbing).

I am going to use a base class called FormsAuthenticationService which implements the guts of forms authentication, and is provided as part of the RIAEssentials framework, that you can easily reuse in your own applications. With that handy base class, here is all I have to do is implement ValidateCredentials, and produce a valid User object using my data model:

[EnableClientAccess]
public class AuthenticationService : FormsAuthenticationService<User> {

    protected override User ValidateCredentials(string name, string password, string customData) {
        User user = null;
        using (BookClubEntities bookClubObjectContext = new BookClubEntities()) {
            Member member =
                bookClubObjectContext.Members.Where(m => m.MemberAlias == name).
                                              FirstOrDefault();

            if ((member != null) &&
                Authentication.ValidatePassword(password, member.PasswordHash)) {
                user = new User() {
                    Name = name,
                    MemberID = member.MemberID,
                    DisplayName = member.MemberName
                };

                if (member.LoginDate != DateTime.UtcNow.Date) {
                    member.LoginDate = DateTime.UtcNow.Date;
                    bookClubObjectContext.SaveChanges();
                }
            }
        }

        return user;
    }
}

Also note how the validation method tracks the last login date, which is something that is captured within the data model.

In the initial version of the Book Club app, the code was [incorrectly] storing passwords in the database. In reality it was a detail that I had yet to get to. Since I am now writing about authentication, I have fixed this. The database stores password hashes, rather than passwords in clear text. This required a small schema update, so you'll need to recreate the database and run the BookClub.sql SQL script before you run the code.

Any application code that uses the authentication service only depends on the contract defined by IAuthentication (whether it is the server, or the client), giving you freedom to implement what it means to authenticate and produce a User object as you desire.


Authentication over HTTPS

If you use forms authentication or even basic authentication (i.e. anything that transfers a user name/password as clear text from client to server), you would be advised to use HTTPS.

This takes a little bit of setup, i.e. setting up an HTTPS site binding, that goes beyond the scope of this post. However RIA Services makes it easy to declaratively indicate that a particular domain service should only be exposed to the world using a secure endpoint, once you've handled the server configuration.

[EnableClientAccess(RequiresSecureEndpoint = true)]
public class AuthenticationService : FormsAuthenticationService<User> {
}


Related Links

Posted on Sunday, 6/27/2010 @ 3:51 PM | #Silverlight


Comments

20 comments have been posted.

Dwight Walker

Posted on 6/27/2010 @ 7:25 PM
Incredible detailed work. It looks like you want the Web to be part of the Enterprise stack and more 'serious'.

Björn

Posted on 6/28/2010 @ 3:26 AM
What are the steps to support also user-roles?
I want to restrict the access to search books only for 'Admin' (just as a test).

I added following:
1. AuthenticationService.ValidateCredentials(...):
user = new User()
{
...
// add 'Admin' as role (HACK: just for to test - normaly it will be loaded from the db)
Roles = new List<string> { "Admin" }
};

2. Consume the Roles on Client-Side:
BookClubModel.SearchBooks(...) {
var roles = WebContext.Current.User.Roles;

=> when I logged in the user has the 'Admin'-Role

3. Define 'RequiresRole' for BookClubService.SearchBooks:
[RequiresRole("Admin")]
public IQueryable<BookInfo> SearchBooks(string searchText)

=> there is always the access denied message also if I'm logged in
result: There was an error loading the requested books. Load operation failed to query 'SearchBooks'. Access to operation 'SearchBooks' was denied.

What's missing?
- the BookClubService user-field has no rules! When and where do we have to load this information? Or should this be saved in the cookie?
- which solution you propose to implement?

Björn

Nikhil Kothari

Posted on 6/28/2010 @ 7:46 AM
@Björn
When ASP.NET forms authentication recreates a principal on subsequent requests, it doesn't know anything about roles. If you're using roles, you need to handle the event raised when a principal is created, and add in the role information (believe me, I wish forms authentication did).

It is something I wanted to account for in RIAEssentials, but another asp.net limitation pops up - there is no convenient hook point for the FormsAuthenticationService to do this... so for now you have to write code along these lines in Global.asax.cs:

protected void Application_AuthenticateRequest(object sender, EventArgs e) {
HttpContext context = HttpContext.Current;
User currentUser = Authentication.GetUser<User, AuthenticationService>(context);
if (currentUser != null) {
context.User = new GenericPrincipal(context.User.Identity, currentUser.Roles);
}
}

Hope this helps... note, that I wrote the above code while writing the comment - so it might need tweaking, and there might even be something better to do.

The above code is pretty standard - lots of blog posts on this, though I am not happy, as its ideally something that just takes care of itself. I'll see if I can provide something in the RIAEssentials framework to automatically do this, but like I said, there are some known asp.net limitations that prevent the simple/right solution from being implemented.

Steve

Posted on 6/28/2010 @ 6:23 PM
Hi there seems to be an issue with validation. I grabbed the latest from codeplex. When you add a new book if you put just 4 numbers in the ASIN field you can add the the record to the data context with the OK button and the error that the field needs to be 10 digits does not appear. If you then click save you get the error and if you correct it you get a exception because the record is already added to the context and it wants to add it again because isnew is still true.

Also how do i get validation on the combobox of categories because you can save a book without a category being required ? You cant put a [required] attribute on the Category in the book class so how do you force it to be required ?

Angelo

Posted on 6/30/2010 @ 2:19 AM
Hi.
I was using RIAEssentials and I think there's a problem with Authentication.ValidatePassword. I Had to extract the code from the framework because my old db conversion app was built with .NET FW 3.5. The issue is:

ORIGINAL CODE:
private static bool ValidatePassword(string password, string storedPasswordHash)
{
if (String.IsNullOrEmpty(password))
{
throw new ArgumentNullException("password");
}
if (String.IsNullOrEmpty(storedPasswordHash))
{
throw new ArgumentNullException("storedPasswordHash");
}

// Extract the random password salt that was generated.
string passwordSalt = storedPasswordHash.Substring(storedPasswordHash.Length - PasswordSaltSize);

// Now take the password that is to be validated and hash it with the same salt,
// and compare the two hashes.
string passwordHash = HashPassword(password, passwordSalt);

return (String.CompareOrdinal(passwordHash, storedPasswordHash) == 0);
}

MODIFIED (and working for me):
private static bool ValidatePassword(string password, string storedPasswordHash)
{
if (String.IsNullOrEmpty(password))
{
throw new ArgumentNullException("password");
}
if (String.IsNullOrEmpty(storedPasswordHash))
{
throw new ArgumentNullException("storedPasswordHash");
}

// Extract the random password salt that was generated.
string passwordSalt = storedPasswordHash.Substring(storedPasswordHash.Length - (PasswordSaltSize + 4)); <- MODIFIED HERE!!

// Now take the password that is to be validated and hash it with the same salt,
// and compare the two hashes.
string passwordHash = HashPassword(password, passwordSalt);

return (String.CompareOrdinal(passwordHash, storedPasswordHash) == 0);
}

The passwordSalt is long 12 character while PasswordSaltSize is 8.

Björn

Posted on 6/30/2010 @ 3:17 AM
1) solution with Application_AuthenticateRequest:
Thank you for the proposed solution with Application_AuthenticateRequest(...).
But it's not running, because GetUser needs the DomainServiceContext instead of the HttpContext.
What to do?

2) solution with storing roles into auth-cookie
I tried an other solution: append the user roles into the ticket.UserData
But it's not working with [RequiresRole("Admin")]
You have an idea?

I made changes in four places:
- AuthenticationService.ValidateCredentials(...)
userData = member.MemberName + ":" + member.MemberID + ":" + BookClubGuid;

// add roles into userData
if (user.Roles != null)
{
var roleData = user.Roles.Aggregate("", (current, role) => current + (role + ";"));
userData += ":" + roleData;
}

- AuthenticationService.GetUser(string name, string userData)
// extract roles from userData into User instance
string[] roleDataParts = userDataParts[3].Split(';');
user.Roles = (from roleDataPart in roleDataParts where roleDataPart.Trim().Length > 0 select roleDataPart.Trim()).ToList();

- FormsAuthenticationService.GetUser():
// set CurrentPrincipal with the roles
var roles = (from role in user.Roles select role).ToArray();
Thread.CurrentPrincipal = new GenericPrincipal(currentUser.Identity, roles);

- check for role in public IQueryable<BookInfo> SearchBooks(string searchText):
a) [RequiresRole("Admin")] <== is not working!!!
b) if (Thread.CurrentPrincipal.IsInRole("Admin")) <== can be used
c) if (_user.IsInRole("Admin")) <== can be used

Regards
Björn.

Nikhil Kothari

Posted on 6/30/2010 @ 3:49 AM
@Angelo
You're right - I didn't account for base64 encoding which expands the 8-byte binary hash into a 12 character string.
I have checked in a fix. See http://riaservices.codeplex.com/SourceControl/changeset/changes/71742

@Björn
Can you look at the latest code? The roles support has been added, and MembersService now has [RequiresRole("Admin")]. Hope that will help you.

In terms of your specific point about GetUser needing a DomainServiceContext - there is a way to construct one given an HttpContext (the checked in code has that bit - its a few lines of code, so not conducive for putting into a comment).

Arun

Posted on 7/8/2010 @ 12:09 AM
I'm using your custom authentication technique. It always return user as null in domain service after logged in but login was successful (In client side i am able to access user's properties) . I'm using domaindatasource control for operations. I spent last two days to figure out the issue, but I can't find the solution. Please help me.

Nikhil Kothari

Posted on 7/11/2010 @ 8:22 PM
@Arun
Are you using a custom DomainServiceFactory to get the current user and passing it into the domain service like the sample does?

asd

Posted on 9/1/2010 @ 6:27 PM
Incredible detailed work. It looks like you want the Web to be part of the Enterprise stack and more 'serious'.
Björn
Posted on 6/28/2010 @ 3:26 AM
What are the steps to support also user-roles?
I want to restrict the access to search books only for 'Admin' (just as a test).

I added following:
1. AuthenticationService.ValidateCredentials(...):
user = new User()
{
...
// add 'Admin' as role (HACK: just for to test - normaly it will be loaded from the db)
Roles = new List<string> { "Admin" }
};

2. Consume the Roles on Client-Side:
BookClubModel.SearchBooks(...) {
var roles = WebContext.Current.User.Roles;

=> when I logged in the user has the 'Admin'-Role

3. Define 'RequiresRole' for BookClubService.SearchBooks:
[RequiresRole("Admin")]
public IQueryable<BookInfo> SearchBooks(string searchText)

=> there is always the access denied message also if I'm logged in
result: There was an error loading the requested books. Load operation failed to query 'SearchBooks'. Access to operation 'SearchBooks' was denied.

What's missing?
- the BookClubService user-field has no rules! When and where do we have to load this information? Or should this be saved in the cookie?
- which solution you propose to implement?

Björn
Nikhil Kothari
Posted on 6/28/2010 @ 7:46 AM
@Björn
When ASP.NET forms authentication recreates a principal on subsequent requests, it doesn't know anything about roles. If you're using roles, you need to handle the event raised when a principal is created, and add in the role information (believe me, I wish forms authentication did).

It is something I wanted to account for in RIAEssentials, but another asp.net limitation pops up - there is no convenient hook point for the FormsAuthenticationService to do this... so for now you have to write code along these lines in Global.asax.cs:

protected void Application_AuthenticateRequest(object sender, EventArgs e) {
HttpContext context = HttpContext.Current;
User currentUser = Authentication.GetUser<User, AuthenticationService>(context);
if (currentUser != null) {
context.User = new GenericPrincipal(context.User.Identity, currentUser.Roles);
}
}

Hope this helps... note, that I wrote the above code while writing the comment - so it might need tweaking, and there might even be something better to do.

The above code is pretty standard - lots of blog posts on this, though I am not happy, as its ideally something that just takes care of itself. I'll see if I can provide something in the RIAEssentials framework to automatically do this, but like I said, there are some known asp.net limitations that prevent the simple/right solution from being implemented.
Steve
Posted on 6/28/2010 @ 6:23 PM
Hi there seems to be an issue with validation. I grabbed the latest from codeplex. When you add a new book if you put just 4 numbers in the ASIN field you can add the the record to the data context with the OK button and the error that the field needs to be 10 digits does not appear. If you then click save you get the error and if you correct it you get a exception because the record is already added to the context and it wants to add it again because isnew is still true.

Also how do i get validation on the combobox of categories because you can save a book without a category being required ? You cant put a [required] attribute on the Category in the book class so how do you force it to be required ?
Angelo
Posted on 6/30/2010 @ 2:19 AM
Hi.
I was using RIAEssentials and I think there's a problem with Authentication.ValidatePassword. I Had to extract the code from the framework because my old db conversion app was built with .NET FW 3.5. The issue is:

ORIGINAL CODE:
private static bool ValidatePassword(string password, string storedPasswordHash)
{
if (String.IsNullOrEmpty(password))
{
throw new ArgumentNullException("password");
}
if (String.IsNullOrEmpty(storedPasswordHash))
{
throw new ArgumentNullException("storedPasswordHash");
}

// Extract the random password salt that was generated.
string passwordSalt = storedPasswordHash.Substring(storedPasswordHash.Length - PasswordSaltSize);

// Now take the password that is to be validated and hash it with the same salt,
// and compare the two hashes.
string passwordHash = HashPassword(password, passwordSalt);

return (String.CompareOrdinal(passwordHash, storedPasswordHash) == 0);
}

MODIFIED (and working for me):
private static bool ValidatePassword(string password, string storedPasswordHash)
{
if (String.IsNullOrEmpty(password))
{
throw new ArgumentNullException("password");
}
if (String.IsNullOrEmpty(storedPasswordHash))
{
throw new ArgumentNullException("storedPasswordHash");
}

// Extract the random password salt that was generated.
string passwordSalt = storedPasswordHash.Substring(storedPasswordHash.Length - (PasswordSaltSize + 4)); <- MODIFIED HERE!!

// Now take the password that is to be validated and hash it with the same salt,
// and compare the two hashes.
string passwordHash = HashPassword(password, passwordSalt);

return (String.CompareOrdinal(passwordHash, storedPasswordHash) == 0);
}

The passwordSalt is long 12 character while PasswordSaltSize is 8.
Björn
Posted on 6/30/2010 @ 3:17 AM
1) solution with Application_AuthenticateRequest:
Thank you for the proposed solution with Application_AuthenticateRequest(...).
But it's not running, because GetUser needs the DomainServiceContext instead of the HttpContext.
What to do?

2) solution with storing roles into auth-cookie
I tried an other solution: append the user roles into the ticket.UserData
But it's not working with [RequiresRole("Admin")]
You have an idea?

I made changes in four places:
- AuthenticationService.ValidateCredentials(...)
userData = member.MemberName + ":" + member.MemberID + ":" + BookClubGuid;

// add roles into userData
if (user.Roles != null)
{
var roleData = user.Roles.Aggregate("", (current, role) => current + (role + ";"));
userData += ":" + roleData;
}

- AuthenticationService.GetUser(string name, string userData)
// extract roles from userData into User instance
string[] roleDataParts = userDataParts[3].Split(';');
user.Roles = (from roleDataPart in roleDataParts where roleDataPart.Trim().Length > 0 select roleDataPart.Trim()).ToList();

- FormsAuthenticationService.GetUser():
// set CurrentPrincipal with the roles
var roles = (from role in user.Roles select role).ToArray();
Thread.CurrentPrincipal = new GenericPrincipal(currentUser.Identity, roles);

- check for role in public IQueryable<BookInfo> SearchBooks(string searchText):
a) [RequiresRole("Admin")] <== is not working!!!
b) if (Thread.CurrentPrincipal.IsInRole("Admin")) <== can be used
c) if (_user.IsInRole("Admin")) <== can be used

Regards
Björn.
Nikhil Kothari
Posted on 6/30/2010 @ 3:49 AM
@Angelo
You're right - I didn't account for base64 encoding which expands the 8-byte binary hash into a 12 character string.
I have checked in a fix. See http://riaservices.codeplex.com/SourceControl/changeset/changes/71742

@Björn
Can you look at the latest code? The roles support has been added, and MembersService now has [RequiresRole("Admin")]. Hope that will help you.

In terms of your specific point about GetUser needing a DomainServiceContext - there is a way to construct one given an HttpContext (the checked in code has that bit - its a few lines of code, so not conducive for putting into a comment).
Arun
Posted on 7/8/2010 @ 12:09 AM
I'm using your custom authentication technique. It always return user as null in domain service after logged in but login was successful (In client side i am able to access user's properties) . I'm using domaindatasource control for operations. I spent last two days to figure out the issue, but I can't find the solution. Please help me.
Nikhil Kothari
Posted on 7/11/2010 @ 8:22 PM
@Arun
Are you using a custom DomainServiceFactory to get the current user and passing it into the domain service like the sample does?

Hatem

Posted on 9/20/2010 @ 5:06 AM
Thank you for the help, I realy like your code.

but I have a problem with current User info.

what I want to do ..
set a user profile variable on the Client. like

WebContext.Current.User.FriendlyName = "this is the friendly name";

then if I get a user object on the server like:

User u = new Authentication().GetUser();

I can find the modified value u.FriendlyName.
I can't find the FriendlyName value anywhere on the server.

am i doing this right ?!!

thanks

Kumait

Posted on 5/29/2011 @ 10:45 PM
Hello;
Check my post on custom authentication implementation

http://techblog.alkumait.net/?p=77

Mark

Posted on 10/7/2011 @ 12:57 AM
Nikhil,

Hello!

I'm attempting to use the registration service from the RIA Services business template with the authentication service from the Book Club sample in a new Silverlight RIA Services application. Passing the user info to the client and getting the roles to populate is all working great. What's got me stumped is figuring out what algorithm the out of the box solution (business template) is using to hash the password. I've set the Membership provider config setting to SHA1 to match what was in the RIA Essentials Authentication class, but no joy. Also tried concatenating Password and PasswordSalt from the Membership table...no good either

Is there something simple I'm missing here? Maybe a configuration setting?.... or is the solution more along the lines of building a custom membership provider as you did in the Book Club example?

Help!

Mark

Gareth King

Posted on 11/16/2011 @ 4:47 AM
Hi Nikhil,

Your custom authentication for validating users work great. Thanks.

I wish to extend the custom authentication service for registering new users. Have you any guidance/sample code/documentation available?

Can you please help?

Thanks,
Gareth

Gareth King

Posted on 11/16/2011 @ 4:53 AM
Hi Nikhil,

Your custom authentication for validating users work great. Thanks.

I wish to extend the custom authentication service for registering new users. Have you any guidance/sample code/documentation available?

Can you please help?

Thanks,
Gareth

Gareth King

Posted on 11/16/2011 @ 5:07 AM
Hi Nikhil,

Your custom authentication for validating users work great. Thanks.

I wish to extend the custom authentication service for registering new users. Have you any guidance/sample code/documentation available?

Can you please help?

Thanks,
Gareth

Dilip

Posted on 2/2/2012 @ 8:11 PM
Hi Nikhil,
I tried to implement the custom authentication and authorization using your code. At some point during the startup of the application, it calls FormAuthenticationService.GetUser() and it gives me an error on the client, saying "Load operation failed for query 'GetUser'. Entity 'User : Null' cannot be added to cache because it doesn't have a valid identity."
I tried to debug the FormAuthenticationService.GetUser() on the server side and eventually I hit the following code in my service, where I see the exception:

public override void Initialize(DomainServiceContext context) {
base.Initialize(context);

// This allows authorization rules to get access to the current user
AuthorizationContext customAuthContext = new AuthorizationContext(context); // this throws an exception described below
customAuthContext.Items[typeof(User)] = _user;

AuthorizationContext = customAuthContext;
}
Exception: customAuthContext.Instance threw an exception of type System.InvalidOperationException.
Description: This AuthorizationContext instance is only a template and cannot be used directly.

In fact, when I ran the BookClub application and put a breakpoint on this line in the BookShelf service, I got exactly the same exception.

Can you please help me resolve this bug? Thanks.

-- Dilip

DP Evy

Posted on 2/3/2012 @ 2:45 AM
Hi Nikhil,

If need to add a lot of roles to the user.
But on this line in the Global.asax.cs
User currentUser = Authentication.GetUser<User, AuthenticationService>(context);

DP Evy

Posted on 2/3/2012 @ 2:45 AM
Hi Nikhil,

If need to add a lot of roles to the user.
But on this line in the Global.asax.cs
User currentUser = Authentication.GetUser<User, AuthenticationService>(context);

EvyDP

Posted on 2/3/2012 @ 3:06 AM
Hi Nikhil,

In my application the user can have a lot of roles. They can be even more then 100 roles for a user.

The login works.
But when a ask Authentication.GetUser<User, AuthenticationService>(context); in the CreateDomainService method of the DomainServiceFactory the user is allows empty.

I even tried it in the bookclub example project and this also fails when I give the user 100 roles.

Does anybody have a solution for this?

Thanks,

Evy
Post your comment and continue the discussion.