14

I may be over-complicating things, but we have an internal ASP.NET MVC5 SPA with AngularJS using Windows Authentication. This application has a SQL back-end database that has a table of users, containing their account names, and their respective roles in the application. We will be making calls to another Web API application that also has Windows Authentication enabled.

I have tried to do research on how to handle authorization using OWIN but couldn't find any specific examples regarding OWIN and Windows Authentication. Everything that turns up uses forms authentication with a username and password.

How can I go about using OWIN and Windows Auth for my app? Here's a sample of my OAuthAuthorizationServerProvider class.

public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        context.Validated();
        return;
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

        var container = UnityHelper.GetContainerInstance("***");
        var securityHelper = container.Resolve<ISecurityHelper>();

        User currentUser = securityHelper.GetCurrentUser(); // Validates user based on HttpContext.Current.User
        if (currentUser == null)
        {
            context.SetError("invalid_grant", "The user could not be found.");
            return;
        }

        var identity = new ClaimsIdentity(context.Options.AuthenticationType);
        identity.AddClaim(new Claim("sub", currentUser.AccountName));
        identity.AddClaim(new Claim("role", "user"));

        context.Validated(identity);
    }
}

UPDATE: Oops, I forgot to include more information on what we'd like to accomplish. If possible, we'd like to use bearer authentication tickets so we don't have to look up the user and their roles everytime we make a call to a web api method.

UPDATE 2: As requested by Andrew, below is the TLDR version of my _securityHelper class, specifically the GetCurrentUser() method. You'll notice that I'm attempting to call:

HttpContext.Current.GetOwinContext().Request.User.Identity.Name

This always returns null for User.

public class SecurityHelper : ISecurityHelper
{
    private readonly ISecurityGroupController _securityGroupController;
    private readonly IUserController _userController;
    private readonly IEmployeeController _employeeController;
    private readonly IFieldPermissionController _fieldPermissionController;
    private readonly IOACController _oacController;

    public SecurityHelper(ISecurityGroupController securityGroupController,
        IUserController userController,
        IEmployeeController employeeController,
        IFieldPermissionController fieldPermissionController,
        IOACController oacController)
    {
        _securityGroupController = securityGroupController;
        _userController = userController;
        _employeeController = employeeController;
        _fieldPermissionController = fieldPermissionController;
        _oacController = oacController;
    }

    // ... other methods

    public User GetCurrentUser()
    {
        User user = _userController.GetByAccountName(HttpContext.Current.GetOwinContext().Request.User.Identity.Name);
        if (user != null)
        {
            List<OAC> memberships = _oacController.GetMemberships(user.SourceId).ToList();
            if (IsTestModeEnabled() && ((user.OACMemberships != null && user.OACMemberships.Count == 0) || user.OACMemberships == null))
            {
                user.OACMemberships = memberships;
            }
            else if (!IsTestModeEnabled())
            {
                user.OACMemberships = memberships;
            }
        }

        return user;
    }
}
mmoreno79
  • 473
  • 2
  • 4
  • 10

2 Answers2

5

This article series would be a good place to start: http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/

of note, would be the following code, which essentially stores the bearer token in local storage and attaches it to the headers. There is obviously a lot more to it than this, including the forms and the actual server authentication system, but this should give you a decent start.

server component:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        ConfigureOAuth(app);
    //Rest of code is here;
    }

    public void ConfigureOAuth(IAppBuilder app)
    {
        OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
        {
            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
            Provider = new SimpleAuthorizationServerProvider()
        };

        // Token Generation
        app.UseOAuthAuthorizationServer(OAuthServerOptions);
        app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

    }
}

and the following client side code:

'use strict';
app.factory('authService', ['$http', '$q', 'localStorageService', function ($http, $q, localStorageService) {

    var serviceBase = 'http://ngauthenticationapi.azurewebsites.net/';
    var authServiceFactory = {};

    var _authentication = {
        isAuth: false,
        userName : ""
    };

    var _saveRegistration = function (registration) {

        _logOut();

        return $http.post(serviceBase + 'api/account/register', registration).then(function (response) {
            return response;
        });

    };

    var _login = function (loginData) {

        var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password;

        var deferred = $q.defer();

        $http.post(serviceBase + 'token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) {

            localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName });

            _authentication.isAuth = true;
            _authentication.userName = loginData.userName;

            deferred.resolve(response);

        }).error(function (err, status) {
            _logOut();
            deferred.reject(err);
        });

        return deferred.promise;

    };

    var _logOut = function () {

        localStorageService.remove('authorizationData');

        _authentication.isAuth = false;
        _authentication.userName = "";

    };

    var _fillAuthData = function () {

        var authData = localStorageService.get('authorizationData');
        if (authData)
        {
            _authentication.isAuth = true;
            _authentication.userName = authData.userName;
        }

    }

    authServiceFactory.saveRegistration = _saveRegistration;
    authServiceFactory.login = _login;
    authServiceFactory.logOut = _logOut;
    authServiceFactory.fillAuthData = _fillAuthData;
    authServiceFactory.authentication = _authentication;

    return authServiceFactory;
}]);

along with

'use strict';
app.factory('authInterceptorService', ['$q', '$location', 'localStorageService', function ($q, $location, localStorageService) {

    var authInterceptorServiceFactory = {};

    var _request = function (config) {

        config.headers = config.headers || {};

        var authData = localStorageService.get('authorizationData');
        if (authData) {
            config.headers.Authorization = 'Bearer ' + authData.token;
        }

        return config;
    }

    var _responseError = function (rejection) {
        if (rejection.status === 401) {
            $location.path('/login');
        }
        return $q.reject(rejection);
    }

    authInterceptorServiceFactory.request = _request;
    authInterceptorServiceFactory.responseError = _responseError;

    return authInterceptorServiceFactory;
}]);
Claies
  • 22,124
  • 4
  • 53
  • 77
  • Thanks Andrew. That's the very article series that started me down this path and would love to implement it. I have it all in place but when I try to get HttpContext.Current.User in my SimpleAuthorizationServerProvider.GrantResourceOwnerCredentials, it is null and I cannot get the principal. I've set the grant_type to "password" like the article recommends but I can't get the principal. – mmoreno79 Nov 07 '14 at 14:53
  • can you post the code from your `SecurityHelper` class? – Claies Nov 07 '14 at 22:28
  • After trying a few things out, it turns out that my implementation of the article series that Andrew originally suggested really was working. The "hurdle" I was trying to get passed was getting the User principal from HttpContext.Current.GetOwinContext().Request.User. This was returning **null** in my GetCurrentUser() method. Well, turns out, it was returning **null** because I was using Fiddler to compose my request and test the authentication out. On a whim, I simply created a test controller in my angular app to request a token and my User is now populated! – mmoreno79 Nov 08 '14 at 02:00
  • To further clarify, my credentials were not being passed using Fiddler's composer, which I believe is a completely different topic. – mmoreno79 Nov 08 '14 at 02:02
  • glad to hear that this was something you could identify the issue with :) – Claies Nov 08 '14 at 02:05
1

Check out this article for steps on enabling Windows Authentication in OWIN: http://www.asp.net/aspnet/overview/owin-and-katana/enabling-windows-authentication-in-katana

From the article:

Katana does not currently provide OWIN middleware for Windows Authentication, because this functionality is already available in the servers.

The linked article covers enabling Windows Authentication for development. For deployments, these settings are in IIS under Authentication. Users will be prompted for their username and password by the browser when they first arrive on your application's page.

davidmdem
  • 3,763
  • 2
  • 30
  • 33
  • Thanks david. I already have Anonymous Authentication disabled and Windows Authentication enabled in my project but was looking more for guidance on how to handle bearer tokens between my SPA (angular app) and the web api app. – mmoreno79 Nov 06 '14 at 22:01