I have a custom validator that validate the incoming username and password in a webservice .
Once the validation is done, i need to use that user name and password inside the webservice .
Here is my CustomValidator
public class ServiceAuthenticator : UserNamePasswordValidator
{
private static readonly ILog _log = LogManager.GetLogger("ServiceAuthenticator");
public override void Validate(String userName, string password)
{
_log.InfoFormat("-------------{0}/{1}------------------------------", userName, password);
if (userName == null || password == null)
{
_log.WarnFormat(" Missing User-name / Password {0}/{1}", userName, password);
throw new FaultException("Incorrect User name or Password");
}
}
}
Now i have a webservice where i am trying to get the above user name and password
[WebInvoke(Method = "POST", UriTemplate = "Uplooc")]
[WebMethod(Description = "Save documents ")]
public void UploadDocGen(RemoteFileInfo remoteFileInfo)
{
// string UserName = ""; --- How i get the username
// sting Password = ""; -- How to get the password into this
}
We could use the ServiceSecurityContext to obtain the username value, while we could not get the password after the credential is authenticated to pass.
public string SayHello()
{
OperationContext oc = OperationContext.Current;
var username1=oc.ServiceSecurityContext.PrimaryIdentity.Name;
Console.WriteLine(username1);
var username2 = ServiceSecurityContext.Current.PrimaryIdentity.Name;
Console.WriteLine(username2);
return $"Hello Buddy,{DateTime.Now.ToLongTimeString()}";
}
Result.
The security token based on the SAML, we only can obtain the claim sets. It is a complex topic, which I don’t know much.
Here are some related documents, wish it is useful to you.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/how-to-examine-the-security-context
https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.servicesecuritycontext?view=netframework-4.8
Feel free to let me know if there is anything I can help with.
Related
I am using .net core 2.0 brand new fresh project. When building out my roles with Identity I am also creating a user. To create the user I have the following code.
var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var userManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
string[] roleNames = { "SuperAdmin", "Admin", "Support", "User" };
IdentityResult roleResult;
foreach (var roleName in roleNames)
{
var roleExist = await roleManager.RoleExistsAsync(roleName);
if (!roleExist)
{
//create the roles and seed them to the database: Question 1
roleResult = await roleManager.CreateAsync(new IdentityRole(roleName));
}
}
// administrator
var user = new ApplicationUser
{
UserName = "Administrator",
Email = "something#something.com",
EmailConfirmed = true
};
var i = await userManager.FindByEmailAsync(user.Email);
if (i == null)
{
var adminUser = await userManager.CreateAsync(user, "Testing1!");
if (adminUser.Succeeded)
{
await userManager.AddToRoleAsync(user, "SuperAdmin");
}
}
The user creates, but when I try to login with the email and password it says invalid login.
For the PasswordSignInAsync method, that you are using...
await _signInManager.PasswordSignInAsync(model.Email, model.Password,
model.RememberMe, lockoutOnFailure: false);
It requires the username and not the email. There is not a method on the SignInManager that uses a string email as a parameter to signin with a password.
public virtual async Task<SignInResult> PasswordSignInAsync(string userName, string password,
bool isPersistent, bool lockoutOnFailure)
Please see:
https://github.com/aspnet/Identity/blob/dev/src/Identity/SignInManager.cs#L297
If you want to use the email address and not the username, please look at using the following:
public virtual async Task<SignInResult> CheckPasswordSignInAsync(TUser user, string password, bool lockoutOnFailure)
Or:
public virtual async Task<SignInResult> PasswordSignInAsync(TUser user, string password,
bool isPersistent, bool lockoutOnFailure)
Your comment:
The default Identity install uses the username, which it sets as the
email when someone registers. Then the values are the same thats why I
needed to change the username to the email upon creation. Thank you
though for this response. It will help me not have to constantly make
sure I'm updating two fields if someone wants to change their login
email.
This may be true for default values; however, it does not reflect the actual code and how the login is resolved. The source clearly uses the userName and not the emailAddress to find the user with the signin operation that you are using.
public virtual async Task<SignInResult> PasswordSignInAsync(string userName, string password,
bool isPersistent, bool lockoutOnFailure)
{
var user = await UserManager.FindByNameAsync(userName);
if (user == null)
{
return SignInResult.Failed;
}
return await PasswordSignInAsync(user, password, isPersistent, lockoutOnFailure);
}
Alternative solution
This approach uses the UserManager<T> to lookup the user via their email or userName and then uses the SignInManager<T> to attempt to login the user using the PasswordSignInAsync overload method:
public virtual async Task<SignInResult> PasswordSignInAsync(TUser user, string password,
bool isPersistent, bool lockoutOnFailure)
In this example T is an ApplicationUser : IdentityUser
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
....
/// <summary>
/// Handle postback from username/password login
/// </summary>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model, string button)
{
try
{
if (!ModelState.IsValid)
{
return await ReturnLoginError(model);
}
ApplicationUser user = await _userManager.FindByEmailAsync(model.Email)
?? await _userManager.FindByNameAsync(model.Username);
SignInResult signInResult = await _signInManager.PasswordSignInAsync(user, model.Password, isPersistent: true,
lockoutOnFailure: true);
...
}
This will allow you to use either the email address or username to perform the login.
To solve the issue I had to set the username and the email to the same value as I want my users to login via email.
The PasswordSignInAsync wants a username not an email so I had to use this instead.
// administrator
var user = new ApplicationUser
{
UserName = "something#something.com",
Email = "something#something.com",
EmailConfirmed = true
};
I'm creating one asp.net mvc 4.0 application. in which I want to use default asp.net form authentication.
I've created all required tables in ms sql for storing roles and username password.
help me earliest possible. I'm stuck at this point.
if you are using asp.net mvc 4.0 form authentication with required tables in mssql server.
you can create new user as follows.
[AllowAnonymous]
public JsonResult RegisterUser()
{
String Uid = Request.QueryString["id"];
String Pass = Request.QueryString["pass"];
String username = Uid;
String password = Pass;
try
{
//Session["username"] = username;
Membership.CreateUser(Uid, Pass);
return Json("success", JsonRequestBehavior.AllowGet);
}
catch(Exception e)
{
return Json("falied", JsonRequestBehavior.AllowGet);
}
}
and you can validate the user in the same as follows.
[AllowAnonymous]
public JsonResult ValidateUser()
{
String Uid = Request.QueryString["id"];
String Pass = Request.QueryString["pass"];
String username = Uid;
String password = Pass;
if (Membership.ValidateUser(username, password))
{
//Session["username"] = username;
FormsAuthentication.RedirectFromLoginPage(username, true);
return Json("success", JsonRequestBehavior.AllowGet);
}
else
{
return Json("falied", JsonRequestBehavior.AllowGet);
}
}
Hope this will help you.
I'm having trouble solving architecture of an ASP MVC application that servers html pages and web services through ServiceStack.
The application lives in the base url eg "http://myapplication.com" and SS lives in "http://myapplication.com/api" because it is the easiest way to configure both.
In general everything works fine, but when I reached the part of the authorization and authentication, is where I'm stuck.
For one, I need the application handle cookies as ASP normally do FormsAuthentication through, and users would go through a login screen and could consume actions and controllers when the attribute "Authorize" is used. This is typical of ASP, so I have no problem with it, such as "http://myapplication.com/PurchaseOrders".
On the other hand, clients of my application will consume my web service api from javascript. Those web services will also be tagged in some cases with the attribute "Authenticate" of ServiceStack. For example "http://myapplication.com/api/purchaseorders/25" would have to validate if the user can view that particular purchase order, otherwise send a 401 Unauthorized so javascript can handle those cases and display the error message.
Last but not least, another group of users will make use of my API by a token, using any external application (probably Java or .NET). So I need to solve two types of authentication, one using username and password, the other by the token and make them persistant so once they are authenticated the first time, the next calls are faster to solve from the API.
This is the code that I have so far, I've put it very simply to make clear the example.
[HttpPost]
public ActionResult Logon(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
JsonServiceClient client = new JsonServiceClient("http://myapplication.com/api/");
var authRequest = new Auth { provider = CredentialsAuthProvider.Name, UserName = model.UserName, Password = model.Password, RememberMe = model.RememberMe };
try
{
var loginResponse = client.Send(authRequest);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(loginResponse.UserName, false, 60);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket));
Response.Cookies.Add(cookie);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/") && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Test");
}
}
catch (Exception)
{
ModelState.AddModelError("", "Invalid username or password");
}
}
return View();
}
As for the authentication provider I am using this class
public class MyCredentialsAuthProvider : CredentialsAuthProvider
{
public MyCredentialsAuthProvider(AppSettings appSettings)
: base(appSettings)
{
}
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
//Add here your custom auth logic (database calls etc)
//Return true if credentials are valid, otherwise false
if (userName == "testuser" && password == "nevermind")
{
return true;
}
else
{
return false;
}
}
public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
{
//Fill the IAuthSession with data which you want to retrieve in the app eg:
session.FirstName = "some_firstname_from_db";
//...
session.CreatedAt = DateTime.Now;
session.DisplayName = "Mauricio Leyzaola";
session.Email = "mauricio.leyzaola#gmail.com";
session.FirstName = "Mauricio";
session.IsAuthenticated = true;
session.LastName = "Leyzaola";
session.UserName = "mauricio.leyzaola";
session.UserAuthName = session.UserName;
var roles = new List<string>();
roles.AddRange(new[] { "admin", "reader" });
session.Roles = roles;
session.UserAuthId = "uniqueid-from-database";
//base.OnAuthenticated(authService, session, tokens, authInfo);
authService.SaveSession(session, SessionExpiry);
}
}
On the Configure function of AppHost I am setting my custom authentication class to use it as the default. I guess I should create another class and add it here as well, to handle the token scenario.
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new MyCredentialsAuthProvider(appSettings)
}, htmlRedirect: "~/Account/Logon"));
So far, ServiceStack is working as expected. I can submit a post to /auth/credentials passing username and password and it stores this information, so next call to a service the request is already authorized, great so far!
The question I need to know is how to call (and probably set somewhere in SS) the user that is logging in from my Account controller. If you see the first block of code I am trying to call the web service (looks like I am doing it wrong) and it works, but the next call to any web service looks unauthenticated.
Please don't point me to ServiceStack tutorials, I've been there for the last two days and still cannot figure it out.
Thanks a lot in advance.
Here is what I usually use:
You can replace the "Logon" action method with the code below:
public ActionResult Login(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
try
{
var authService = AppHostBase.Resolve<AuthService>();
authService.RequestContext = System.Web.HttpContext.Current.ToRequestContext();
var response = authService.Authenticate(new Auth
{
UserName = model.UserName,
Password = model.Password,
RememberMe = model.RememberMe
});
// add ASP.NET auth cookie
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
return RedirectToLocal(returnUrl);
}
catch (HttpError)
{
}
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
...and the plugins:
//Default route: /auth/{provider}
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new CustomCredentialsAuthProvider(),
new CustomBasicAuthProvider()
}));
....the Auth provider classes:
public class CustomCredentialsAuthProvider : CredentialsAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
return UserLogUtil.LogUser(authService, userName, password);
}
}
public class CustomBasicAuthProvider : BasicAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
return UserLogUtil.LogUser(authService, userName, password);
}
}
...finally, the logging utility class
internal static class UserLogUtil
{
public static bool LogUser(IServiceBase authService, string userName, string password)
{
var userService = new UserService(); //This can be a webservice; or, you can just call your repository from here
var loggingResponse = (UserLogResponse)userService.Post(new LoggingUser { UserName = userName, Password = password });
if (loggingResponse.User != null && loggingResponse.ResponseStatus == null)
{
var session = (CustomUserSession)authService.GetSession(false);
session.DisplayName = loggingResponse.User.FName.ValOrEmpty() + " " + loggingResponse.User.LName.ValOrEmpty();
session.UserAuthId = userName;
session.IsAuthenticated = true;
session.Id = loggingResponse.User.UserID.ToString();
// add roles and permissions
//session.Roles = new List<string>();
//session.Permissions = new List<string>();
//session.Roles.Add("Admin);
//session.Permissions.Add("Admin");
return true;
}
else
return false;
}
}
It's an outdated article, but http://msdn.microsoft.com/en-us/library/ff650308.aspx#paght000026_step3 illustrates what I want to do. I've chosen Nancy as my web framework because of it's simplicity and low-ceremony approach. So, I need a way to authenticate against Active Directory using Nancy.
In ASP.NET, it looks like you can just switch between a db-based membership provider and Active Directory just by some settings in your web.config file. I don't need that specifically, but the ability to switch between dev and production would be amazing.
How can this be done?
Really the solution is much simpler than it may seem. Just think of Active Directory as a repository for your users (just like a database). All you need to do is query AD to verify that the username and password entered are valid. SO, just use Nancy's Forms Validation and handle the connetion to AD in your implementation of IUserMapper. Here's what I came up with for my user mapper:
public class ActiveDirectoryUserMapper : IUserMapper, IUserLoginManager
{
static readonly Dictionary<Guid, long> LoggedInUserIds = new Dictionary<Guid, long>();
readonly IAdminUserValidator _adminUserValidator;
readonly IAdminUserFetcher _adminUserFetcher;
readonly ISessionContainer _sessionContainer;
public ActiveDirectoryUserMapper(IAdminUserValidator adminUserValidator, IAdminUserFetcher adminUserFetcher, ISessionContainer sessionContainer)
{
_adminUserValidator = adminUserValidator;
_adminUserFetcher = adminUserFetcher;
_sessionContainer = sessionContainer;
}
public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
{
_sessionContainer.OpenSession();
var adminUserId = LoggedInUserIds.First(x => x.Key == identifier).Value;
var adminUser = _adminUserFetcher.GetAdminUser(adminUserId);
return new ApiUserIdentity(adminUser);
}
public Guid Login(string username, string clearTextPassword, string domain)
{
var adminUser = _adminUserValidator.ValidateAndReturnAdminUser(username, clearTextPassword, domain);
var identifier = Guid.NewGuid();
LoggedInUserIds.Add(identifier, adminUser.Id);
return identifier;
}
}
I'm keeping a record in my database to handle roles, so this class handles verifying with AD and fetching the user from the database:
public class AdminUserValidator : IAdminUserValidator
{
readonly IActiveDirectoryUserValidator _activeDirectoryUserValidator;
readonly IAdminUserFetcher _adminUserFetcher;
public AdminUserValidator(IAdminUserFetcher adminUserFetcher,
IActiveDirectoryUserValidator activeDirectoryUserValidator)
{
_adminUserFetcher = adminUserFetcher;
_activeDirectoryUserValidator = activeDirectoryUserValidator;
}
#region IAdminUserValidator Members
public AdminUser ValidateAndReturnAdminUser(string username, string clearTextPassword, string domain)
{
_activeDirectoryUserValidator.Validate(username, clearTextPassword, domain);
return _adminUserFetcher.GetAdminUser(1);
}
#endregion
}
And this class actually verifies that the username/password combination exist in Active Directory:
public class ActiveDirectoryUserValidator : IActiveDirectoryUserValidator
{
public void Validate(string username, string clearTextPassword, string domain)
{
using (var principalContext = new PrincipalContext(ContextType.Domain, domain))
{
// validate the credentials
bool isValid = principalContext.ValidateCredentials(username, clearTextPassword);
if (!isValid)
throw new Exception("Invalid username or password.");
}
}
}
I'm trying to get a System.Web.Security.MembershipUser user so I can check it's lastPasswordChangedDate property, only problem is it's returning null.
I've had a look around and most of the similar problems seem to be because the user is not authenticated when trying to create the object.
In my case the user is authenticated directly before calling the method, albeit in another class.
Any pointers?
PSEUDO-CODE FOLLOWS :
FrontEnd ---->
Login.cs
string userName = txtUser.Text; string password = txtPW.Text;
wsAuth = new wsAuth();
wsAuthentication.Authenticate(userName,password);
Auth service ---->
public bool Authenticate(string userName, string password)
{
SecurityProviderSettings settings = SecurityProviderSettings.GetFromConfiguration("SecurityProviders");
ProviderSetting providerSetting = settings.Providers[providerName];
object provider = Activator.CreateInstance(providerType);
return ((IAuthenticationProvider)provider).Authenticate(userName, password, providerSetting.CustomInfo, out failReason, out token);
}
Specific Provider ---->
public bool Authenticate(string userName, string password, out string failReason .....)
{
SqlMembershipProvider provider = new SqlMembershipProvider();
#region settings
//blablabla
#endregion
provider.initialize(/*input vars*/);
bool authenticated = provider.ValidateUser(userName,password);
if(authenticated)
{
pwValidator val = new pwValidator();
if(!val.IsInLifetime(userName,password))
{
failReason = "password expired";
}
}
}
PasswordValidator ---->
public bool IsInLifetime(string userName, string password)
{
MembershipUser user = Membership.GetUser(userName);
MembershipUser user2 = Membership.GetUser();
#bla bla bla
}
Try something like this.
Create a method to get the user
HttpContext httpContext = HttpContext.Current;
if (httpContext != null && httpContext.User != null && httpContext.User.Identity.IsAuthenticated)
{
return Membership.GetUser();
}