Setting specific authentication type with mvc 5 - authentication

ASP.NET comes default with the external login provider partial page which is set as:
var loginProviders = Context.GetOwinContext().Authentication.GetExternalAuthenticationTypes();
if (loginProviders.Count() == 0)
{
<div>
<p>
There are no external authentication services configured. See this article
for details on setting up this ASP.NET application to support logging in via external services.
</p>
</div>
}
else
{
using (Html.BeginForm("ExternalLogin", "Account", new { ReturnUrl = Model.ReturnUrl }))
{
#Html.AntiForgeryToken()
<div id="socialLoginList">
<p>
#foreach (AuthenticationDescription p in loginProviders)
{
<button type="submit" class="btn btn-default" id="#p.AuthenticationType" name="provider" value="#p.AuthenticationType" title="Log in using your #p.Caption account">#p.AuthenticationType</button>
}
</p>
</div>
}
}
What if i know exactly the authentication type i am going to use, say facebook? How can i set that up?

Related

Authorize with roles is not working in .NET 5.0 Blazor Client app

I have a .NET 5.0 Blazor client app and I am unable to get the [Authorize(Roles="Admin")] and AuthorizeView tag to work.
I have scaffolded identity pages as well:
I am using a custom identity implementation that uses Cosmos Db: https://github.com/pierodetomi/efcore-identity-cosmos
I know that Authorization with roles in the Blazor client project template is an issue: https://github.com/dotnet/AspNetCore.Docs/issues/17649#issuecomment-612442543
I tried workarounds as mentioned in the above Github issue thread and the following SO answer: https://stackoverflow.com/a/64798061/6181928
...still, I am unable to get it to work.
Ironically, the IsInRoleAsync method is not even called after logging in to the application. I have applied a breakpoint on its implementation in the custom CosmosUserStore class and it doesn't get hit.
The browser console shows this after logging in to the application with the admin user:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddCosmosIdentity<MyDbContext, IdentityUser, IdentityRole>(
// Auth provider standard configuration (e.g.: account confirmation, password requirements, etc.)
options => options.SignIn.RequireConfirmedAccount = true,
options => options.UseCosmos(
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
databaseName: "xxxxxxxxxxxxxxxxxxxxxxxxx"
),
addDefaultTokenProviders: true
).AddDefaultUI().AddRoles<IdentityRole>();
services.AddScoped<IUsersRepository, UsersRepository>();
services.AddIdentityServer().AddApiAuthorization<IdentityUser, MyDbContext>(options =>
{
options.IdentityResources["openid"].UserClaims.Add("role");
options.ApiResources.Single().UserClaims.Add("role");
});
// Need to do this as it maps "role" to ClaimTypes.Role and causes issues
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddControllersWithViews();
services.AddRazorPages();
}
Program.cs
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services.AddHttpClient("IdentityDocApp.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
// Supply HttpClient instances that include access tokens when making requests to the server project
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("IdentityDocApp.ServerAPI"));
builder.Services.AddHttpClient();
builder.Services.AddScoped<IManageUsersService, ManageUsersService>();
builder.Services.AddBlazorTable();
builder.Services.AddApiAuthorization();
builder.Services.AddApiAuthorization(options =>
{
options.UserOptions.RoleClaim = "role";
});
await builder.Build().RunAsync();
}
}
App.razor
NavMenu.razor
<div class="#NavMenuCssClass" #onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<AuthorizeView Roles="Admin">
<li class="nav-item px-3">
<NavLink class="nav-link" href="users">
<span class="oi oi-person" aria-hidden="true"></span> Users
</NavLink>
</li>
</AuthorizeView>
</ul>
ManageUsers.razor
ManageUsersController
The database has the right data in the UserRoles collection. No issues there.
So, what could be the issue? What am I doing wrong?
Update:
It is embarrassing but my IsInRoleAsync implementation in the custom user store was not correct. As soon as I fixed it the issue was gone.
I am only using the following code in the Startup.cs of the server side:
services.AddIdentityServer()
.AddApiAuthorization<IdentityUser, MyDbContext>(options =>
{
options.IdentityResources["openid"].UserClaims.Add("name");
options.ApiResources.Single().UserClaims.Add("name");
options.IdentityResources["openid"].UserClaims.Add("role");
options.ApiResources.Single().UserClaims.Add("role");
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");
In the Program.cs of client-side I am only using builder.Services.AddApiAuthorization();
Thanks to #MrC aka Shaun Curtis for letting me know that the issue lied on the server-side.
Paste this into your Index page so you can see the information for your user:
#if (user is not null)
{
<h3>#user.Identity.Name</h3>
<div class="m-2 p-2">
Is Authenticated: #user.Identity.IsAuthenticated
</div>
<div class="m-2 p-2">
Authentication Type: #user.Identity.AuthenticationType
</div>
<div class="m-2 p-2">
Admin Role: #user.IsInRole("Admin")
</div>
<div class="m-2 p-2">
<h5>Claims</h5>
#foreach (var claim in user.Claims)
{
<span>
#claim.Type
</span>
<span>:</span>
<span>
#claim.Value
</span>
<br />
}
</div>
}
else
{
<div class="m-2 p-2">
No User Exists
</div>
}
#code {
[CascadingParameter] public Task<AuthenticationState> AuthTask { get; set; }
private System.Security.Claims.ClaimsPrincipal user;
protected async override Task OnInitializedAsync()
{
var authState = await AuthTask;
this.user = authState.User;
}
}
You should get something like this:
This shows which roles have been passed in the authentication data in the header from the authentication provider. This should include role.
Update
Remove:
// Need to do this as it maps "role" to ClaimTypes.Role and causes issues
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

How do I build an Asp.Net Core Razor page control that allows users to sign their names?

I am building an Asp.Net Core web application using Razor.
The intended audience for this app will be using it on tablets.
Part of the application consists of several pages/forms that will require user signatures.
We could retrieve an image of a user's signature and display that on demand in the web page.
Is it possible to be more interactive and allow users to "sign" the form/page within the browser? Are there any 3rd party control libraries that would support this functionality?
I pretty sure this can be done on native applications, but can I achieve this through Asp.Net Core?
I found signature_pad in github, and it works for me.
You can take a look at the screenshots of my test steps first, and I will add the test code at the bottom.
Test Code
1. signature.cshtml
#*
For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
*#
<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/signature_pad#2.3.2/dist/signature_pad.min.js"></script>
<form method="POST">
<p>
<canvas width="500" height="400" id="signature"
style="border:1px solid black"></canvas><br>
<button type="button" id="accept"
class="btn btn-primary">
Accept signature
</button>
<button type="submit" id="save"
class="btn btn-primary">
Save
</button><br>
<img width="500" height="400" id="savetarget"
style="border:1px solid black"><br>
<input id="SignatureDataUrl" type="text">
</p>
</form>
<script>
$(function () {
var canvas = document.querySelector('#signature');
var pad = new SignaturePad(canvas);
$('#accept').click(function () {
var data = pad.toDataURL();
$('#savetarget').attr('src', data);
$('#SignatureDataUrl').val(data);
pad.off();
});
$('#save').click(function () {
$.ajax({
url: "/ForTest/get_signature",
type: "POST",
data: { base64png:$('#SignatureDataUrl').val()},
success: function (data) {
console.log("success");
},
error: function (hata, ajaxoptions, throwerror) {
alert("failed");
}
});
});
});
</script>
2. C# code
[HttpPost]
public string get_signature(string base64png) {
var dataUri = base64png;//"data:image/png;base64,iVBORw0K...";
var encodedImage = dataUri.Split(',')[1];
var decodedImage = Convert.FromBase64String(encodedImage);
System.IO.File.WriteAllBytes("signature_pic/"+DateTime.Now.ToString("yyyyMMddHHmmss")+"signature.png", decodedImage);
return "ok";
}
Tips
If you want test my code, you need create signature_pic folder like me.

in asp.net core Component #OnClick not trigger target method

I try to use my repository data to display in card view, then press button to see more information about the item, it does not work. #OnClick is only working for JSON data
#using Microsoft.AspNetCore.Components.Web - to access #onclick and more option
repoItem - my ItemRepository for get data from database
#OnClick="(e => SelectProduct(item.Id))" - when i click item card, its shoud get item id send to SelectProduct(item.Id) method.
but it work for following link. he works with JSON data but I need to work for model data.
https://github.com/dotnet-presentations/ContosoCrafts/blob/master/src/Components/ProductList.razor
<div class="card-columns">
#foreach (var item in repoItem.GetAll())
{
<div class="card">
<div class="card-header">
<h5 class="card-title">#item.Name</h5>
</div>
<div class="card-body">
<h5 class="card-title"> Total available items : #item.Quantity</h5>
<h5 class="card-title">Price : Rs. #item.Price.00</h5>
</div>
<div class="card-footer">
<small class="text-muted">
<button #onclick="(e => SelectProduct(item.Id))"
data-toggle="modal" data-target="#productModal" class="btn btn-primary">
More Info
</button>
</small>
</div>
</div>
}
</div>
#code {
Item selectedItem;
int selectedItemId;
void SelectProduct(int productId)
{
selectedItemId = productId;
selectedItem = _context.Items.Where(x => x.Id == selectedItemId).FirstOrDefault();
ContItem();
}
int itemcnt = 0;
string cuntLable;
void ContItem()
{
if (selectedItem.Quantity != null || selectedItem.Quantity != 0)
{
itemcnt = selectedItem.Quantity;
cuntLable = itemcnt.ToString();
}
else cuntLable = "Not available ..!";
}
}
problem: #onclick=".." is not hit my selectprodect method breakpoint when clicking the button.
Solution: the mistake is Statup.cs need to add services.AddServerSideBlazor() in ConfigureServices and then add in Configure part endpoints.MapBlazorHub()
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
endpoints.MapBlazorHub();
});
after 6 hours of hard work. #onclick is worked smoothly

Web API authentication turns request.authentication false

I have a project that I created in ASP.NET MVC and now the second part of the work is pass the logic of the ASP.NET MVC application (basically the database) to an ASP.NET Web API and do the connection with them.
The thing is, I already did the connection and I already save values in the Web API database, but a strange thing is happening.
I have markup in my layout.cs.html file:
<body>
#if (User.Identity.IsAuthenticated)
{
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
#Html.ActionLink("MSDiary", "Index", "Home", new { area = "" }, new { #class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>#Html.ActionLink("Despesas", "Index", "Despesas")</li>
<li>#Html.ActionLink("Rendimentos", "Index", "Rendimentos")</li>
<li>#Html.ActionLink("Tipos de Despesa", "Index", "TipoDespesas")</li>
<li>#Html.ActionLink("Tipos de Pagamento", "Index", "TipoPagamentos")</li>
<li>#Html.ActionLink("Tipos de Rendimento", "Index", "TipoRendimentos")</li>
</ul>
#Html.Partial("_LoginPartial")
</div>
</div>
</div>
}
<div class="container body-content">
#RenderBody()
<footer>
#if (Request.IsAuthenticated)
{
<p>#Html.Action("_ObtemSaldo", "Home")</p>
}
</footer>
</div>
#Scripts.Render("~/bundles/bootstrap")
#RenderSection("scripts", required: false)
</body>
</html>
That request is authenticated shows the navbar on the top or not depending if the user is authenticated I cut it off to see if in fact my program is getting the user, and it kinda get the user so the problem is not with the connection, but the request is authenticated don't change in the controller maybe :S
Here is my login controller:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
try
{
var client = WebApiHttpClient.GetClient();
string username = model.Email;
string password = model.Password;
HttpContent content = new StringContent(
"grant_type=password&username=" + username + "&password=" + password,
System.Text.Encoding.UTF8,
"application/x-www-form-urlencoded");
var response = await client.PostAsync("/Token", content);
if (response.IsSuccessStatusCode)
{
TokenResponse tokenResponse =
await response.Content.ReadAsAsync<TokenResponse>();
WebApiHttpClient.storeToken(tokenResponse);
// return Content(tokenResponse.AccessToken);
return RedirectToAction("Index", "Home");
}
else
{
return Content("Ocorreu um erro: " + response.StatusCode);
}
}
catch
{
return Content("Ocorreu um erro.");
}
}
I already tried with user.authenticated still doesn't work if someone can give me a hand I would appreciate it a lot :D
ps: Sorry for my bad English
If I understood, you have an MVC Application and you have passed some logic (database access) to an Web Api project, so when you send a form/request to the server, it will be received by the controller from MVC, and after that the request will be send to the WebApi (at least is what I understood from your code).
Your problem is that the user logs into the application, the MVC Controller goes to the WebApi to authenticate the user and afterwards, even the login and password been correct the MVC (View) still considers that the user is not logged in.
Well, if what I described is correct, seen your code, I would say that the user is indeed being authenticate in the Web Api, however, as the MVC is the direct interface with the user, it is missing set the MVC Application User as authenticated through some authentication method, something like:
FormsAuthentication.SetAuthCookie(username, false);
It´s worth to say that you would have to store the token (from webapi, after user has been authenticated) so that the nexts requests to the WebApi it considers the user authenticated to that specific token.
Hope I´ve helped.
Regards,

Validation in ASP.Net MVC4 for unbound textboxes

I have the following code for a view, in which I am submitting some data from 3 different text-boxes. This is just a practice sample that I created. I am using Razor syntax with MVC4 in ASP.Net.
My question: How can I validate the text-boxes so they are always required?
#{
ViewBag.Title = "HelloWorld";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>HelloWorld</h2>
<div>This is a sample Hello World page</div>
<h1>#ViewBag.Title</h1>
<h2>#ViewBag.Message</h2>
#using (Html.BeginForm("HandleSubmit", "Home"))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<fieldset>
<legend>Registration Form</legend>
<ol>
<li>
#Html.Label("username", "UserName")
#Html.TextBox("username")
</li>
<li>
#Html.Label("pwd", "Password")
#Html.Password("pwd")
</li>
<li>
#Html.Label("cpwd", "Confirm Password")
#Html.Password("cpwd")
</li>
</ol>
<input type="submit" value="TestPost" />
<div style="color:red;font-weight:bold">#ViewBag.Feedback</div>
</fieldset>
}
UPDATE 1: Another way to validate textbox and show a custom invalid message is as below.
#Html.TextBox("username", null, new { #required = "required",
#oninvalid = "this.setCustomValidity('This data is a must')" })
Set the required attribute in the html attributes parameter for the helper
#Html.TextBox("username", null, new { #required="required", #oninvalid="setCustomValidity('I\'m required')" })
To customize this and get the same effect as the MVC templates you need jquery.validate.js and jquery.validate.unobtrusive.js
#Html.TextBox("username", null, new { #data_val_required="I'm required", #data_val="true" })
<span data-valmsg-replace="true" data-valmsg-for="username"></span>