4

Background

Our web applications use external authentication, in a sense that users' usernames/passwords aren't validated locally, but are being validated "outside" the web app in a central, single-sign-on type website. The authentication proof (and user's identification) becomes available via local server variables (HTTP_EMPLOYEEID, etc.). However, it's not quite external like authenticating against Google, Facebook, or another OAuth based setup. So I just wanted to make that distinction, so it doesn't collide with terms "External Logins" in ASP.NET Identity / Owin.

Problem

I'm trying to figure out a clean way to leverage the authenticated user data (from server variables) and pass it over to ASP.NET authentication. However, the user profile and role data has to be looked up against a web service before the user can be logged into the app.

I want to use Owin and Claims-based identity, but I am not sure if I should also use ASP.NET Identity, as well, or just do a more "pure" implementation with claims. I like the idea of not reinventing the wheel, but I also don't want to force a square peg into a round hole (as the saying goes), if the way the user is identified and looked up from a web service does not fit a typical ASP.NET Identity usage.

For example, if I take a more purist approach, I could do something like:

// Get the current user's id
var userId = HttpContext.Current.Request.ServerVariables["HTTP_EMPLOYEEID"];

// Get profile and role data from a web service
MyUser user = MyUserService.GetUserById(userId);

// Create claims
var claims = new Claim[]
{
    new Claim(ClaimTypes.Name, user.Id),
    new Claim(ClaimTypes.Email, user.Email),
    new Claim(ClaimTypes.Role, user.Role), // there can be more roles, but you get the idea
    // etc.
};

// Establish identity and login
var identity = new ClaimsIdentity(claims, "CookieAuthentication");
HttpContext.Current.GetOwinContext().Authentication.SignIn(identity);

But I also know I could be using ASP.NET Identity (just without Entity Framework stuff), and just implement IUser, IUserStore, IRoleStore (and whatever else is minimally required), and use an existing, established framework from Microsoft for handling this. The argument would be that this is more in line with current standards, and could potentially be extended easier for other types of authentication (if, say, a local username/password, or Google/Facebook become other allowed authentication options eventually, in addition to the current, ServerVariables-based setup).

Any advice from people who've been down this path before? Should I treat the server variable injected data as custom middleware and leverage it via ASP.NET Identity, or just not worry about where to fit it in that world, and go in a more of a "purist" approach as described above?

p.s. I'm using ASP.NET 4.6.1, and not the new ASP.NET Core.

Jiveman
  • 1,022
  • 1
  • 13
  • 30

1 Answers1

3

I have similar saturation. I do not want to use entire ASP.Net Identity, because I need to authenticate user again our external authentication.

So I just use OWIN Claim Authentication which basically creates Authentication cookie with Claims; Similar to Form Authentication we used in the old days.

public class OwinAuthenticationService 
{
    private readonly HttpContextBase _context;
    private const string AuthenticationType = "ApplicationCookie";

    public OwinAuthenticationService(HttpContextBase context)
    {
        _context = context;
    }

    public void SignIn(User user)
    {
        IList<Claim> claims = new List<Claim>
        {
            new Claim(ClaimTypes.Sid, user.Id.ToString()),
            new Claim(ClaimTypes.Name, user.UserName),
            new Claim(ClaimTypes.GivenName, user.FirstName),
            new Claim(ClaimTypes.Surname, user.LastName),
        };

        foreach (Role role in user.Roles)
        {
            claims.Add(new Claim(ClaimTypes.Role, role.Name));
        }

        ClaimsIdentity identity = new ClaimsIdentity(claims, AuthenticationType);

        IOwinContext context = _context.Request.GetOwinContext();
        IAuthenticationManager authenticationManager = context.Authentication;

        authenticationManager.SignIn(identity);
    }

    public void SignOut()
    {
        IOwinContext context = _context.Request.GetOwinContext();
        IAuthenticationManager authenticationManager = context.Authentication;

        authenticationManager.SignOut(AuthenticationType);
    }
}

Startup.cs

Note: I have Angular using both MVC and Web API, so I return 404 message for REST instead of 404 Page.

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = "ApplicationCookie",
            LoginPath = new PathString("/Account/Login"),
            Provider = new CookieAuthenticationProvider
            {
                OnApplyRedirect = ctx =>
                {
                    if (!IsApiRequest(ctx.Request))
                    {
                        ctx.Response.Redirect(ctx.RedirectUri);
                    }
                }
            }
        });
    }

    private static bool IsApiRequest(IOwinRequest request)
    {
        string apiPath = VirtualPathUtility.ToAbsolute("~/api/");
        return request.Uri.LocalPath.ToLower().StartsWith(apiPath);
    }
}
Win
  • 61,100
  • 13
  • 102
  • 181
  • Thanks. Yeah, this is the direction I was thinking with the "pure" claims approach I described above. I was just curious that, since there is another user profile/role data lookup involved, that maybe ASP.NET Identity deserves some consideration, because it is, supposedly, very customizable. – Jiveman Jul 20 '16 at 19:57
  • Once you create claims, it will not need to access external source again until user logout or close the browser. In addition, default **AuthorizeAttribute** can also recognize it. – Win Jul 20 '16 at 20:08