Logging in???

Jun 11, 2013 at 1:30 AM
Markus,
I know this has to be simple, but I have been pulling my hair out trying to get it to work.

I wanted to create a login request to the BE, have the BE attempt to log the user in, and, if successful, return the login credentials, etc.

My actual classes were much more involved, but when I couldn't get them to work, I started trying all kinds of things. If I remove all references to the Identity, I can get all the other code to work, but then I can't assign the principal to Thread.CurrentPrincipal on the client side as it complains that I don't have a true Identity. The easy solution is to pass what I need back as other fields and build the principal on the client.

But I want to know why this doesn't work so I can avoid wasting tons of time in the future when I run into another situation where I can't get an object to serialize properly.

I took the default CODEFrameworkPrincipal and CODEFrameworkUser classes and added the DataContract/DataMember decorators as appropriate. I then ran a test instantiating it as an object and it worked find.

In my response object, I have a property that contains an object reference to the CODEFrameworkPrincipal. When I try to return it, the FW crashes. (This is related to my other discussion "How to catch missing required fields without crashing? " - as the fields were empty, not null.)

My attempts (even in the host harness UI) resulted in the following errors (1=error, 2&3= inner exceptions) near line 229 in ServiceTestHarnessUI.xaml.cs :
1) "Exception has been thrown by the target of an invocation." - System.Exception {System.Reflection.TargetInvocationException}
2) "The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '10675199.02:48:05.4775807'." - System.Exception {System.ServiceModel.CommunicationException}
3) "An existing connection was forcibly closed by the remote host" - System.Exception {System.Net.Sockets.SocketException}

I did end up wrapping the invoke in a try/catch that shows when I am in dev mode.

No matter what I try, it will not work unless I remove the Identity. But then, I can't pass that back. Since the classes are based on interfaces, not other classes, I can't figure out why it has so much trouble serializing it.

I figure I can work around this by sending back enough info to create a client side principal. But I am more concerned with what is causing the problem in the first place so I don't have related problems in the future.

Here are the principal/user classes (they should look familiar):
[DataContract]
public class CODEFrameworkUser : IIdentity
{
        
    public CODEFrameworkUser(string username)
    {
        Name = username;
        AuthenticationType = "Custom";
        IsAuthenticated = true;
    }
        
    [DataMember(IsRequired = true)]
    public string Name
    {
        get;
        private set;
    }
    [DataMember(IsRequired = true)]
    public string AuthenticationType
    {
        get;
        private set;
    }
    [DataMember(IsRequired = true)]
    public bool IsAuthenticated
    {
        get;
        private set;
    }
}

[DataContract]
public class CODEFrameworkPrincipal : IPrincipal
{
    public CODEFrameworkPrincipal(CODEFrameworkUser user)
    {
        Identity = user;
    }
    public bool IsInRole(string role)
    {
        return true;
    }
    [DataMember(IsRequired = true)]
    public IIdentity Identity
    {
        get;
        private set;
    }
}
Here is the response class (it inherits a generic response that has the success/failure/etc. properties
[DataContract]
public class UserLoginResponse2 : GenericResponse
{
    public UserLoginResponse2()
    {
        userPrincipal = new CODEFrameworkPrincipal(new CODEFrameworkUser(""));
    }
    [DataMember(IsRequired = true)]
    public CODEFrameworkPrincipal userPrincipal
    {
        get;
        set;
    }
}
And finally, here is the UserService that tries to return it:
public UserLoginResponse2 Login2(UserLoginRequest userLoginRequest)
{
    CODEFrameworkPrincipal myPrincipal;
    CODEFrameworkUser myUser = new CODEFrameworkUser("Foobar");
    UserLoginResponse2 userResp = new UserLoginResponse2();

    myPrincipal = new CODEFrameworkPrincipal(myUser);

    userResp.Success = true;
    userResp.FailureInformation = "";
    userResp.userPrincipal = myPrincipal;

    return userResp;
}
Note, the last one really looks silly, but it's enough to cause the crash. Also there are some places where the code is probably redundant, but then, there was much more code before I deleted it all and left you with the smallest footprint.

I am sure this is really simple, but I just can't figure it out.

Thanks,

Fletcher
Coordinator
Jun 17, 2013 at 11:50 AM
OK, let me tackle this one :-).

First, let's discuss the overall setup. You want to make sure you keep things separated, to keep everything simple. That is one of the great advantages of CODE Framework. So you keep UI stuff in the UI, logic in the services, and so on.

The principal object is purely a UI object, because it is specific to .NET UIs. Therefore, the principal should not travel back from the service, but it should instead be kept local in the UI. In fact, the default principal that gets created should be all that is needed.

On the service side, you need methods that can figure out whether the user is who they claim they are, what roles they are a member of, and so forth. All that service should do is return that information. That information should not be specific to being used by a principal. Services should have no concept of that sort of stuff, otherwise your service is not usable in environments that do not use the .NET specific principal implementation.

Take a look at the default LoginViewModel:
using System;
using CODE.Framework.Wpf.Mvvm;
using MagazineManagement.Models.Home;

namespace MagazineManagement.Models.User
{
    public class LoginViewModel : ViewModel
    {
        public LoginViewModel()
        {
            Actions.Add(new ViewAction("Login", execute: Login));
            Actions.Add(new ApplicationShutdownViewAction("Cancel"));
        }

        public string UserName { get; set; }
        public string Password { get; set; }

        private void Login(IViewAction action, object parameter)
        {
            // TODO: Perform actual user login here

            AppDomain.CurrentDomain.SetThreadPrincipal(new CODEFrameworkPrincipal(new CODEFrameworkUser(UserName)));

            Controller.CloseViewForModel(this);

            StartViewModel.Current.LoadActions();
        }
    }
}
This is where you perform your login operation. Where it has the TODO comment, just add your own service call that determines whether the user is valid. I usually have something like this:
ServiceClient.Call<IUserService>(s => {
    var response = s.GetUserName(new GetUserNameRequest{ UserName=UserName, Password=Password});
    if (response.UserIsValid)
    {
        AppDomain.CurrentDomain.SetThreadPrincipal(new CODEFrameworkPrincipal(new CODEFrameworkUser(response.UserName)));
        Controller.CloseViewForModel(this);
        StartViewModel.Current.LoadActions();
    }
    else
    {
        // Show some kind of error
    }
});
I typically also encrypt the user name and password, but you can add that yourself if you want. The point is that the service performs a simple call to see if the user name and password match and then return a success indicator as well as the real user name (I often allow the user name entered into the dialog to be an email or a phone number and so on... but then I internally determine a single user name I use behind the scenes... but that is just how I like to implement it).

The roles stuff works similar. When .NET checks for roles, it ultimately queries the IsInRole() method of the principal. Here's our default for that:
public bool IsInRole(string role)
{
    // TODO: implement this for real by detirmining which roles the user is in
    return false;
}
I usually change it into something like this:
private List<string> _roles;
public bool IsInRole(string role)
{
    if (_roles == null)
    {
        _roles = new List<string>();
        ServiceClient.Call<IUserService>(s => {
            var response = s.GetRolesForUser(new GetRolesForUserRequest{UserName = UserName});
            if (response.Success)
                foreach (var definedRole in response.Roles)
                    _roles.Add(definedRole);
        }
    }

    return _roles.Contains(role);
}
Done. That's all you really need there. No complex passing principals from and to services. It's really just a relatively straightforward matter of checking a few bits of data.


Markus
Jun 28, 2013 at 7:37 PM
Edited Jun 29, 2013 at 2:53 AM
Markus,

I agree. In my case, I now just send back a token that is used for future requests (so I know that the person is still authenticated).

My issue was much more along the lines of trying to figure out why it was broken. My guess is that I had tried to return an object that contained code in addition to data. What I know is that when I tried to send it back, the system crashed in transport (i.e. I could create it fine on the host side and/or client side, but when I tried to include it in the response, VS refused to let me send it.

I have the code working, I just wish I knew why it wouldn't get passed back without error so I avoid a similar headache down the road.

Thanks,

Fletcher
Coordinator
Jul 1, 2013 at 8:38 PM
Yeah, no code can be passed back and forth. Code is not allowed in services, since that would mean you are tied to a certain language or execution environment.

Markus