Multiple objects to single endpoint with asp.net - asp.net

I'm building a Restful API with asp.net core. I have an endpoint that are used to authenticate users. I have two kinds of Users where one is an Admin and one is "FilmStudio". I succesfully managed to authenticte the User (Admin) but i also need to be able to authenticate the FilmStudio with username and password. Is there anyway I can do this with a single endpoint?
This is my endpoint form the UsersController:
[AllowAnonymous]
[HttpPost("Authenticate")]
public IActionResult Authenticate([FromBody] UserDto model)
{
var user = _userRepo.Authenticate(model.UserName, model.Password);
if (user !=null)
{
if (user == null)
{
return BadRequest("The username or password is incorrect.");
}
return Ok(new
{
Id = user.UserId,
Username = user.UserName,
Role = user.Role,
Token = user.Token
});
}
else
{
var filmStudioDto = new FilmStudioDto();
var studio = _studioRepo.Authenticate(model.Name, model.Password);
if (studio == null)
{
return BadRequest("The username or password is incorrect.");
}
return Ok(new
{
Id = studio.StudioId,
Username = studio.Name,
City = studio.City,
Role = studio.Role,
Token = studio.Token
});
}
}
}
When im giving the username and password for the admin user it works. However when im trying to enter the username and passwod for FilmStudio I allways get the error messsage that says: "The username or password is incorrect."

[AllowAnonymous]
[HttpPost("Authenticate")]
public IActionResult Authenticate([FromBody] UserDto model)
{
if (model.UserName != null) // Check if UserName is null or not
{
var user = _userRepo.Authenticate(model.UserName, model.Password);
if (user == null)
{
return BadRequest("The username or password is incorrect.");
}
return Ok(new
{
Id = user.UserId,
Username = user.UserName,
Role = user.Role,
Token = user.Token
});
}
else
{
var studio = _studioRepo.Authenticate(model.StudioName, model.StudioPassword);
if (studio == null)
{
return BadRequest("The username or password is incorrect.");
}
return Ok(new
{
Id = studio.StudioId,
Username = studio.Name,
City = studio.City,
Role = studio.Role,
Token = studio.Token
});
}
}
}

you can try to use instead of model.Name model.UserName for studio too
else
{
var studio = _studioRepo.Authenticate(model.UserName, model.Password);
if (studio == null)
return BadRequest("The username or password is incorrect.");
return Ok( new FilmStudioDto
{
Id = studio.StudioId,
Username = studio.Name,
City = studio.City,
Role = studio.Role,
Token = studio.Token
});
}
and IMHO you can fix user part too
if (user !=null)
return Ok(new UserDto
{
Id = user.UserId,
Username = user.UserName,
Role = user.Role,
Token = user.Token
});

Related

Obtain user email from Mastodon API via OAuth

Is it possible to obtain user email through Mastodon Api? I'm working on adding OAuth authentication via Mastodon Api but only seem to get "id" and "display_name" using "/api/v1/accounts/verify_credentials" endpoint. I do not see a property returned for email so currently just using "acct" parameter. I'm using both "read:accounts" and "admin:read:accounts" scopes. This is for an NetCore application.
builder.Services.AddAuthentication()
.AddMicrosoftAccount("Microsoft", "Microsoft", microsoftOptions =>
{
microsoftOptions.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
microsoftOptions.ClientId = _appSettings.Authentication.Microsoft.ClientId;
microsoftOptions.ClientSecret = _appSettings.Authentication.Microsoft.ClientSecret;
})
.AddGoogle("Google", "Google", googleOptions =>
{
googleOptions.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
googleOptions.ClientId = _appSettings.Authentication.Google.ClientId;
googleOptions.ClientSecret = _appSettings.Authentication.Google.ClientSecret;
})
.AddGitHub("GitHub", "GitHub", githubOptions =>
{
githubOptions.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
githubOptions.ClientId = _appSettings.Authentication.GitHub.ClientId;
githubOptions.ClientSecret = _appSettings.Authentication.GitHub.ClientSecret;
})
.AddOAuth("Fosstodon", "Fosstodon", fosstodonOptions =>
{
fosstodonOptions.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
fosstodonOptions.ClientId = _appSettings.Authentication.Fosstodon.ClientId;
fosstodonOptions.ClientSecret = _appSettings.Authentication.Fosstodon.ClientSecret;
fosstodonOptions.CallbackPath = new PathString("/signin-fosstodon");
fosstodonOptions.AuthorizationEndpoint = _appSettings.Authentication.Fosstodon.AuthorizationEndpoint;
fosstodonOptions.TokenEndpoint = _appSettings.Authentication.Fosstodon.TokenEndpoint;
fosstodonOptions.UserInformationEndpoint = _appSettings.Authentication.Fosstodon.UserInformationEndpoint;
fosstodonOptions.SaveTokens = true;
fosstodonOptions.Scope.Add("read:accounts");
fosstodonOptions.Scope.Add("admin:read:accounts");
fosstodonOptions.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
fosstodonOptions.ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
fosstodonOptions.ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
fosstodonOptions.Events = new OAuthEvents
{
OnCreatingTicket = async context =>
{
var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
var response = await context.Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, context.HttpContext.RequestAborted);
response.EnsureSuccessStatusCode();
var user = JObject.Parse(await response.Content.ReadAsStringAsync());
var identifier = user.Value<string>("id")?.Clean();
if (!string.IsNullOrEmpty(identifier))
{
context.Identity?.AddClaim(new Claim(
ClaimTypes.NameIdentifier, identifier,
ClaimValueTypes.String, context.Options.ClaimsIssuer));
}
var userName = user.Value<string>("display_name")?.Clean();
if (!string.IsNullOrEmpty(userName))
{
context.Identity?.AddClaim(new Claim(
ClaimTypes.Name, userName,
ClaimValueTypes.String, context.Options.ClaimsIssuer));
}
var userEmail = user.Value<string>("acct")?.Clean();
if (!string.IsNullOrEmpty(userEmail))
{
context.Identity?.AddClaim(new Claim(
ClaimTypes.Email, userEmail,
ClaimValueTypes.String, context.Options.ClaimsIssuer));
}
}
};
});
Really wanting to know if their is an endpoint that will return current user email address. I have looked through Mastodon Api documentation but not seeing and enpoint for this.
Found that it is not posible to obtain user email through Mastodon Api. Instead decided to handel this situation in the registration process of application authentication flow. Basicly check if a user exists with given email. If so then remove the auto provision user and add external provider information to existing user. Later will submit verify email, to email address provided, to avoid someone from stealing user account through registration.
/// <summary>
/// Handle postback from new registration
/// </summary>
/// <returns>IActionResult</returns>
[AllowAnonymous]
[HttpPost("Registration/New")]
[HttpPost("Registration/New/{id?}")]
[ValidateAntiForgeryToken]
public virtual async Task<IActionResult> New([Bind(RegistrationViewModel.BindProperties)] RegistrationViewModel model, [FromForm(Name = "Button")] string button)
{
// Check if cancled
if (button.Clean() != "submit")
return RedirectToAction("Index", "Home");
// Check email is valid
if (!model.Email.Clean().IsValidEmail())
ModelState.AddModelError(nameof(model.Email), _sharedLocalizer["ErrorMessage.Invalid"]);
if (ModelState.IsValid)
{
// setup results
IdentityResult identityResult = new IdentityResult();
// Check for existing user
ApplicationUser user = await _userManager.FindByEmailAsync(model.Email.Clean());
if (user != null)
{
if (user.Id != model.Id.Clean())
{
ApplicationUser removeUser = await _userManager.FindByIdAsync(model.Id.Clean());
if (removeUser != null)
{
identityResult = await _userManager.DeleteAsync(removeUser);
if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description);
}
}
}
else
{
user = await _userManager.FindByIdAsync(model.Id.Clean());
if (user == null)
throw new KeyNotFoundException($"[Key]: {nameof(model.Id)} [Value]: {model.Id}");
}
user.DisplayName = model.DisplayName.Clean();
user.UserName = model.Email.Clean();
user.NormalizedUserName = model.Email.Clean();
user.Email = model.Email.Clean();
user.NormalizedEmail = model.Email.Clean();
identityResult = await _userManager.UpdateAsync(user);
if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description);
if (!string.IsNullOrEmpty(model.ProviderUserId.Clean()))
{
var userLogins = await _userManager.GetLoginsAsync(user);
UserLoginInfo? userLogin = userLogins
.Where(x => x.LoginProvider == model.Provider.Clean())
.Where(x => x.ProviderKey == model.ProviderUserId.Clean())
.FirstOrDefault();
if (userLogin == null)
{
identityResult = await _userManager.AddLoginAsync(user, new UserLoginInfo(model.Provider.Clean(), model.ProviderUserId.Clean(), model.Provider.Clean()));
if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description);
}
}
}
return View(model);
}

MVC custom login authentication

Hi I'm developing an app in MVC and I have a problem with login, I want to know how can I manage the login depending on the user role.
While the moment the login works fine but I need to identify the role user for sending to different pages
I have a table in my database call Employee and one column is call IdPosition that is referred to another table call Position.
Here is my code
[HttpPost]
public ActionResult Autorizacion(Pepitos.Models.Employee employee)
{
using (pepitosEntities db = new pepitosEntities())
{
var userDetails = db.Employees.Where(x => x.Username == employee.Username && x.Password == employee.Password).FirstOrDefault();
if (userDetails == null)
{
employee.ErrorLoginMensaje = "Username or Password incorrect";
return View("Login",employee);
}
else
{
Session["IdEmployee"] = userDetails .IdEmployee;
Session["name"] = userDetails.Name;
return RedirectToAction("EmployeesIndex", "EmployeesHome");
}
}
}
Now what you need to do is check the role after the username and password matches and then redirect accordingly.for that i assumed you have role column in your database table along with username and password.
using (pepitosEntities db = new pepitosEntities())
{
var userDetails = db.Employees.Where(x => x.Username == employee.Username && x.Password == employee.Password).FirstOrDefault();
if (userDetails == null)
{
employee.ErrorLoginMensaje = "Username or Password incorrect";
return View("Login",employee);
}
else
{
var userRole=userDetails.role; //get the role of the user i.e whether user is admin or any other role
if(userRole=="Admin")
{
Session["IdEmployee"] = userDetails .IdEmployee;
Session["name"] = userDetails.Name;
return RedirectToAction("EmployeesIndex","EmployeesHome");
}
else if(userRole=="User")
{
Session["IdUser"] = userDetails .IdUser;
Session["name"] = userDetails.Name;
return RedirectToAction("UserIndex","UserHome");
}
//and so on
}
}
hope it helps!

Prevent multiple login in asp.net MVC 4 application

A system need single user login at a time. If tried for multiple login simultaneously the user get blocked. I have used Cookie Authentication which will manage from client browser.
Login Code:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel oLoginViewModel)
{
try
{
bool Result = new UserBL().ValidateUser(oLoginViewModel.UserName, oLoginViewModel.Password);
if (Result == true)
{
FormsService.SignIn(oLoginViewModel.UserName, oLoginViewModel.RememberMe);
CreateAuthenticationTicket(oLoginViewModel.UserName);
return RedirectToLocal(Request.Form["returnUrl"]);
}
else
ViewBag.Error = "Invalid Username or Password / Due to simultaneous login you get blocked.";
return View();
}
catch (Exception ex)
{
throw ex;
}
}
public void CreateAuthenticationTicket(string username)
{
Users oUsers = new Users();
oUsers.Email = username;
oUsers.Role = "User";
int sessionid = new UserBL().GetByUserName(username).UserId;
string userData = JsonConvert.SerializeObject(oUsers);
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1,
username,
DateTime.Now,
DateTime.Now.AddYears(1), // value of time out property
false, //pass here true, if you want to implement remember me functionality
userData);
string encTicket = FormsAuthentication.Encrypt(authTicket);
var isSsl = Request.IsSecureConnection; // if we are running in SSL mode then make the cookie secure only
HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket)
{
HttpOnly = false,
Secure = isSsl,
};
faCookie.Expires = DateTime.Now.AddYears(1);
Response.Cookies.Add(faCookie);
//Login Repository Entry
LoginsRepository oLogin = new LoginsRepository();
oLogin.UserName = username;
oLogin.SessionId = sessionid.ToString();
oLogin.LoggedIn = true;
oLogin.CreatedOn = Utility.CommonFunction.DateTime_Now();
oLogin.IPAddress = HttpContext.Request.RequestContext.HttpContext.Request.ServerVariables["REMOTE_ADDR"];
oLogin.Status = En_LoginStatus.SingleUser.ToString();
new LoginRepositoryBL().Add(oLogin);
}
I'm saving every user login with their IP Address to check the user multiple login.
After login it redirects to home controller and their I checked the multiple logins logic from database table Loginsrepository which is mentioned above :
public class HomeController : CustomerBaseController
{
public ActionResult Index()
{
Users oUser = new Users();
oUser = new UserBL().getActiveUser();
// check to see if your ID in the Logins table has
// LoggedIn = true - if so, continue, otherwise, redirect to Login page.
if (new LoginRepositoryBL().IsYourLoginStillTrue(System.Web.HttpContext.Current.User.Identity.Name, oUser.UserId.ToString()))
{
// check to see if your user ID is being used elsewhere under a different session ID
if (!new LoginRepositoryBL().IsUserLoggedOnElsewhere(System.Web.HttpContext.Current.User.Identity.Name, oUser.UserId.ToString()))
{
Answers oAnswer = new Answers();
return View(oAnswer);
}
else
{
// if it is being used elsewhere, update all their
// Logins records to LoggedIn = false, except for your session ID
new LoginRepositoryBL().LogEveryoneElseOut(System.Web.HttpContext.Current.User.Identity.Name, oUser.UserId.ToString());
Answers oAnswer = new Answers();
return View(oAnswer);
}
}
else
{
oUser = new UserBL().GetByUserName(System.Web.HttpContext.Current.User.Identity.Name);
oUser.Status = En_Status.Inactive.ToString();
new UserBL().update(oUser);
FormsService.SignOut();
FormsAuthentication.SignOut();
return RedirectToAction("Login", "Account");
}
}
}
Above methods :
public bool IsYourLoginStillTrue(string userId, string sid)
{
try
{
using (var ctx = new CnSiteEntities())
{
IEnumerable<LoginsRepository> logins = (from i in ctx.LoginsRepository
where i.LoggedIn == true &&
i.UserName == userId && i.SessionId == sid
select i).AsEnumerable();
return logins.Any();
}
}
catch (Exception)
{
throw;
}
}
public bool IsUserLoggedOnElsewhere(string userId, string sid)
{
try
{
using (var ctx = new CnSiteEntities())
{
IEnumerable<LoginsRepository> logins = (from i in ctx.LoginsRepository
where i.LoggedIn == true &&
i.UserName == userId && i.SessionId != sid
select i).AsEnumerable();
return logins.Any();
}
}
catch (Exception)
{
throw;
}
}
public void LogEveryoneElseOut(string userId, string sid)
{
try
{
using (var ctx = new CnSiteEntities())
{
IEnumerable<LoginsRepository> logins = (from i in ctx.LoginsRepository
where i.LoggedIn == true &&
i.UserName == userId &&
i.SessionId != sid // need to filter by user ID
select i).AsEnumerable();
foreach (LoginsRepository item in logins)
{
item.LoggedIn = false;
}
ctx.SaveChanges();
}
}
catch (Exception)
{
throw;
}
}
It's not working properly. It keeps it true after login even if multiple simultaneous logins. I have googled it and tried it much but I didn't get any solution.

Cannot Get ID of user after login using User.Identity.GetUserId()

I am using Microsoft.AspNet.Identity in my application.
The user can Log In and can only see his/her own controller.
Because I've added attributes ([Authorize(Roles = "1")]) and this works fine as well.
But I can't get the user Id by using string users = User.Identity.GetUserId();.
users is always null, and I don't know why.
I am not using default login template.
And one more thing, after login I've done something like this
if (User.IsInRole("1"))
{
return RedirectToAction("Dashboard", "Supplier");
}
else if (User.IsInRole("2"))
{
return RedirectToAction("Dashboard", "Site");
}
but it does not work as well. But when I login and go to controller which has Authorize(Roles ="1") in controller, it does not give any error or redirect to login page.
But when I login with the user (which has Roles="2"), I cannot access the controller with Authorize(Roles="1").
This is how I have configured ASP.Net Identity:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
ExpireTimeSpan = TimeSpan.FromMinutes(5),
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Login/Index") });
}
}
}
Update
For Redirection based on Role I've updated my Controller/Action.
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginView login)
{
if (ModelState.IsValid)
{
var user = (from u in db.logins
where u.user_name == login.user_name && u.user_password == login.user_password && u.isactive == 1
select new
{
u.user_name,
u.login_id,
u.user_type,
u.isactive
});
if (user.FirstOrDefault() != null)
{
FormsAuthentication.SetAuthCookie(login.user_name, true);
Session["UserName"] = user.FirstOrDefault().user_name;
string userId = User.Identity.GetUserId();
return RedirectToAction("RedirectToDefault");
}
else
{
ViewBag.error = "User Does Not Exist";
return View(login);
}
}
else
{
ModelState.AddModelError("", "Invalid Credentials");
}
return View(login);
}
And the in RedirectToDefault I've added:
public ActionResult RedirectToDefault()
{
String[] roles = Roles.GetRolesForUser();
string id = User.Identity.GetUserId(); //<- this is null here as well.
string name = User.Identity.GetUserName();
if (roles.Contains("1"))
{
return RedirectToAction("Index", "Supplier");
}
else if (roles.Contains("2"))
{
return RedirectToAction("Index", "Site");
}
and so on..
Any help would be much Appreciated.
From your incomplete code it looks like you are doing everything manually, selecting users from the db and so on.
Try using the SignInManager:
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginView login)
{
if (ModelState.IsValid)
{
var result = await SignInManager.PasswordSignInAsync(login.Username, login.Password, false, shouldLockout: true);
if (result == SignInStatus.Success)
{
//FormsAuthentication.SetAuthCookie(login.user_name, true);
//Session["UserName"] = user.FirstOrDefault().user_name;
//string userId = User.Identity.GetUserId();
//These shouldn't be neede anymore
return RedirectToAction("RedirectToDefault");
}
else
{
ViewBag.error = "User Does Not Exist";
return View(login);
}
}
else
{
ModelState.AddModelError("", "Invalid Credentials");
}
return View(login);
}
I would suggest that you read up on how Identity works, as it does everything for you, however you need to know how to access the data.
You can get user id through UserManager like:
var user = await UserManager.FindAsync(username, password);
Hope this helps someone.

Working on Sign up page with ASP .net

I'm an intern developer and learning to code in ASP .net with mvc framework.
I'm working on a sign up page for a website where the users have to click on the link provided to validate their account with a website while registering their account.
Now, if the users have not clicked on the link which was sent to them in an email while registering then the user will not have a valid account and for those users I want that this message should be displayed "Please confirm your link to activate your account".
For that my code is as below but this code is giving me errors while I run it (looks like it is not going into the else condition.
My code is as below and is not working. Can someone please help me on this-
public ActionResult Login(LoginModel model, string returnUrl)
{
Session["CheckAmountOfSites"] = true;
ViewBag.SuggestBrowser = false;
if (ModelState.IsValid)
{
try
{
model.UserName = model.UserName.Trim().ToLower();
if (ValidateUser(model.UserName, model.Password))
{
if (activated == true)
{
FormsAuthentication.SetAuthCookie(model.UserName.ToLower(), model.RememberMe);
--Some more stuffs in if condition but have not been pasted completely here for --security reasons
} }
This is my else condition--But somehow its not entering the else part.
//validate user is not true
else
{
//ModelState.AddModelError("", "The Email or Password provided is incorrect.");
if (activated == false)
{
return RedirectToAction("Confirm", "Account", new { userName = model.UserName });
}
}
Confirm action--
[AllowAnonymous]
public ActionResult Confirm( string userName)
{
using (var db = SiteUtil.NewDb)
{
var user = db.Users.Where(n => n.Username == userName && n.CanLogin == false).FirstOrDefault();
if (user.CanLogin == false)
{
ViewBag.Email = "Please confirm your mail to activate your account";
}
}
return View();
}
public bool ValidateUser(string userName, string password)
{
using (var db = SiteUtil.NewDb)
{
var user = db.Users.Where(n => n.Username == userName && n.IsActive).FirstOrDefault();
if (user == null)
{
return false;
}
if (user.CanLogin == false)
{
//Mod("Email", "please confirm the mail which was sent to you");
//ViewBag.Email = "please confirm the mail which was sent to you";
return true;
}

Resources