Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

System.Web.Http.Owin WebApi2 installation woes

One of the annoying things I have found with .net and NUGet is that I never know what version of anything is installed by default. This can be really frustrating when trying to add things to an existing project.....

Here is my current dilemma.

I have an MVC 5 project with only MVC on it. This project is massive and the work to move it to another project would take too much time. So, I opened up NUGet and type WebApi and installed the one that came along.

Then I created a blank WebApi project with Individual Accounts set up and copied the StartUp code into my current StartUp along with any other configuration that is needed.

Then I came to create my AccountController which is just copied straight from the clean project I created. It looks like this:

[Authorize]
[RoutePrefix("api/Account")]
public class AccountController : ApiController
{
    private const string LocalLoginProvider = "Local";

    public AccountController()
        : this(Startup.UserManagerFactory(), Startup.OAuthOptions.AccessTokenFormat)
    {
    }

    public AccountController(UserManager<IdentityUser> userManager,
        ISecureDataFormat<AuthenticationTicket> accessTokenFormat)
    {
        UserManager = userManager;
        AccessTokenFormat = accessTokenFormat;
    }

    public UserManager<IdentityUser> UserManager { get; private set; }
    public ISecureDataFormat<AuthenticationTicket> AccessTokenFormat { get; private set; }

    // GET api/Account/UserInfo
    [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
    [Route("UserInfo")]
    public UserInfoViewModel GetUserInfo()
    {
        ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);

        return new UserInfoViewModel
        {
            UserName = User.Identity.GetUserName(),
            HasRegistered = externalLogin == null,
            LoginProvider = externalLogin != null ? externalLogin.LoginProvider : null
        };
    }

    // POST api/Account/Logout
    [Route("Logout")]
    public IHttpActionResult Logout()
    {
        Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
        return Ok();
    }

    // GET api/Account/ManageInfo?returnUrl=%2F&generateState=true
    [Route("ManageInfo")]
    public async Task<ManageInfoViewModel> GetManageInfo(string returnUrl, bool generateState = false)
    {
        IdentityUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId());

        if (user == null)
        {
            return null;
        }

        List<UserLoginInfoViewModel> logins = new List<UserLoginInfoViewModel>();

        foreach (IdentityUserLogin linkedAccount in user.Logins)
        {
            logins.Add(new UserLoginInfoViewModel
            {
                LoginProvider = linkedAccount.LoginProvider,
                ProviderKey = linkedAccount.ProviderKey
            });
        }

        if (user.PasswordHash != null)
        {
            logins.Add(new UserLoginInfoViewModel
            {
                LoginProvider = LocalLoginProvider,
                ProviderKey = user.UserName,
            });
        }

        return new ManageInfoViewModel
        {
            LocalLoginProvider = LocalLoginProvider,
            UserName = user.UserName,
            Logins = logins,
            ExternalLoginProviders = GetExternalLogins(returnUrl, generateState)
        };
    }

    // POST api/Account/ChangePassword
    [Route("ChangePassword")]
    public async Task<IHttpActionResult> ChangePassword(ChangePasswordBindingModel model)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword,
            model.NewPassword);
        IHttpActionResult errorResult = GetErrorResult(result);

        if (errorResult != null)
        {
            return errorResult;
        }

        return Ok();
    }

    // POST api/Account/SetPassword
    [Route("SetPassword")]
    public async Task<IHttpActionResult> SetPassword(SetPasswordBindingModel model)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
        IHttpActionResult errorResult = GetErrorResult(result);

        if (errorResult != null)
        {
            return errorResult;
        }

        return Ok();
    }

    // POST api/Account/AddExternalLogin
    [Route("AddExternalLogin")]
    public async Task<IHttpActionResult> AddExternalLogin(AddExternalLoginBindingModel model)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);

        AuthenticationTicket ticket = AccessTokenFormat.Unprotect(model.ExternalAccessToken);

        if (ticket == null || ticket.Identity == null || (ticket.Properties != null
            && ticket.Properties.ExpiresUtc.HasValue
            && ticket.Properties.ExpiresUtc.Value < DateTimeOffset.UtcNow))
        {
            return BadRequest("External login failure.");
        }

        ExternalLoginData externalData = ExternalLoginData.FromIdentity(ticket.Identity);

        if (externalData == null)
        {
            return BadRequest("The external login is already associated with an account.");
        }

        IdentityResult result = await UserManager.AddLoginAsync(User.Identity.GetUserId(),
            new UserLoginInfo(externalData.LoginProvider, externalData.ProviderKey));

        IHttpActionResult errorResult = GetErrorResult(result);

        if (errorResult != null)
        {
            return errorResult;
        }

        return Ok();
    }

    // POST api/Account/RemoveLogin
    [Route("RemoveLogin")]
    public async Task<IHttpActionResult> RemoveLogin(RemoveLoginBindingModel model)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        IdentityResult result;

        if (model.LoginProvider == LocalLoginProvider)
        {
            result = await UserManager.RemovePasswordAsync(User.Identity.GetUserId());
        }
        else
        {
            result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(),
                new UserLoginInfo(model.LoginProvider, model.ProviderKey));
        }

        IHttpActionResult errorResult = GetErrorResult(result);

        if (errorResult != null)
        {
            return errorResult;
        }

        return Ok();
    }

    // GET api/Account/ExternalLogin
    [OverrideAuthentication]
    [HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]
    [AllowAnonymous]
    [Route("ExternalLogin", Name = "ExternalLogin")]
    public async Task<IHttpActionResult> GetExternalLogin(string provider, string error = null)
    {
        if (error != null)
        {
            return Redirect(Url.Content("~/") + "#error=" + Uri.EscapeDataString(error));
        }

        if (!User.Identity.IsAuthenticated)
        {
            return new ChallengeResult(provider, this);
        }

        ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);

        if (externalLogin == null)
        {
            return InternalServerError();
        }

        if (externalLogin.LoginProvider != provider)
        {
            Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
            return new ChallengeResult(provider, this);
        }

        IdentityUser user = await UserManager.FindAsync(new UserLoginInfo(externalLogin.LoginProvider,
            externalLogin.ProviderKey));

        bool hasRegistered = user != null;

        if (hasRegistered)
        {
            Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
            ClaimsIdentity oAuthIdentity = await UserManager.CreateIdentityAsync(user,
                OAuthDefaults.AuthenticationType);
            ClaimsIdentity cookieIdentity = await UserManager.CreateIdentityAsync(user,
                CookieAuthenticationDefaults.AuthenticationType);
            AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
            Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
        }
        else
        {
            IEnumerable<Claim> claims = externalLogin.GetClaims();
            ClaimsIdentity identity = new ClaimsIdentity(claims, OAuthDefaults.AuthenticationType);
            Authentication.SignIn(identity);
        }

        return Ok();
    }

    // GET api/Account/ExternalLogins?returnUrl=%2F&generateState=true
    [AllowAnonymous]
    [Route("ExternalLogins")]
    public IEnumerable<ExternalLoginViewModel> GetExternalLogins(string returnUrl, bool generateState = false)
    {
        IEnumerable<AuthenticationDescription> descriptions = Authentication.GetExternalAuthenticationTypes();
        List<ExternalLoginViewModel> logins = new List<ExternalLoginViewModel>();

        string state;

        if (generateState)
        {
            const int strengthInBits = 256;
            state = RandomOAuthStateGenerator.Generate(strengthInBits);
        }
        else
        {
            state = null;
        }

        foreach (AuthenticationDescription description in descriptions)
        {
            ExternalLoginViewModel login = new ExternalLoginViewModel
            {
                Name = description.Caption,
                Url = Url.Route("ExternalLogin", new
                {
                    provider = description.AuthenticationType,
                    response_type = "token",
                    client_id = Startup.PublicClientId,
                    redirect_uri = new Uri(Request.RequestUri, returnUrl).AbsoluteUri,
                    state = state
                }),
                State = state
            };
            logins.Add(login);
        }

        return logins;
    }

    // POST api/Account/Register
    [AllowAnonymous]
    [Route("Register")]
    public async Task<IHttpActionResult> Register(RegisterBindingModel model)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        IdentityUser user = new IdentityUser
        {
            UserName = model.UserName
        };

        IdentityResult result = await UserManager.CreateAsync(user, model.Password);
        IHttpActionResult errorResult = GetErrorResult(result);

        if (errorResult != null)
        {
            return errorResult;
        }

        return Ok();
    }

    // POST api/Account/RegisterExternal
    [OverrideAuthentication]
    [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
    [Route("RegisterExternal")]
    public async Task<IHttpActionResult> RegisterExternal(RegisterExternalBindingModel model)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);

        if (externalLogin == null)
        {
            return InternalServerError();
        }

        IdentityUser user = new IdentityUser
        {
            UserName = model.UserName
        };
        user.Logins.Add(new IdentityUserLogin
        {
            LoginProvider = externalLogin.LoginProvider,
            ProviderKey = externalLogin.ProviderKey
        });
        IdentityResult result = await UserManager.CreateAsync(user);
        IHttpActionResult errorResult = GetErrorResult(result);

        if (errorResult != null)
        {
            return errorResult;
        }

        return Ok();
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            UserManager.Dispose();
        }

        base.Dispose(disposing);
    }

    #region Helpers

    private IAuthenticationManager Authentication
    {
        get { return Request.GetOwinContext().Authentication; }
    }

    private IHttpActionResult GetErrorResult(IdentityResult result)
    {
        if (result == null)
        {
            return InternalServerError();
        }

        if (!result.Succeeded)
        {
            if (result.Errors != null)
            {
                foreach (string error in result.Errors)
                {
                    ModelState.AddModelError("", error);
                }
            }

            if (ModelState.IsValid)
            {
                // No ModelState errors are available to send, so just return an empty BadRequest.
                return BadRequest();
            }

            return BadRequest(ModelState);
        }

        return null;
    }

    private class ExternalLoginData
    {
        public string LoginProvider { get; set; }
        public string ProviderKey { get; set; }
        public string UserName { get; set; }

        public IList<Claim> GetClaims()
        {
            IList<Claim> claims = new List<Claim>();
            claims.Add(new Claim(ClaimTypes.NameIdentifier, ProviderKey, null, LoginProvider));

            if (UserName != null)
            {
                claims.Add(new Claim(ClaimTypes.Name, UserName, null, LoginProvider));
            }

            return claims;
        }

        public static ExternalLoginData FromIdentity(ClaimsIdentity identity)
        {
            if (identity == null)
            {
                return null;
            }

            Claim providerKeyClaim = identity.FindFirst(ClaimTypes.NameIdentifier);

            if (providerKeyClaim == null || String.IsNullOrEmpty(providerKeyClaim.Issuer)
                || String.IsNullOrEmpty(providerKeyClaim.Value))
            {
                return null;
            }

            if (providerKeyClaim.Issuer == ClaimsIdentity.DefaultIssuer)
            {
                return null;
            }

            return new ExternalLoginData
            {
                LoginProvider = providerKeyClaim.Issuer,
                ProviderKey = providerKeyClaim.Value,
                UserName = identity.FindFirstValue(ClaimTypes.Name)
            };
        }
    }

    private static class RandomOAuthStateGenerator
    {
        private static RandomNumberGenerator _random = new RNGCryptoServiceProvider();

        public static string Generate(int strengthInBits)
        {
            const int bitsPerByte = 8;

            if (strengthInBits % bitsPerByte != 0)
            {
                throw new ArgumentException("strengthInBits must be evenly divisible by 8.", "strengthInBits");
            }

            int strengthInBytes = strengthInBits / bitsPerByte;

            byte[] data = new byte[strengthInBytes];
            _random.GetBytes(data);
            return HttpServerUtility.UrlTokenEncode(data);
        }
    }

    #endregion
}

Now, we can see that because it is using RoutePrefix that his is definitely version 2+ of WebApi. My problem is that code does not compile. It states:

The type or namespace name 'HostAuthenticationAttribute' could not be found (are you missing a using directive or an assembly reference?)

Looking at my clean project, I can see that this class resides in System.Web.Http.Owin. The problem is, I don't have that reference in my main project and I have no idea how to install it.

I have tried installing all the other different versions of the WebApi to no avail.

Surely someone has had this issue before?

like image 716
r3plica Avatar asked Feb 20 '14 11:02

r3plica


1 Answers

I experienced the same error when trying to upgrade my WebAPI project to version 2.0. Installing the nuget package Microsoft.AspNet.WebApi.Owin resolved the missing reference.

Install-Package Microsoft.AspNet.WebApi.Owin
like image 126
votive Avatar answered Nov 10 '22 13:11

votive