In asp.net webform application, trying to save user to AspNetUsers after UPDATE-DATABASE command. the following code doesnt do that. solution ?
public Configuration()
{
AutomaticMigrationsEnabled = true;
}
protected override void Seed(MyApp.Models.ApplicationDbContext context)
{
if (!context.Users.Any(u => u.Email == "some#mail"))
{
var store = new UserStore<ApplicationUser>(context);
var manager = new UserManager<ApplicationUser>(store);
var user = new ApplicationUser { Email = "some#mail" };
manager.Create(user, "password");
}
}
In order to add ApplicationUser you must have the property Username initialized.
Related
I have an app that uses CosmosDb as the database and using AspNetIdentityDocument. When I call var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, false), i get the error Cross partition query is required but disabled. Please set x-ms-documentdb-query-enablecrosspartition to true, specify x-ms-documentdb-partitionkey
void InitializeDocumentClient(DocumentClient client) code attempts to create the container if not there. It works for the creating the container on my CossmosDb emultated store but fails on the Azure store requiring a partition key! My app works on the emulated store!
Program.cs
builder.Services.AddDefaultDocumentClientForIdentity(
builder.Configuration.GetValue<Uri>("DocumentDbClient:EndpointUri"),
builder.Configuration.GetValue<string>("DocumentDbClient:AuthorizationKey"),
afterCreation: InitializeDocumentClient);
builder.Services.AddIdentity<ApplicationUser, DocumentDbIdentityRole>()
.AddDocumentDbStores(options =>
{
options.UserStoreDocumentCollection = "AspNetIdentity";
options.Database = "RNPbooking";
})
.AddDefaultTokenProviders();
void InitializeDocumentClient(DocumentClient client)
{
try
{
var db = client.ReadDatabaseAsync(UriFactory.CreateDatabaseUri("RNPbooking")).Result;
}
catch (AggregateException ae)
{
ae.Handle(ex =>
{
if (ex.GetType() == typeof(DocumentClientException) && ((DocumentClientException)ex).StatusCode == HttpStatusCode.NotFound)
{
var db = client.CreateDatabaseAsync(new Microsoft.Azure.Documents.Database() { Id = "RNPbooking" }).Result;
return true;
}
return false;
});
}
try
{
var collection = client.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri("RNPbooking", "AspNetIdentity")).Result;
}
catch (AggregateException ae)
{
ae.Handle(ex =>
{
if (ex.GetType() == typeof(DocumentClientException) && ((DocumentClientException)ex).StatusCode == HttpStatusCode.NotFound)
{
DocumentCollection collection = new DocumentCollection()
{
Id = "AspNetIdentity"
};
collection = client.CreateDocumentCollectionAsync(UriFactory.CreateDatabaseUri("RNPbooking"),collection).Result;
return true;
}
return false;
});
}
}
Controller
[Authorize(Roles = "Admin)]
public class AdminController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
public CosmosClient _client;
public AdminController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
)
{
_userManager = userManager;
_signInManager = signInManager;
}
You need to fill in CreateDocumentCollectionUri method with FeedOptions object as a parameter
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),new FeedOptions { EnableCrossPartitionQuery=true})
UPDATED: From the code examples, you seem to be using this library https://github.com/codekoenig/AspNetCore.Identity.DocumentDb, AspNetCore.Identity.DocumentDb.
This error means the library you are using is performing a Document Query in their code at some point, it is not related to the creation of the Database or Collection.
The library code must be using CreateDocumentQuery somewhere, that code is missing:
new FeedOptions { EnableCrossPartitionQuery = true };
If you search their code base, you will see multiple scenarios like that: https://github.com/codekoenig/AspNetCore.Identity.DocumentDb/search?q=CreateDocumentQuery
Because this code is out of your control, you should try and contact the owner to see if this is a fix they can do on their end. The code for the library doesn't seem to have been updated in several years, so maybe this library is not maintained?
I faced problem with hashing, salting and verifying password in ASP.NET.
I am creating a new User and then using hashing method.
But when I try to get some resources which requires Authorization and I
enter the same username and password as I saved in database the result is failed.
Here is my password hasher class:
using Microsoft.AspNetCore.Identity;
namespace FlowerShop.ApplicationServices.Components.PasswordHasher
{
public class BCryptPasswordHasher<User> : IPasswordHasher<User> where User : class
{
public string HashPassword(User user, string password)
{
return BCrypt.Net.BCrypt.HashPassword(password, 12);
}
public PasswordVerificationResult VerifyHashedPassword(User user, string hashedPassword, string providedPassword)
{
var isValid = BCrypt.Net.BCrypt.Verify(providedPassword, hashedPassword);
if (isValid && BCrypt.Net.BCrypt.PasswordNeedsRehash(hashedPassword, 12))
{
return PasswordVerificationResult.SuccessRehashNeeded;
}
return isValid ? PasswordVerificationResult.Success : PasswordVerificationResult.Failed;
}
}
This is my authentication class:
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly IQueryExecutor queryExecutor;
private readonly IPasswordHasher<User> passwordHasher;
public BasicAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock,
IQueryExecutor queryExecutor, IPasswordHasher<User> passwordHasher)
: base(options, logger, encoder, clock)
{
this.queryExecutor = queryExecutor;
this.passwordHasher = passwordHasher;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
var endpoint = Context.GetEndpoint();
if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
{
return AuthenticateResult.NoResult();
}
if (!Request.Headers.ContainsKey("Authorization"))
{
return AuthenticateResult.Fail("Missing Authorization Header");
}
User user = null;
try
{
var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2);
var username = credentials[0];
var providedPassword = passwordHasher.HashPassword(user, credentials[1]);
var query = new GetUserQuery()
{
UserName = username
};
user = await this.queryExecutor.Execute(query);
if (user == null || passwordHasher.VerifyHashedPassword(user, user.PasswordHash, providedPassword)
== PasswordVerificationResult.Failed)
{
return AuthenticateResult.Fail("Invalid Authorization Header");
}
}
catch
{
return AuthenticateResult.Fail("Invalid Authorization Header");
}
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.Role, user.Role.ToString()),
new Claim(ClaimTypes.Email, user.Email),
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
}
And in this place I am creating a new User:
using MediatR;
using Microsoft.AspNetCore.Identity;
using System.Threading;
using System.Threading.Tasks;
public class AddUserHandler : IRequestHandler<AddUserRequest,
AddUserResponse>
{
private readonly ICommandExecutor commandExecutor;
private readonly IQueryExecutor queryExecutor;
private readonly IMapper mapper;
private readonly IPasswordHasher<User> passwordHasher;
public AddUserHandler(ICommandExecutor commandExecutor,
IQueryExecutor queryExecutor,
IMapper mapper, IPasswordHasher<User> passwordHasher)
{
this.commandExecutor = commandExecutor;
this.queryExecutor = queryExecutor;
this.mapper = mapper;
this.passwordHasher = passwordHasher;
}
public async Task<AddUserResponse> Handle(AddUserRequest
request, CancellationToken cancellationToken)
{
var query = new GetUserQuery()
{
UserName = request.UserName,
Email = request.Email
};
var getUser = await this.queryExecutor.Execute(query);
if (getUser != null)
{
if (getUser.UserName == request.UserName)
{
return new AddUserResponse()
{
Error = new ErrorModel(ErrorType.ValidationError +
"! The name is already taken.")
};
}
if (getUser.Email == request.Email)
{
return new AddUserResponse()
{
Error = new ErrorModel(ErrorType.ValidationError +
"! Email address is in use.")
};
}
return new AddUserResponse()
{
Error = new ErrorModel(ErrorType.Conflict)
};
}
request.PasswordHash = passwordHasher.HashPassword(getUser,
request.Password);
var user = this.mapper.Map<User>(request);
var command = new AddUserCommand()
{
Parameter = user
};
var addedUser = await this.commandExecutor.Execute(command);
var response = new AddUserResponse()
{
Data =
this.mapper.Map<Domain.Models.UserDTO>(addedUser)
};
return response;
}
}
This is my Startup.cs :
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication("BasicAuthentication")
.AddScheme<AuthenticationSchemeOptions,
BasicAuthenticationHandler>("BasicAuthentication", null);
services.AddScoped<IPasswordHasher<User>,
BCryptPasswordHasher<User>>();
}
Maybe first of all, is it all correct implemented?
Is hash in AddUserHandler correct assigned to request.PasswordHash?
How to retrieve salt and assign to request.PasswordSalt?
Sorry for any unclear things if they occur.
Any feedback and help will be appreciated.
Thanks in advance :)
Edit:
for example if I add user with password "pass123" and it is stored in database as 'user.PasswordHash = "$2a$12$Iqpy7FyQh/pt2O8upTtG5eOQKzo1V395wRNdAXPpp5Qf.NQ.KxUyy"' and provided password after hashing is 'providedPassword = "$2a$12$9vSz8Sw/WtmqGY6jyDiTleN/btZ0wXJkXdoB3sDpANVIIDGBpaqT."'
I fixed the bug if anyone needs to use it in thee future.
The problem was in my authentication class.
In place of:
var username = credentials[0];
var providedPassword = passwordHasher.HashPassword(user, credentials[1]);
Should be:
var username = credentials[0];
var providedPassword = credentials[1];
I am sure that I have checked it a few times but somehow didn't work then. Anyway, it finally works properly.
I have class UserFactory where I want to check if User already exist in database, if not it will be automatically created. But having difficulties with accessing DbContext from outside the Controller.
public UserFactory(DbContextOptionsBuilder builder)
{
_dbContext = new PMSContext(builder.Options) ;
}
public User Create(WindowsIdentity currentWindowsUser)
{
User user = new User();
string name = currentWindowsUser.Name.Replace("DOMAIN\\", "");
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal ADuser = UserPrincipal.FindByIdentity(ctx, name);
if(ADuser != null)
{
User userInDatabase = _dbContext.Users.Where(u => u.SamAccountName == name).FirstOrDefault();
}
}
And than in the Startup.cs I habe :
public Startup(IHostingEnvironment env)
{
var builder = new DbContextOptionsBuilder<PMSContext>()
.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]);
User user = new UserFactory(builder).Create(WindowsIdentity.GetCurrent());
}
But I'm getting error:
Severity Code Description Project File Line
Error CS1503 Argument 1: cannot convert from 'Microsoft.Data.Entity.Infrastructure.SqlServerDbContextOptionsBuilder' to 'Microsoft.Data.Entity.DbContextOptionsBuilder'
Is this the right way to access DbContext from the outside of the Controller?
The answer was quite simple:
var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]);
var context = new PMSContext(optionsBuilder.Options);
User user = new UserFactory(context).Create(WindowsIdentity.GetCurrent());
Context:
I am using ASP.NET MVC with OWIN self host. Below are the rest of the configs/setup.
In my Clients in identity server (notice the AllowedScopes set):
public static class InMemoryClientSource
{
public static List<Client> GetClientList()
{
return new List<Client>()
{
new Client()
{
ClientName = "Admin website",
ClientId = "admin",
Enabled = true,
Flow = Flows.Hybrid,
ClientSecrets = new List<Secret>()
{
new Secret("admin".Sha256())
},
RedirectUris = new List<string>()
{
"https://admin.localhost.com/"
},
PostLogoutRedirectUris = new List<string>()
{
"https://admin.localhost.com/"
},
AllowedScopes = new List<string> {
Constants.StandardScopes.OpenId,
Constants.StandardScopes.Profile,
Constants.StandardScopes.Email,
Constants.StandardScopes.Roles
}
}
};
}
}
Here are the Scopes:
public static class InMemoryScopeSource
{
public static List<Scope> GetScopeList()
{
var scopes = new List<Scope>();
scopes.Add(StandardScopes.OpenId);
scopes.Add(StandardScopes.Profile);
scopes.Add(StandardScopes.Email);
scopes.Add(StandardScopes.Roles);
return scopes.ToList();
}
}
In the Identity Server, here's how the server is configured. (Notice the Clients and Scopes are the ones provided above) :
var userService = new UsersService( .... repository passed here .... );
var factory = new IdentityServerServiceFactory()
.UseInMemoryClients(InMemoryClientSource.GetClientList())
.UseInMemoryScopes(InMemoryScopeSource.GetScopeList());
factory.UserService = new Registration<IUserService>(resolver => userService);
var options = new IdentityServerOptions()
{
Factory = factory,
SigningCertificate = Certificates.Load(), // certificates blah blah
SiteName = "Identity"
};
app.UseIdentityServer(options);
Finally, on the client web application side, this is how auth is set up:
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions()
{
Authority = "https://id.localhost.com",
ClientId = "admin",
RedirectUri = "https://admin.localhost.com/",
PostLogoutRedirectUri = "https://admin.localhost.com/",
ResponseType = "code id_token token",
Scope = "openid profile email roles",
ClientSecret = "admin",
SignInAsAuthenticationType = "Cookies"
});
I have implemented a custom class for IUserService:
public class UsersService : UserServiceBase
{
public UsersService( .... repository passed here .... )
{
//.... ctor stuff
}
public override Task AuthenticateLocalAsync(LocalAuthenticationContext context)
{
// var user = .... retrieved from database .....
// ... auth logic ...
if (isAuthenticated)
{
var claims = new List<Claim>();
claims.Add(new Claim(Constants.ClaimTypes.GivenName, user.FirstName));
claims.Add(new Claim(Constants.ClaimTypes.FamilyName, user.LastName));
claims.Add(new Claim(Constants.ClaimTypes.Email, user.EmailAddress));
context.AuthenticateResult = new AuthenticateResult(user.Id.ToString(), user.EmailAddress, claims);
}
return Task.FromResult(0);
}
}
As you see, the claims are passed in this line:
context.AuthenticateResult = new AuthenticateResult(user.Id.ToString(), user.EmailAddress, claims);
When I try logging in to IdentityServer3, I can log in successfully to the client web application. HOWEVER, when I get the user claims, I don't see any identity claims. No given_name, family_name, and email claims. Screenshot below:
Anything I might have missed? Thanks in advance!
My solution was to add a list of claims to my scope configuration in order to return those claims. The wiki's documentation here described it.
For an in-memory client all I did was something like this:
public class Scopes
{
public static IEnumerable<Scope> Get()
{
return new Scope[]
{
StandardScopes.OpenId,
StandardScopes.Profile,
StandardScopes.Email,
StandardScopes.Roles,
StandardScopes.OfflineAccess,
new Scope
{
Name = "yourScopeNameHere",
DisplayName = "A Nice Display Name",
Type = ScopeType.Identity,
Emphasize = false,
Claims = new List<ScopeClaim>
{
new ScopeClaim("yourClaimNameHere", true),
new ScopeClaim("anotherClaimNameHere", true)
}
}
};
}
}
Finally found the solution for this problem.
First, I moved the creation of claims to the overridden GetProfileDataAsync (in my UserService class). Here's my implementation of it:
public override Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var identity = new ClaimsIdentity();
UserInfo user = null;
if (!string.IsNullOrEmpty(context.Subject.Identity.Name))
user = _facade.Get(context.Subject.Identity.Name);
else
{
// get the sub claim
var claim = context.Subject.FindFirst(item => item.Type == "sub");
if (claim != null)
{
Guid userId = new Guid(claim.Value);
user = _facade.Get(userId);
}
}
if (user != null)
{
identity.AddClaims(new[]
{
new Claim(Constants.ClaimTypes.PreferredUserName, user.Username),
new Claim(Constants.ClaimTypes.Email, user.EmailAddress)
// .. other claims
});
}
context.IssuedClaims = identity.Claims; //<- MAKE SURE you add the claims here
return Task.FromResult(identity.Claims);
}
Make sure that we pass the claims to the "context.IssueClaims" inside the GetProfileDataAsync() before returning the task.
And for those interested on how my AuthenticateLocalAsync() looks like:
var user = _facade.Get(context.UserName);
if (user == null)
return Task.FromResult(0);
var isPasswordCorrect = BCrypt.Net.BCrypt.Verify(context.Password, user.Password);
if (isPasswordCorrect)
{
context.AuthenticateResult = new AuthenticateResult(user.Id.ToString(), user.Username);
}
return Task.FromResult(0);
I raised a similar issue in IdentityServer3 GitHub project page that contains the explanation on why I encountered my issue. Here's the link:
https://github.com/IdentityServer/IdentityServer3/issues/1938
I am not using the identity server, however I am using the Windows Identity Foundation, which I believe is what IdentityServer uses. In order to access the claims I use:
((ClaimsIdentity)User.Identity).Claims
I have a Web Api project.
I have implemented a custom Authentication Attribute like so:
public class TokenAuthenticationAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
// In auth web method you should implement functionality of authentication
// so that client app could be able to get token
if (actionContext.Request.RequestUri.AbsolutePath.Contains("api/auth/login"))
{
return;
}
// Receive token from the client. Here is the example when token is in header:
var token = HttpContext.Current.Request.Headers["Token"];
// Put your secret key into the configuration
var secretKey = ConfigurationManager.AppSettings["JWTSecurityKey"];
try
{
string jsonPayload = JWT.JsonWebToken.Decode(token, secretKey);
int separatorIndex = jsonPayload.IndexOf(';');
string userId = "";
DateTime timeIssued = DateTime.MinValue;
if (separatorIndex >= 0)
{
//userId = UTF8Encoding.UTF8.GetString(Convert.FromBase64String(jsonPayload.Substring(0, separatorIndex)));
userId = jsonPayload.Substring(0, separatorIndex);
timeIssued = DateTime.Parse(jsonPayload.Substring(separatorIndex + 1));
}
short TokenTTL = 10;
//try{
//Int16.TryParse(ConfigurationManager.AppSettings["TokenTTL"],TokenTTL);
//}catch(Exception e){ //}
if ((DateTime.Now.Subtract(timeIssued).TotalMinutes >= TokenTTL))
{
throw new HttpResponseException(HttpStatusCode.Forbidden);
}
//Save user in context
var claims = new List<Claim>()
{
new Claim(ClaimTypes.Name, userId)
};
var id = new ClaimsIdentity(claims, "Basic");
var principal = new ClaimsPrincipal(new[] { id });
actionContext.Request.GetRequestContext().Principal = principal;
}
catch (JWT.SignatureVerificationException)
{
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
}
}
Now how do I get hold of that user in my actionmethod?
[BasicHttpAuthorizeAttribute]
[httpGet]
public void Login()
{
// how do i get user here
}
/////// Save the string username to the context so that I can acess
it in the controler.
var claims = new List<Claim>()
{
new Claim(ClaimTypes.Name, "john")
};
var id = new ClaimsIdentity(claims, "Basic");
var principal = new ClaimsPrincipal(new[] { id });
actionContext.Request.GetRequestContext().Principal = principal;
// how do i get user here
var name = User.Identity.Name;
BTW, use an authentication filter instead of an authorization filter to perform authentication. See my blog post - http://lbadri.wordpress.com/2014/02/13/basic-authentication-with-asp-net-web-api-using-authentication-filter/.