123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- using System;
- using System.Collections.Generic;
- using System.Net.Http;
- using System.Security.Claims;
- using System.Security.Cryptography;
- using System.Threading.Tasks;
- using System.Web;
- using System.Web.Http;
- using System.Web.Http.ModelBinding;
- using Microsoft.AspNet.Identity;
- using Microsoft.AspNet.Identity.EntityFramework;
- using Microsoft.AspNet.Identity.Owin;
- using Microsoft.Owin.Security;
- using Microsoft.Owin.Security.Cookies;
- using Microsoft.Owin.Security.OAuth;
- using WebApplication.Models;
- using WebApplication.Providers;
- using WebApplication.Results;
- namespace WebApplication.Controllers
- {
- [Authorize]
- [RoutePrefix("api/Account")]
- public class AccountController : ApiController
- {
- private const string LocalLoginProvider = "Local";
- private ApplicationUserManager _userManager;
- public AccountController()
- {
- }
- public AccountController(ApplicationUserManager userManager,
- ISecureDataFormat<AuthenticationTicket> accessTokenFormat)
- {
- UserManager = userManager;
- AccessTokenFormat = accessTokenFormat;
- }
- public ApplicationUserManager UserManager
- {
- get
- {
- return _userManager ?? Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
- }
- private set
- {
- _userManager = value;
- }
- }
- 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
- {
- Email = 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,
- Email = 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);
-
- if (!result.Succeeded)
- {
- return GetErrorResult(result);
- }
- 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);
- if (!result.Succeeded)
- {
- return GetErrorResult(result);
- }
- 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));
- if (!result.Succeeded)
- {
- return GetErrorResult(result);
- }
- 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));
- }
- if (!result.Succeeded)
- {
- return GetErrorResult(result);
- }
- 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);
- }
- ApplicationUser user = await UserManager.FindAsync(new UserLoginInfo(externalLogin.LoginProvider,
- externalLogin.ProviderKey));
- bool hasRegistered = user != null;
- if (hasRegistered)
- {
- Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
-
- ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager,
- OAuthDefaults.AuthenticationType);
- ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager,
- 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);
- }
- var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
- IdentityResult result = await UserManager.CreateAsync(user, model.Password);
- if (!result.Succeeded)
- {
- return GetErrorResult(result);
- }
- 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);
- }
- var info = await Authentication.GetExternalLoginInfoAsync();
- if (info == null)
- {
- return InternalServerError();
- }
- var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
- IdentityResult result = await UserManager.CreateAsync(user);
- if (!result.Succeeded)
- {
- return GetErrorResult(result);
- }
- result = await UserManager.AddLoginAsync(user.Id, info.Login);
- if (!result.Succeeded)
- {
- return GetErrorResult(result);
- }
- 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
- }
- }
|