0

I have a system that I use Asp.Net Identity to log users that have claims. Each user have different access to the System functionalities because I've created different Authorization filters.

My issue is this one: the system is for the management of car body work and painting. It has companies and the users log with the Asp.Net Identity technology. But I need to log special users that has no user name or password, these useras are the clients of the companies, that must log with License Plate of the cliente vehicle and some personal info like the Social Security Number.

What I've manageged to do until now: find if the client has a vehicle in one company and verify if the client data provided is correct. But the System fail in the SignInManager.SignInAsync() methrod. It gives me the error that the UserId was not found.

Here is my code:

if (!ModelState.IsValid)
{
    return View(model);
}
var serviceOrder = Repository.ServiceOrders
    .Where(so => so.IsActive && so.Vehicle.LicensePlate == model.LicensePlate &&
        so.Client.Document.Substring(0, 3) == model.Password)
    .OrderByDescending(so => so.CreatedAt).FirstOrDefault();
if (serviceOrder != null)
{
    var userId = Guid.NewGuid().ToString();
    var user = new ServerModels.User
    {
        Id = userId,
        Name = serviceOrder.Client.Name,
        UserName = model.LicensePlate,
        SecurityStamp = userId,
        Claims =
        {
            new IdentityUserClaim
            {
                UserId = userId,
                ClaimType = ClaimTypes.Role,
                ClaimValue = AppRoleClaims.Client
            },
            new IdentityUserClaim
            {
                UserId = userId,
                ClaimType = AppAccessClaims.ClientScreenAccess,
                ClaimValue = AppAccessClaimsValues.Visualize,   
            }
        }
    };
    try
    {
        await SignInManager.SignInAsync(user, model.RememberMe, false);
        return Redirect("/cliente/consultarveiculo");
    }
    catch (Exception ex)
    {
        ModelState.AddModelError("", "Tentativa de login inválida. - " + ex.Message);
        return View(model);
    }

And the error StackTrace:

em Microsoft.AspNet.Identity.UserManager`2.<GetSecurityStampAsync>d__42.MoveNext()
--- Fim do rastreamento de pilha do local anterior onde a exceção foi gerada ---
   em System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   em System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   em System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   em Microsoft.AspNet.Identity.TaskExtensions.CultureAwaiter`1.GetResult()
   em Microsoft.AspNet.Identity.ClaimsIdentityFactory`2.<CreateAsync>d__0.MoveNext()
--- Fim do rastreamento de pilha do local anterior onde a exceção foi gerada ---
   em System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   em System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   em System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   em SGOM.Server.Models.User.<GenerateUserIdentityAsync>d__20.MoveNext() na C:\Users\Adalto\Documents\Visual Studio 2015\Projects\MonitoraCar\SGOM\Server\Models\Models\User.cs:linha 25
--- Fim do rastreamento de pilha do local anterior onde a exceção foi gerada ---
   em System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   em System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   em System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   em Microsoft.AspNet.Identity.TaskExtensions.CultureAwaiter`1.GetResult()
   em Microsoft.AspNet.Identity.Owin.SignInManager`2.<SignInAsync>d__2.MoveNext()
--- Fim do rastreamento de pilha do local anterior onde a exceção foi gerada ---
   em System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   em System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   em System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   em SGOM.Client.Web.WebApp.Controllers.AccountController.<ClientLogin>d__13.MoveNext() na C:\Users\Adalto\Documents\Visual Studio 2015\Projects\MonitoraCar\SGOM\Client\Web\WebApp\Controllers\AccountController.cs:linha 195

Client login screen

7
  • is this a design question or a bug question? Commented Jan 25, 2017 at 12:09
  • I'm not sure I understand your question. There is no user, yet you want to login the user. OK, what is the purpose of that login? What are you going to do with this login? Why not create the user record in the db just for this client with this reg.plate? Commented Jan 25, 2017 at 12:11
  • It is a bug question Commented Jan 25, 2017 at 12:11
  • Each company has lots of users that are stored in the DB, I have a page for them to log in, these users have access to visualize data, change data, see reports and other stuff. But the access to the client is just for visualization: the client must see how his car is being repaired, the photos of the vehicle, the stages it has passed in the company. Commented Jan 25, 2017 at 12:14
  • If the ServerModels.User does not exist in the authentication DB, you can not log him in. Two solutions come to mind: - If it does not matter that the customer can see the work done on cars that are not his, add a single general user that will be shared by all customers for the purpose you outlined above. (Or allow anonymous access for the relevant actions.) - Else create the customer user somewhere in another workflow, e.g. when he originally requests the repair of the car. Commented Jan 25, 2017 at 13:30

1 Answer 1

0

Following the link that Sam Farajpour Guamari passed I came up to this solution:

public ActionResult ClientLogin(ClientLoginViewModel model)
{
        if (!ModelState.IsValid)
        {
            return View(model);
        }
        var serviceOrder = Repository.ServiceOrders
            .Where(so => so.IsActive && so.Vehicle.LicensePlate == model.LicensePlate &&
            so.Client.Document.Replace(".", "").Replace("-", "").Replace("/", "").Substring(0, 6) == model.Password)
            .OrderByDescending(so => so.CreatedAt).FirstOrDefault();
        if (serviceOrder != null)
        {
            try
            {
                var identity = new ClaimsIdentity(
                    new[]
                    {
                        // adding following 2 claims just for supporting default antiforgery provider
                        new Claim(ClaimTypes.NameIdentifier, "AnonymousUserID"),
                        new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string"),
                        new Claim(ClaimTypes.Name, serviceOrder.Client.Name),
                        new Claim(ClaimTypes.Role, AppRoleClaims.Client),
                        new Claim(AppAccessClaims.ClientScreenAccess, AppAccessClaimsValues.Visualize) 
                    },
                    DefaultAuthenticationTypes.ApplicationCookie);

                HttpContext.GetOwinContext().Authentication.SignIn(
                   new AuthenticationProperties { IsPersistent = model.RememberMe }, identity);
                return Redirect("/cliente/consultarveiculo");
            }
            catch (Exception ex)
            {
                ModelState.AddModelError("", "Tentativa de login inválida. - " + ex.Message);
                return View(model);
            }
        }
        ModelState.AddModelError("", "Tentativa de login inválida. - Nenhum cliente com a placa e a senha informada.");
        return View(model);
  }

That worked very well: create claims on the fly and use them to authenticate.

Link to the original post:

MVC 5 Identity (v2) Authentication Without Creating an Application User

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.