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,
Related
Environment: ASP.NET Core 6.0, Razor Pages
Authentication:
The users log in, and their access token (Bearer jwt) is stored in Session storage.
The problem:
I want to display an array of objects that a request gives me. These objects have a part that has to be queried separately for each of them from the backend. For presentation purposes, I want to display them on the same page, but I do not want to query them before the page loads, because that is slow.
So the question is, how to access HttpClient (or the service that uses HttpClient and handles requests) from jQuery? My current idea is to pass the Bearer token to the partials, that way it can be accessed from them, and to go with an XMLHttpRequest. Are there any better solutions to populate these dropdowns on demand?
Index.cshtml
#page
#model IndexModel
#foreach (var part in Model.Parts)
{
<partial name="_PartPartial" model="new { PartData = part, Id = #Model.Id, Bearer = Bearer }" />
}
Index.cshtml.cs
public class IndexModel : PageModel
{
public List<PartModel> Parts;
public string Id;
public async Task<IActionResult> OnGetAsync(string Id)
{
// Query Parts from endpoint...
return Page();
}
}
_PartPartial.cshtml
#model dynamic
<div class="card">
<div class="card-header">
<button type="button" class="btn" data-bs-toggle="collapse" data-bs-target="#partContent_#Model.PartData.partId"
aria-expanded="false" aria-controls="partContent_#Model.PartData.partId" data-id="#Model.PartData.partId" onclick="queryInnerData(this);">
#Model.PartData.DisplayName
</button>
</div>
<div class="collapse" id="partContent_#Model.PartData.partId">
<div class="card card-body">
<div id="innerData_#Model.PartData.partId"></div>
</div>
</div>
</div>
<script type="text/javascript">
function queryInnerData(sender)
{
// This is the part where I am stuck. How do I access the Bearer
}
</script>
I have tried creating a page that does an HttpRequest and returns a JsonResult in its OnGet method. This works, but exposes the raw data to the outside world as well, so I would rather not go with this solution.
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");
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.
I'm sure someone has asked this question or its something easy that i'm just struggling with.
I have going their the Identity Server docs and I've been able to setup as server, api, and MVC Client.
I'm able to login to the MVC Client using the Identity Server.
I'm trying to add currently login user and logout to a _loginPartial this should prove I can authorize view based on login as well. But I keep running into errors that UserManger is not doesn't have type registered.
any help or even a link to and example would be appreciated.
Based on the IdentityServer4 mvc client sample code, you could get current user from user claims.To display all the claims, you could use
#foreach (var claim in User.Claims)
{
<dt>#claim.Type</dt>
<dd>#claim.Value</dd>
}
Try to use below code in _loginPartial.cshtml to get Name claim of user
<ul class="navbar-nav">
#if (User.FindFirst("Name").Value != null)
{
<li class="nav-item">
<a> Hello #User.FindFirst("Name").Value !</a>
</li>
<li class="nav-item">
<form id="logoutForm" class="form-inline" asp-area="" asp-controller="Home" asp-action="Logout"">
<button id="logout" type="submit" class="nav-link btn btn-link text-dark">Logout</button>
</form>
</li>
}
</ul>
Logout action:
public IActionResult Logout()
{
return SignOut("Cookies", "oidc");
}
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?