I am building a rather simple site with ASP.NET Core MVC 2.0 that is more or less an image gallery, just for me. I am not using any database so far. It is just a json file with metadata and the image files itself.
Now this site is supposed to get a hidden admin page where I (and only I) can upload new pictures.
What would be a simple but still secure way to add this admin page without having to introduce a full fledged user management to the site? I'd like to avoid to add a database and entity framework etc. to the site - there will only be one user.
In other words, what is a secure and simple way to add user management where there are is only one user that authenticates: Me, the admin.
Store a hashed version of your desired username/password in the appsettings.json and then rehash the values provided through the login screen and compare them.
Here's an example of how logging in could be accomplished. This bootstraps off of the default hasher present in Asp.Net Identity but you could use any hashing function.
You might want to create some other helpers too in case you want to reset the hashed password from your application versus having to go into the settings file.
appsettings.json
{
...
"LoginCredentials": {
"UsernameHash": "AQAAAAEAACcQAAAAENmv+riLvtTIa5wafXxzEX4rMSMXwVzG00q4jZKBI7Lx/oe2PFdqW1r521HBsL567g==",
"PasswordHash": "AQAAAAEAACcQAAAAEKwwppiixEQM9QO7hOXcoXXgIvHKs9QHRz1k0lAZ3noVwID2lv+I+Dwc9OheqDGFBA=="
}
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//Assuming services.AddIdentity<...>(...) is not added as a service
services.Configure<LoginCredentialOptions>(Configuration.GetSection("LoginCredentials"));
services.AddTransient<IPasswordHasher<User>, PasswordHasher<User>>();
...
}
LoginCredentialOptions.cs
public class LoginCredentialOptions
{
public string UsernameHash { get; set; }
public string PasswordHash { get; set; }
}
AccountController.cs
...
public async Task<IActionResult> Login([FromServices] IOptions<LoginCredentialOptions> loginCreds, LoginViewModel model, string returnUrl = null)
{
if (ModelState.IsValid)
{
var passwordResult = passwordHasher.VerifyHashedPassword(null, loginCreds.Value.PasswordHash, model.Password);
var usernameResult = passwordHasher.VerifyHashedPassword(null, loginCreds.Value.UsernameHash, model.Username);
if (passwordResult == PasswordVerificationResult.Success &&
usernameResult == PasswordVerificationResult.Success)
{
//Create identity cookie and sign in
RedirectToAction(nameof(Index), "Home");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
Related
I guess I am trying to mix two providers in project but I am looking to use websecurity in conjunction to my forms authentication. I need websecurity for OAUTH authentication using Facebook, and google.
The error that I am getting when I try to login using facebook is
To call this method, the Membership.Provider property must be an instance of ExtendedMembershipProvider.
Here are the code samples. How can I use both?
public ActionResult ExternalLoginCallback(string returnUrl)
{
AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
if (!result.IsSuccessful)
{
return RedirectToAction("ExternalLoginFailure");
}
if (OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false))
{
return RedirectToLocal(returnUrl);
}
if (User.Identity.IsAuthenticated)
{
// If the current user is logged in add the new account
OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, User.Identity.Name);
return RedirectToLocal(returnUrl);
}
else
{
// User is new, ask for their desired membership name
string loginData = OAuthWebSecurity.SerializeProviderUserId(result.Provider, result.ProviderUserId);
ViewBag.ProviderDisplayName = OAuthWebSecurity.GetOAuthClientData(result.Provider).DisplayName;
ViewBag.ReturnUrl = returnUrl;
return View("ExternalLoginConfirmation", new RegisterExternalLoginModel { UserName = result.UserName, ExternalLoginData = loginData });
}
}
and
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
Could possibly be related to the same issue as me MVC4 ExtendedMembershipProvider and entityframework
.. I removed the universal providers nuget package and this particular error dissappeared.
Also this "very recent" article by Jon Galloway may help.
If you are using Visual Studio, you might want to save yourself all this effort. The MVC 4 Internet template comes with four external identity providers out of the box. I have tested them and Google Accounts, Microsoft account, Facebook login, and Twitter login all work fine, with zero lines of code!
I think the same is provided with the Web Form template too.
More info at http://blogs.msdn.com/b/webdev/archive/2012/08/15/oauth-openid-support-for-webforms-mvc-and-webpages.aspx.
You can use an implementation of ExtendedMembershipProvider. For ex: the built-in SimpleMembershipProvider.
Every ExtendedMembershipProvider IS A MembershipProvider .
Read more at Jon Galloway's Blog.
I am working in Asp.net MVC and have a peculiar requirement for which I have no idea.
What I want is that when a user request a particular URL from my site, I want to visit some preset URL in the database and extract some data and bind them to View before rendering.
For example, If you visit mysite.com/Search/Index, then in my action method Index, i want to visit the anothersite.com/someparticular/url, extract the value in <div> with id="searclbl", bind it to my view and render the page.
I need to read the HTML because the sites I am working with don't offer any Web services or RSS.
Any sort of help or guidance in this matter is appreciated.
I believe you might be able to pull this off using HtmlAgilityPack (which can be installed via a NuGet package inside your project).
For example:
Let’s assume your Index View of the SearchController is strongly typed to the following ViewModel:
public class SearchViewModel
{
public string DivElement { get; set; }
//other properties...
}
This is the Index ActionResult():
public ActionResult Index()
{
var model = new SearchViewModel();
model.DivElement = GetDivFromWebSite();
return View(model);
}
The GetDivFromWebSite() method is where I use HtmlAgilityPack to fetch information from another web site and is defined like so:
private string GetDivFromWebSite()
{
var baseUrl = new Uri("http://www.anotherdomaine.com");
HtmlAgilityPack.HtmlDocument document = new HtmlDocument();
using (WebClient client = new WebClient())
{
document.Load(client.OpenRead(baseUrl));
}
if (document == null) return "nothing found!";
var div = document.DocumentNode.SelectNodes("//div[#id='missing-category']").FirstOrDefault();
return div.InnerHtml;
}
This might do the trick!
I have an asp.net MVC 3 system which different actions have different permissions.
All the permissions are managed using attributes that defines the required user permission.
I would also like to remove any buttons (or links) that the user is not permitted to click on.
Is there any way to do so without doing a lot of if-s in my views ?
Thanks.
Is there any way to do so without doing a lot of if-s in my views ?
You could write custom HTML helpers that will generate those buttons. For example:
#Html.Button("button text", "role1,role2");
The custom helper will check whether the current user posses one of the required roles and only in this case generate the corresponding button.
For example:
public static class HtmlExtensions
{
public static IHtmlString Button(this HtmlHelper htmlHelper, string buttonText, string roles)
{
var rolesSplit = (roles ?? string.Empty).Split(',');
var user = htmlHelper.ViewContext.HttpContext.User;
if (!user.Identity.IsAuthenticated || !rolesSplit.Any(user.IsInRole))
{
return MvcHtmlString.Empty;
}
var button = new TagBuilder("button");
button.SetInnerText(buttonText);
return new HtmlString(button.ToString());
}
}
For our web app I want to let developers create accounts using our api, when an account is created the URI has a subdomain. To accomplish this, do I have to have two separate API's because the URL is different.
For account creation: api.example.com/v1
For account usage: subdomain.example.com/api/v1/
we are using .net mvc3, can this be done with one set of api's and routes?
You should set up routing to get to the relevant controller+action as usual, and inside an action (or much better - using an ActionFilter), examine the subdomain and set the appropriate code that determines the who the user is, for further handling.
Example of a filter would be:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class SubdomainFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
int? userID = null;
Uri uri = filterContext.HttpContext.Request.Url;
if (uri != null)
{
string domain = uri.Host;
// do whatever you need analyzing the 'domain' variable... like getting the user id according to the subdomain. Let's say we discoveered that the user id is 1.
userID = 1;
}
if (filterContext.ActionParameters.ContainsKey("Subdomain"))
filterContext.ActionParameters["SubdomainUser"] = userID;
base.OnActionExecuting(filterContext);
}
}
and then your action would be:
[SubdomainFilter]
public virtual ActionResult GetUserName(int? userID)
{
...
}
All examples that I can find do something like this:
[Required]
public string Title { get; set; }
That's great for simple cases, but what about something that checks the database or something else server side?
For example, say I have a movie database and I want to allow people to rate it. How could I tell if someone has already rated a movie, as I'd only want them to rate a movie once.
I would think it would be something like:
public IEnumerable<string> ValidateUserHasNotAlreadyRatedMovie(User currentUser, Guid movieId)
{
if(movieHasAlreadyBeenRated)
{
yield return "Movie been rated man!";
}
}
Now then I'd call this with something like:
var errors = new List<string>();
errors.AddRange(ValidateUserHasNotAlreadyRatedMovie(topic, comment));
if(errors.Any())
{
throw new SomeTypeOfCustomExtension??(errors);
}
Do I just need to extend Exception for a custom SomeTypeOfCustomExtension above, or is there something already built? Am I doing this the ASP.NET MVC 2 way?
After that how do I put it into the model state and let the view know something's wrong?
See this it may help
Remote Validation with ASP.NET MVC 2
http://bradwilson.typepad.com/blog/2010/01/remote-validation-with-aspnet-mvc-2.html
The ASP.NET 2.0 MVC way of doing custom validation is described here. It basically shows how to write a custom validation attribute. Here is an example of one such custom attribute that checks the database for name uniqueness:
public class UniqueNameAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
string str = (string)value;
if (String.IsNullOrEmpty(str))
return true;
using (XDataContext vt = new XDataContext())
{
return !(vt.Users.Where(x => x.Username.Equals(str)).Any());
}
}
}
The ASP.NET 1.0 way (that doesn't require custom attributes) is described here.