External Authentication ASP.NET Web API - asp.net

I have Web API and the client that calls the web API in separate solutions. I need to authenticate users using local authentication with existing user data and also authenticate with external authentication like Google and Facebook.
Local Authentication works fine. But while authenticating external logins, I'm getting unauthorized response when calling api/Account/UserInfo. Please note that I'm able to generate the token from google account correctly.
Here's my AccountController.cs file.
[Authorize]
[RoutePrefix("api/Account")]
[EnableCors("*","*","*")]
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)]
//[AllowAnonymous]
[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)]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[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,
FirstName = model.FirstName,
LastName = model.LastName,
CellNumber = model.CellNumber,
CompanyName = model.CompanyName,
Address = model.Address,
City = model.City,
Country = model.Country,
State = model.State,
Zip = model.Zip,
EmailConfirmed = true
};
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok("User Registered Successfully!");
}
// POST api/Account/RegisterExternal
[OverrideAuthentication]
//[AllowAnonymous]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("RegisterExternal")]
public async Task<IHttpActionResult> RegisterExternal()
{
var info = await Authentication.GetExternalLoginInfoAsync();
if (info == null)
{
return InternalServerError();
}
var user = new ApplicationUser() { UserName = info.Email, Email = info.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 != null)
{
_userManager.Dispose();
_userManager = null;
}
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
}
Here's the ConfigureAuth method in StartUp.Auth.cs file
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
var facebookoptions = new FacebookAuthenticationOptions()
{
AppId = "",
AppSecret = "",
BackchannelHttpHandler = new FaceBookBackChannelHandler(),
UserInformationEndpoint = "https://graph.facebook.com/v2.4/me?fields=email,first_name,last_name,gender"
};
facebookoptions.Scope.Add("email");
app.UseFacebookAuthentication(facebookoptions);
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = "",
ClientSecret = ""
});
}
Also here's the jQuery methods that call the API endpoints
function getAccessToken() {
if (location.hash) {
if (location.hash.split('access_token=')) {
var accessToken = location.hash.split('access_token=')[1].split('&')[0];
if (accessToken) {
isUserRegistered(accessToken);
}
}
}
}
function isUserRegistered(accessToken) {
$.ajax({
url: 'http://localhostXXXX/api/Account/UserInfo',
method: 'GET',
cors:
{
headers: {
'content-type': 'application/JSON',
'Authorization': 'Bearer ' + accessToken
}
},
success: function (response) {
if (response.HasRegistered) {
localStorage.setItem('accessToken', accessToken);
localStorage.setItem('userName', response.Email);
window.location.href = "Data.aspx";
}
else {
signupExternalUser(accessToken);
}
}
});
}
function signupExternalUser(accessToken) {
$.ajax({
url: 'http://localhost:XXXXX/api/Account/RegisterExternal',
method: 'POST',
cors:
{
headers: {
'content-type': 'application/json',
'Authorization': 'Bearer ' + accessToken
}
},
success: function () {
window.location.href = "";
}
});
}

Here is the code which i used for getting my informations. its work fine:
// check if authorized
if (Request["code"] == null)
{
Response.Redirect(string.Format(
"https://graph.facebook.com/oauth/authorize?client_id={0}&redirect_uri={1}&scope={2}",
app_id, Request.Url.AbsoluteUri, scope));
}
else
{
FacebookAccessToken token = new FacebookAccessToken();
FacebookUser user = new FacebookUser();
//Requesting for access token
string url = string.Format("https://graph.facebook.com/oauth/access_token?client_id={0}&redirect_uri={1}&scope={2}&code={3}&client_secret={4}",
app_id, Request.Url.AbsoluteUri, scope, Request["code"].ToString(), app_secret);
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(response.GetResponseStream());
string vals = reader.ReadToEnd();
// Deserialize json object
token = JsonConvert.DeserializeObject<FacebookAccessToken>(data);
}
//Getting user info
url = string.Format("https://graph.facebook.com/v2.8/me?access_token={0}", token.AccessToken);
request = WebRequest.Create(url) as HttpWebRequest;
using (var client = request.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(client.GetResponseStream());
string data = reader.ReadToEnd();
// data:"{\"name\":\"Er Vatsal D Patel\",\"id\":\"13168723650*****\"}";
user = JsonConvert.DeserializeObject<FacebookUser>(data);
}
return "done";
}

Related

Setting authorization header of Httpclient return internal server error 500 repone why?

I have a problem I don't know where , after setting authorization header of HttpClient return internal server error 500.
Auth Controller Login Action
namespace MagicVilla_Web.Controllers
{
public class AuthController : Controller
{
private readonly IAuthService _authService;
public AuthController(IAuthService authService)
{
_authService = authService;
}
[HttpGet]
public IActionResult Login()
{
LoginRequestDTO loginRequestDTO = new LoginRequestDTO();
return View(loginRequestDTO);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginRequestDTO loginRequestDTO)
{
APIResponse response = await _authService.LoginAsync<APIResponse>(loginRequestDTO);
if (response != null && response.IsSuccess)
{
LoginResponseDTO model = JsonConvert.DeserializeObject<LoginResponseDTO (Convert.ToString(response.Result));
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.Name, model.User.Name));
identity.AddClaim(new Claim(ClaimTypes.Role, model.User.Role));
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
HttpContext.Session.SetString(SD.SessionToken, model.Token);
return RedirectToAction("Index","Home");
}
else
{
ModelState.AddModelError("CustomError", response.ErrorMessages.FirstOrDefault());
return View(loginRequestDTO);
}
}
[HttpGet]
public IActionResult Register()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterationRequestDTO registerationRequestDTO)
{
APIResponse result = await _authService.RegisterAsync<APIResponse>(registerationRequestDTO);
if (result != null && result.IsSuccess)
{
return RedirectToAction("Login");
}
return View();
}
[HttpGet]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync();
HttpContext.Session.SetString(SD.SessionToken, "");
return RedirectToAction("Index", "Home");
}
[HttpGet]
public async Task<IActionResult> AccessDenied()
{
return View();
}
}
}
Home Controller Index
namespace MagicVilla_Web.Controllers
{
public class HomeController : Controller
{
private readonly IVillaService villaService;
private readonly IMapper mapper;
public HomeController(IVillaService villaService, IMapper mapper)
{
this.villaService = villaService;
this.mapper = mapper;
}
public async Task<IActionResult> Index()
{
List<VillaDTO> list = new();
var response = await villaService.GetAllAsync<APIResponse>(HttpContext.Session.GetString(SD.SessionToken));
if (response != null && response.IsSuccess)
{
list = JsonConvert.DeserializeObject<List<VillaDTO>>(Convert.ToString(response.Result));
}
return View(list);
}
}
}
GetAllAsync From VillaService.cs
public class VillaService : BaseService , IVillaService
{
private readonly IHttpClientFactory clientFactory;
private string villaUrl;
public VillaService(IHttpClientFactory clientFactory, IConfiguration configuration) : base(clientFactory)
{
this.clientFactory = clientFactory;
this.villaUrl = configuration.GetValue<string>("ServiceUrls:VillaAPI");
}
public Task<T> GetAllAsync<T>(string token)
{
return SendAsync<T>(new APIRequest
{
ApiType = SD.ApiType.GET,
Url = this.villaUrl + "/api/VillaAPI",
Token = token
});
}
}
Send Async From BaseService.cs
Here response return internal server error 500 after pass the token in the header Authrization & not going to API endpoint .
namespace MagicVilla_Web.Services
{
public class BaseService : IBaseServices
{
public APIResponse APIResponse { get; set; }
public IHttpClientFactory httpClient { get; set; }
public BaseService(IHttpClientFactory httpClient)
{
this.APIResponse = new();
this.httpClient = httpClient;
}
public async Task<T> SendAsync<T>(APIRequest apiRequest)
{
try
{
var client = httpClient.CreateClient("MagicAPI");
HttpRequestMessage request = new HttpRequestMessage();
request.Headers.Add("Accept", "application/json");
request.RequestUri = new Uri(apiRequest.Url);
if (apiRequest.Data != null)
{
request.Content = new StringContent(JsonConvert.SerializeObject(apiRequest.Data), Encoding.UTF8, "application/json");
}
switch (apiRequest.ApiType)
{
case SD.ApiType.POST:
request.Method = HttpMethod.Post;
break;
case SD.ApiType.PUT:
request.Method = HttpMethod.Put;
break;
case SD.ApiType.DELETE:
request.Method = HttpMethod.Delete;
break;
default:
request.Method = HttpMethod.Get;
break;
}
HttpResponseMessage response = null;
if (!string.IsNullOrEmpty(apiRequest.Token))
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiRequest.Token);
}
response = await client.SendAsync(request);
var apiContent = await response.Content.ReadAsStringAsync();
try
{
APIResponse apiResponse = JsonConvert.DeserializeObject<APIResponse>(apiContent);
if ((response.StatusCode == System.Net.HttpStatusCode.BadRequest || response.StatusCode == System.Net.HttpStatusCode.NotFound))
{
apiResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
apiResponse.IsSuccess = false;
var apiResponseSerialized = JsonConvert.SerializeObject(apiResponse);
var apiResponseDeserialized = JsonConvert.DeserializeObject<T>(apiResponseSerialized);
return apiResponseDeserialized;
}
}
catch (Exception e)
{
var exceptionResponse = JsonConvert.DeserializeObject<T>(apiContent);
return exceptionResponse;
}
var APIResponse = JsonConvert.DeserializeObject<T>(apiContent);
return APIResponse;
}
catch (Exception e)
{
var dto = new APIResponse
{
ErrorMessages = new List<string> { Convert.ToString(e.Message) },
IsSuccess = false
};
var res = JsonConvert.SerializeObject(dto);
var apiResponse = JsonConvert.DeserializeObject<T>(res);
return apiResponse;
}
}
}
}
VillaAPI Controller
[Route("api/[controller]")]
[ApiController]
public class VillaAPIController : ControllerBase
{
public readonly IVillaRepository villaRepository;
public readonly IMapper mapper;
protected APIResponse response;
public VillaAPIController(IVillaRepository villaRepository, IMapper mapper)
{
this.villaRepository = villaRepository;
this.mapper = mapper;
this.response = new();
}
[HttpGet]
[Authorize]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<IEnumerable<APIResponse>>> GetVillas()
{
try
{
IEnumerable<Villa> villaList = await villaRepository.GetAllAsync();
response.Result = mapper.Map<List<VillaDTO>>(villaList);
response.StatusCode = System.Net.HttpStatusCode.OK;
}
catch (Exception ex)
{
response.IsSuccess = false;
response.ErrorMessages = new List<string> { ex.Message };
}
return Ok(response);
}
}
Program.cs API
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddDbContext<ApplicationDbContext>(o => {
o.UseSqlServer(builder.Configuration.GetConnectionString("DefaultSQLConnection"));
});
builder.Services.AddAutoMapper(typeof(MappingConfig));
builder.Services.AddScoped<IVillaRepository, VillaRepository>();
builder.Services.AddScoped<IVillaNumberRepository, VillaNumberRepository>();
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddControllers(o => {
//o.ReturnHttpNotAcceptable = true;
}).AddNewtonsoftJson().AddXmlDataContractSerializerFormatters();
builder.Services.AddEndpointsApiExplorer();
//get secret key from appSittings.json
var key = builder.Configuration.GetValue<string>("ApiSettings:Secret");
//add Authentication Configurations
builder.Services.AddAuthentication(x =>
{
//configure the default authentication scheme and the default challenge scheme. Both of them we want to use JWT bearer defaults
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
//we have something called as add JWT bearer and then we can configure options on that.
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false; //we have the required https metadata
x.SaveToken = true; //save the token
x.Authority = "https://localhost:7003/";
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = false,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(key)),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = false,
};
});
//to enable bearer in swagger
builder.Services.AddSwaggerGen(options =>
{
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description =
"JWT Authorization header using the Bearer scheme. \r\n\r\n " +
"Enter 'Bearer' [space] and then your token in the text input below.\r\n\r\n" +
"Example: \"Bearer 12345abcdef\"",
Name = "Authorization",
In = ParameterLocation.Header,
Scheme = "Bearer"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header
},
new List<string>()
}
});
});
//app => configured application
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Program.cs Web
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddAutoMapper(typeof(MappingConfig));
builder.Services.AddHttpClient<IVillaService, VillaService>();
builder.Services.AddScoped<IVillaService, VillaService>();
builder.Services.AddHttpClient<IVillaNumberService, VillaNumberService>();
builder.Services.AddScoped<IVillaNumberService, VillaNumberService>();
builder.Services.AddHttpClient<IAuthService, AuthService>();
builder.Services.AddScoped<IAuthService, AuthService>();
builder.Services.AddDistributedMemoryCache();
IdentityModelEventSource.ShowPII = true;
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
options.SlidingExpiration = true;
options.LoginPath = "/Auth/Login";
options.AccessDeniedPath = "/Auth/AccessDenied";
});
//add authorization
//builder.Services.AddAuthorization();
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(100);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
If I turn off the authorization and not send the token in the header, everything works fine .MagicVilla_VillaAPI this project link in Github :https://github.com/MoZytoon/MagicVilla
The problem was with the program.cs file for the Api. here :
x. Authority = "https://localhost:7003/";
When setting the the AddAuthentication .

Authentication and LoginPath for different areas in ASP.NET Core 2

ASP.NET Core 2
Help me to configure AddAuthentication for two routes: users (user accounts) and admin area.
For example, if user doesn't signed in and trying to enter /Account/Orders/ he'll be redirected to /Account/SignIn/.
But if someone trying access /Admin/Orders/ must be redireted to /Admin/Signin/
Have not found ay solution ATM.
Solved!
In admin area (controllers) we using Authorize attr. arg.: [Authorize(AuthenticationSchemes = "backend")] and that is.
BTW we are able to make any tuning by accessing HttpContext in AddCookie's options and events.
Configuration:
services
.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, o =>
{
o.LoginPath = new PathString("/account/login/");
})
.AddCookie("backend", o =>
{
o.LoginPath = new PathString("/admin/account/login/");
});
#Alex's answer got me 90% of the way there.
In .Net 6, I'm using this https://learn.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-6.0 approach to use Cookies without using the Username setup in identity.
Program.cs
var authentication = services.AddAuthentication(o =>
{
o.DefaultScheme = AuthenticationSchemes.FrontEnd;
});
authentication.AddCookie(AuthenticationSchemes.FrontEnd, o =>
{
o.LoginPath = CookieAuthenticationDefaults.LoginPath;
});
authentication.AddCookie(AuthenticationSchemes.BackEnd, o =>
{
o.LoginPath = new PathString("/admin/login/");
o.AccessDeniedPath = new PathString("/admin/accessdenied");
});
AppAuthenticationSchemes.cs
public class AuthenticationSchemes
{
public const string FrontEnd = "Frontend";
public const string BackEnd = "Backend";
public const string Either = FrontEnd + "," + BackEnd;
}
AccountController.cs
[AllowAnonymous]
public class AccountController : Controller
{
private readonly FrontEndSecurityManager _frontEndSecurityManager;
public AccountController(FrontEndSecurityManager frontEndSecurityManager)
{
_frontEndSecurityManager = frontEndSecurityManager;
}
[HttpPost(Name = "Login")]
public async Task<ActionResult> Login(LoginViewModel loginModel)
{
if (string.IsNullOrEmpty(loginModel.Username) ||
string.IsNullOrEmpty(loginModel.Password))
{
ModelState.AddModelError("form", "Please enter Username and Password");
return RedirectToAction("Login", "Account");
}
var loginResult = await _frontEndSecurityManager.ValidateCredentials(loginModel.Username, loginModel.Password);
if (!loginResult.IsSuccess)
{
this.AddFlash(FlashMessageType.Danger, "UserName or Password is incorrect");
return RedirectToAction("Login", "Account");
}
var identity = await _frontEndSecurityManager.CreateIdentityAsync(loginModel.Username, loginResult);
await _frontEndSecurityManager.SignInAsync(identity, HttpContext);
return RedirectToAction("Menu", "App");
}
}
FrontEndSecurityManager.cs
public class FrontEndSecurityManager
{
private readonly SignInApi _api;
private readonly AuthenticationOptions _authenticationOptions;
public FrontEndSecurityManager(SignInApi api, IOptions<AuthenticationOptions> authenticationOptions)
{
_api = api;
_authenticationOptions = authenticationOptions.Value;
}
public async Task SignInAsync(ClaimsIdentity identity, HttpContext httpContext)
{
var authProperties = new AuthenticationProperties
{
AllowRefresh = true,
ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(30),
IsPersistent = true,
IssuedUtc = DateTimeOffset.UtcNow
};
await httpContext.SignOutAsync(AuthenticationSchemes.BackEnd);
await httpContext.SignInAsync(AuthenticationSchemes.FrontEnd, new ClaimsPrincipal(identity), authProperties);
}
public async Task<LoginResult> ValidateCredentials(string username, string password)
{
if (_authenticationOptions.DemoUserEnabled)
{
if (string.Equals(username, "demo", StringComparison.InvariantCultureIgnoreCase))
{
var result = new LoginResult(StandardResults.SuccessResult, "")
{
Employee_Name = "Demo User",
Employee_Email = "DemoGuy#gmail.com",
Employee_Initials = "DG",
Employee_Type = "Regular",
Role1 = true,
Role2 = true,
Role3 = false
};
return result;
}
}
var apiRequest = new LoginRequest() { Username = username, Password = password };
var loginResult = await _api.LoginAsync(apiRequest);
if (loginResult.Success)
{
return loginResult.Data;
}
else
{
return LoginResult.Failure();
}
}
public async Task<ClaimsIdentity> CreateIdentityAsync(string username, LoginResult loginResult)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.Role, AppRoles.User),
new Claim(ClaimTypes.Email, loginResult.Employee_Email, ClaimValueTypes.Email),
new Claim(ClaimTypes.GivenName, loginResult.GivenName),
new Claim(ClaimTypes.Surname, loginResult.Surname),
new Claim(AppClaimTypes.EmployeeType, loginResult.Employee_Type),
new Claim(AppClaimTypes.EmployeeInitials, loginResult.Employee_Initials),
new Claim(AppClaimTypes.Location, loginResult.Location.ToString(), ClaimValueTypes.Integer),
};
if (loginResult.Use_Checkin)
{
claims.Add(new Claim(ClaimTypes.Role, AppRoles.Checkin));
}
if (loginResult.Use_Pickup)
{
claims.Add(new Claim(ClaimTypes.Role, AppRoles.Pickup));
}
var identity = new ClaimsIdentity(claims, AuthenticationSchemes.FrontEnd);
return identity;
}
public void SignOutAsync(HttpContext httpContext)
{
httpContext.SignOutAsync(AuthenticationSchemes.FrontEnd);
}
}
From here, you could easily extrapolate how you want the back-end authentication controller to work. Essentially something like
await HttpContext.SignOutAsync(AuthenticationSchemes.FrontEnd);await HttpContext.SignInAsync(AuthenticationSchemes.BackEnd, new ClaimsPrincipal(identity), authProperties);
An example of using each policy would be
[Authorize(AuthenticationSchemes = AuthenticationSchemes.BackEnd)]
public IActionResult Secure()
{
return View("Secure");
}
or
[Authorize] // scheme not explicit, so Pr DefaultScheme = AuthenticationSchemes.FrontEnd is used
[Route("[controller]")]
public class OutgoingController : Controller
{
}

OAuth 2 refresh token invalid_grant

Following Taiseer Joudeh excellent article
Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin (http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/) currently I am creating a Token based authentication with refresh token option.
My Startup class code is as follows:
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new SimpleAuthorizationServerProvider(),
RefreshTokenProvider = new SimpleRefreshTokenProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
My SimpleAuthorizationServerProvider class code is as follows:
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId = string.Empty;
string clientSecret = string.Empty;
Client client = null;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (context.ClientId == null)
{
//Remove the comments from the below line context.SetError, and invalidate context
//if you want to force sending clientId/secrects once obtain access tokens.
context.Validated();
//context.SetError("invalid_clientId", "ClientId should be sent.");
return Task.FromResult<object>(null);
}
using (AuthRepository _repo = new AuthRepository())
{
client = _repo.FindClient(context.ClientId);
}
if (client == null)
{
context.SetError("invalid_clientId", string.Format("Client '{0}' is not registered in the system.", context.ClientId));
return Task.FromResult<object>(null);
}
if (client.ApplicationType == Models.ApplicationTypes.NativeConfidential)
{
if (string.IsNullOrWhiteSpace(clientSecret))
{
context.SetError("invalid_clientId", "Client secret should be sent.");
return Task.FromResult<object>(null);
}
else
{
if (client.Secret != Helper.GetHash(clientSecret))
{
context.SetError("invalid_clientId", "Client secret is invalid.");
return Task.FromResult<object>(null);
}
}
}
if (!client.Active)
{
context.SetError("invalid_clientId", "Client is inactive.");
return Task.FromResult<object>(null);
}
context.OwinContext.Set<string>("as:clientAllowedOrigin", client.AllowedOrigin);
context.OwinContext.Set<string>("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString());
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
if (allowedOrigin == null) allowedOrigin = "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
//var id = "";
using (AuthRepository _repo = new AuthRepository())
{
IdentityUser user = await _repo.FindUser(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
//Here set User.Identity.Id = RavenUserId, So rest of the user will be able to get it
//id = (user == null ? "0" : user.RavenUserId.ToString());
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
//So when we will call User.Identity.Id we will be able to get Raven User Id
// identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, id));
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId
},
{
"userName", context.UserName
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
var originalClient = context.Ticket.Properties.Dictionary["as:client_id"];
var currentClient = context.ClientId;
if (originalClient != currentClient)
{
context.SetError("invalid_clientId", "Refresh token is issued to a different clientId.");
return Task.FromResult<object>(null);
}
// Change auth ticket for refresh token requests
var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
var newClaim = newIdentity.Claims.Where(c => c.Type == "newClaim").FirstOrDefault();
if (newClaim != null)
{
newIdentity.RemoveClaim(newClaim);
}
newIdentity.AddClaim(new Claim("newClaim", "newValue"));
var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
context.Validated(newTicket);
return Task.FromResult<object>(null);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
}
My SimpleRefreshTokenProvider class code is as follows:
public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
{
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var clientid = context.Ticket.Properties.Dictionary["as:client_id"];
if (string.IsNullOrEmpty(clientid))
{
return;
}
var refreshTokenId = Guid.NewGuid().ToString("n");
using (var _repo = new AuthRepository())
{
var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime");
var token = new RefreshToken()
{
Id = Helper.GetHash(refreshTokenId),
ClientId = clientid,
Subject = context.Ticket.Identity.Name,
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime))
};
context.Ticket.Properties.IssuedUtc = token.IssuedUtc;
context.Ticket.Properties.ExpiresUtc = token.ExpiresUtc;
token.ProtectedTicket = context.SerializeTicket();
var result = await _repo.AddRefreshToken(token);
if (result)
{
context.SetToken(refreshTokenId);
}
}
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
string hashedTokenId = Helper.GetHash(context.Token);
using (var _repo = new AuthRepository())
{
var refreshToken = await _repo.FindRefreshToken(hashedTokenId);
if (refreshToken != null)
{
//Get protectedTicket from refreshToken class
context.DeserializeTicket(refreshToken.ProtectedTicket);
var result = await _repo.RemoveRefreshToken(hashedTokenId);
}
}
}
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
public void Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
}
The AuthRepository class code is as follows:
public class AuthRepository : IDisposable
{
private AuthContext _ctx;
private UserManager<IdentityUser> _userManager;
public AuthRepository()
{
_ctx = new AuthContext();
_userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(_ctx));
}
public async Task<IdentityResult> RegisterUser(UserModel userModel)
{
IdentityUser user = new IdentityUser
{
UserName = userModel.UserName
};
var result = await _userManager.CreateAsync(user, userModel.Password);
return result;
}
public async Task<IdentityUser> FindUser(string userName, string password)
{
IdentityUser user = await _userManager.FindAsync(userName, password);
return user;
}
public Client FindClient(string clientId)
{
var client = _ctx.Clients.Find(clientId);
//var clients = _ctx.Clients;
//var client = _ctx.Clients.FirstOrDefault(x => x.Id==clientId);
return client;
}
public async Task<bool> AddRefreshToken(RefreshToken token)
{
var existingToken = _ctx.RefreshTokens.Where(r => r.Subject == token.Subject && r.ClientId == token.ClientId).SingleOrDefault();
if (existingToken != null)
{
var result = await RemoveRefreshToken(existingToken);
}
_ctx.RefreshTokens.Add(token);
return await _ctx.SaveChangesAsync() > 0;
}
public async Task<bool> RemoveRefreshToken(string refreshTokenId)
{
var refreshToken = await _ctx.RefreshTokens.FindAsync(refreshTokenId);
if (refreshToken != null)
{
_ctx.RefreshTokens.Remove(refreshToken);
return await _ctx.SaveChangesAsync() > 0;
}
return false;
}
public async Task<bool> RemoveRefreshToken(RefreshToken refreshToken)
{
_ctx.RefreshTokens.Remove(refreshToken);
return await _ctx.SaveChangesAsync() > 0;
}
public async Task<RefreshToken> FindRefreshToken(string refreshTokenId)
{
var refreshToken = await _ctx.RefreshTokens.FindAsync(refreshTokenId);
return refreshToken;
}
public List<RefreshToken> GetAllRefreshTokens()
{
return _ctx.RefreshTokens.ToList();
}
public void Dispose()
{
_ctx.Dispose();
_userManager.Dispose();
}
}
And the ajax code is:
$("#refresh").click(function () {
var token = sessionStorage.getItem(tokenKey);
var refresh = sessionStorage.getItem('isRefreshToken');
var refreshToken = sessionStorage.getItem('refreshToken');
if (refresh) {
var refreshdata = "grant_type=refresh_token&refresh_token=" + refreshToken + "&client_id=TokenBasedAuthentication";
console.log(refreshdata);
sessionStorage.setItem(tokenKey, '');
sessionStorage.setItem(isRefreshToken, '');
sessionStorage.setItem(refreshToken, '');
$.ajax({
url: '/token',
type: 'POST',
data: refreshdata,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
success: function (data) {
sessionStorage.setItem(tokenKey, data.access_token);
sessionStorage.setItem(isRefreshToken, true);
sessionStorage.setItem(refreshToken, data.refresh_token);
},
error: function (xhr) {
alert(xhr.status + ': ' + xhr.statusText);
}
});
}
});
Finally when I click on Refresh it returns me following error
error: "invalid_grant"
Last two days I tried to figure out but failed.
I had a problem where I was always receiving a invalid_grant error even though I knew the refresh_token was valid. Granted there are a lot of reasons why there might be a invalid_grant error, but after debugging through the code I discovered that my issue was in the CreateAsync method. The refreshTokenLifetime variable was null. Thus, when the RefreshToken is created, the ExpiresUtc value is already expired, causing the invalid_grant error. To resolve this I verified that I had a valid value for the refreshTokenLifetime variable.
var refreshTokenLifetime = context.OwinContext.Get<string>("as:RefreshTokenLifetime") ?? "60";
Try this.
Remove this line of code newIdentity.AddClaim(new Claim("newClaim", "newValue")); from your GrantRefreshToken function of SimpleAuthorizationServerProvider class. As this line is of no use.
It is duplicating the claim when you request for new refresh token. So it is opposing you.
In ReceiveAsync method in SimpleRefreshTokenProvider class:
var refreshToken = await _repo.FindRefreshToken(hashedTokenId);
inspect refreshToken object to make sure all of its attributes have valid values.
In my case ProtectedTicket property has invalid value (Date value instead of string) and it causes this error.

UserManager is Always null when trying to Add user to Role

I am trying to add a user to a role using the following code but the UserManager always returns a NULLReferenceException. Any tips for how to get this to work. I feel like it should not be that hard.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult RoleAddToUser(string UserName, string RoleName)
{
ApplicationUser user = cd.Users.Where(u => u.UserName.Equals(UserName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
var account = new AccountController();
account.UserManager.AddToRole(user.Id, RoleName);
ViewBag.ResultMessage = "Role created successfully !";
// prepopulat roles for the view dropdown
var list = cd.Roles.OrderBy(r => r.Name).ToList().Select(rr => new SelectListItem { Value = rr.Name.ToString(), Text = rr.Name }).ToList();
ViewBag.Roles = list;
return View("Index");
}
Here is my account controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using Owin;
using LaCeibaNetv4.Models;
namespace LaCeibaNetv4.Controllers
{
[Authorize]
public class AccountController : Controller
{
private ApplicationUserManager _userManager;
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager)
{
UserManager = userManager;
}
public ApplicationUserManager UserManager {
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
//
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindAsync(model.Email, model.Password);
if (user != null)
{
await SignInAsync(user, model.RememberMe);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
return View();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model, string passCode)
{
if (ModelState.IsValid && passCode == "Fury")
{
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking here");
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/ConfirmEmail
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
IdentityResult result = await UserManager.ConfirmEmailAsync(userId, code);
if (result.Succeeded)
{
return View("ConfirmEmail");
}
else
{
AddErrors(result);
return View();
}
}
//
// GET: /Account/ForgotPassword
[AllowAnonymous]
public ActionResult ForgotPassword()
{
return View();
}
//
// POST: /Account/ForgotPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
{
ModelState.AddModelError("", "The user either does not exist or is not confirmed.");
return View();
}
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
// string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
// var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking here");
// return RedirectToAction("ForgotPasswordConfirmation", "Account");
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/ForgotPasswordConfirmation
[AllowAnonymous]
public ActionResult ForgotPasswordConfirmation()
{
return View();
}
//
// GET: /Account/ResetPassword
[AllowAnonymous]
public ActionResult ResetPassword(string code)
{
if (code == null)
{
return View("Error");
}
return View();
}
//
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null)
{
ModelState.AddModelError("", "No user found.");
return View();
}
IdentityResult result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
else
{
AddErrors(result);
return View();
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/ResetPasswordConfirmation
[AllowAnonymous]
public ActionResult ResetPasswordConfirmation()
{
return View();
}
//
// POST: /Account/Disassociate
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Disassociate(string loginProvider, string providerKey)
{
ManageMessageId? message = null;
IdentityResult result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey));
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
await SignInAsync(user, isPersistent: false);
message = ManageMessageId.RemoveLoginSuccess;
}
else
{
message = ManageMessageId.Error;
}
return RedirectToAction("Manage", new { Message = message });
}
//
// GET: /Account/Manage
public ActionResult Manage(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
: message == ManageMessageId.Error ? "An error has occurred."
: "";
ViewBag.HasLocalPassword = HasPassword();
ViewBag.ReturnUrl = Url.Action("Manage");
return View();
}
//
// POST: /Account/Manage
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Manage(ManageUserViewModel model)
{
bool hasPassword = HasPassword();
ViewBag.HasLocalPassword = hasPassword;
ViewBag.ReturnUrl = Url.Action("Manage");
if (hasPassword)
{
if (ModelState.IsValid)
{
IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
}
else
{
AddErrors(result);
}
}
}
else
{
// User does not have a password so remove any validation errors caused by a missing OldPassword field
ModelState state = ModelState["OldPassword"];
if (state != null)
{
state.Errors.Clear();
}
if (ModelState.IsValid)
{
IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
if (result.Succeeded)
{
return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess });
}
else
{
AddErrors(result);
}
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}
//
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
// Sign in the user with this external login provider if the user already has a login
var user = await UserManager.FindAsync(loginInfo.Login);
if (user != null)
{
await SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
else
{
// If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
}
}
//
// POST: /Account/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LinkLogin(string provider)
{
// Request a redirect to the external login provider to link a login for the current user
return new ChallengeResult(provider, Url.Action("LinkLoginCallback", "Account"), User.Identity.GetUserId());
}
//
// GET: /Account/LinkLoginCallback
public async Task<ActionResult> LinkLoginCallback()
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
if (loginInfo == null)
{
return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
}
IdentityResult result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
if (result.Succeeded)
{
return RedirectToAction("Manage");
}
return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
}
//
// POST: /Account/ExternalLoginConfirmation
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
{
if (User.Identity.IsAuthenticated)
{
return RedirectToAction("Manage");
}
if (ModelState.IsValid)
{
// Get the information about the user from the external login provider
var info = await AuthenticationManager.GetExternalLoginInfoAsync();
if (info == null)
{
return View("ExternalLoginFailure");
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user);
if (result.Succeeded)
{
result = await UserManager.AddLoginAsync(user.Id, info.Login);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// SendEmail(user.Email, callbackUrl, "Confirm your account", "Please confirm your account by clicking this link");
return RedirectToLocal(returnUrl);
}
}
AddErrors(result);
}
ViewBag.ReturnUrl = returnUrl;
return View(model);
}
//
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut();
return RedirectToAction("Index", "Home");
}
//
// GET: /Account/ExternalLoginFailure
[AllowAnonymous]
public ActionResult ExternalLoginFailure()
{
return View();
}
[ChildActionOnly]
public ActionResult RemoveAccountList()
{
var linkedAccounts = UserManager.GetLogins(User.Identity.GetUserId());
ViewBag.ShowRemoveButton = HasPassword() || linkedAccounts.Count > 1;
return (ActionResult)PartialView("_RemoveAccountPartial", linkedAccounts);
}
protected override void Dispose(bool disposing)
{
if (disposing && UserManager != null)
{
UserManager.Dispose();
UserManager = null;
}
base.Dispose(disposing);
}
#region Helpers
// Used for XSRF protection when adding external logins
private const string XsrfKey = "XsrfId";
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, await user.GenerateUserIdentityAsync(UserManager));
}
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error);
}
}
private bool HasPassword()
{
var user = UserManager.FindById(User.Identity.GetUserId());
if (user != null)
{
return user.PasswordHash != null;
}
return false;
}
private void SendEmail(string email, string callbackUrl, string subject, string message)
{
// For information on sending mail, please visit http://go.microsoft.com/fwlink/?LinkID=320771
}
public enum ManageMessageId
{
ChangePasswordSuccess,
SetPasswordSuccess,
RemoveLoginSuccess,
Error
}
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
private class ChallengeResult : HttpUnauthorizedResult
{
public ChallengeResult(string provider, string redirectUri) : this(provider, redirectUri, null)
{
}
public ChallengeResult(string provider, string redirectUri, string userId)
{
LoginProvider = provider;
RedirectUri = redirectUri;
UserId = userId;
}
public string LoginProvider { get; set; }
public string RedirectUri { get; set; }
public string UserId { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
}
}
#endregion
}
}
I think you might be getting error on the following line :
account.UserManager.AddToRole(user.Id, RoleName);
at user.Id
You must check :
if (user != null)
then add User to Role.

Web API 2. Register method returns error

I've created a simple web api 2 service from default template (asp.net mvc 5) with basic authentication.
I use Unity.WebAPI for IoC. And when I try send a POST request to /api/account/register method I get an error:
An error occurred when trying to create a controller of type 'AccountController'.
Make sure that the controller has a parameterless public constructor.
Googling didn't help me because all example are more specific, but I'm too new in Web API and Unity.
I found that link but didn't understood all from #Horizon_Net's answer :(
Can anyone show me the example "for dummies"?
Edit:
using System;
using System.Collections.Generic;
using System.Linq;
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.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OAuth;
using NewMMC.Models;
using NewMMC.Providers;
using NewMMC.Results;
namespace NewMMC.Controllers
{
[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
}
}
Edit 2: If I comment UnityConfig.RegisterComponents(); string in my WebApiConfig class registration works good.
Edit 3:
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
container.RegisterType<Data.Interfaces.IRepository, MemoryRepository>(new ContainerControlledLifetimeManager());
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}

Resources