I have the following razor component
page "/test"
#using Microsoft.AspNetCore.Components.Authorization
#inject HttpClient Http
<h3>TestAuthPage</h3>
<AuthorizeView Roles="admin">
<Authorized>
Hello
</Authorized>
<NotAuthorized>
Not Auth
</NotAuthorized>
</AuthorizeView>
#code {
protected override async Task OnInitializedAsync()
{
var f = await Http.GetFromJsonAsync<List<string>>("api/test");
}
}
This throws an error in the browser as it gives a 401 when calling the api
However if I do this
#page "/test"
#using Microsoft.AspNetCore.Components.Authorization
#inject HttpClient Http
<h3>TestAuthPage</h3>
<AuthorizeView Roles="admin">
<Authorized>
Hello
#if(sss().Result)
{}
</Authorized>
<NotAuthorized>
Not Auth
</NotAuthorized>
</AuthorizeView>
#code {
public async Task<bool> sss()
{
var f = await Http.GetFromJsonAsync<List<string>>("api/test");
return true;
}
}
The api controller is called correctly.
What on earth is going on? Surely OnInitializedAsync() is a standard way to initialise the page?
Okay, I seem to have solved the issue. If I add an AuthenticationState parameter and then await it in the method then the Controller is called fine and I am authenticated on the controller side. I have no idea why this works but I assume it must be a timing thing? If anyone knows why this works then please let me know...
#page "/test"
#using Microsoft.AspNetCore.Components.Authorization
#inject HttpClient Http
<h3>TestAuthPage</h3>
<AuthorizeView Roles="admin">
<Authorized>
Hello
#if(sss().Result)
{}
</Authorized>
<NotAuthorized>
Not Auth
</NotAuthorized>
</AuthorizeView>
#code {
[CascadingParameter]
public Task<AuthenticationState> AuthState{ get; set; }
protected override async Task OnInitializedAsync()
{
var t = await AuthState;
var f = await Http.GetFromJsonAsync<List<string>>("api/test");
}
}
Related
When user is logged in, I'd like to fetch user's profile data so that I can display them throughout the application.
<CascadingValue Value="#UserProfile">
<div class="content px-4">
#Body
</div>
</CascadingValue>
I'd like to be able to intercept a request so that I can check 1) the user is logged in and 2) the UserProfile object is set. If the user's logged in but the object is not set then I'll make a request to the API to get data.
Here's the portion of the App.razor code.
<Found Context="routeData">
<AuthorizeRouteView RouteData="#routeData" DefaultLayout="#typeof(MainLayout)">
<NotAuthorized>
#if (!context.User.Identity.IsAuthenticated)
{
<RedirectToLogin />
}
else
{
<p>You are not authorized to access this resource.</p>
}
</NotAuthorized>
<Authorizing>
</Authorizing>
</AuthorizeRouteView>
</Found>
<Authorizing></Authorizing> can be used for when the authentication process is going on. How do I know when the process is done and that the user's logged in, so that I can fetch user's profile.
Thanks for helping.
The answer is the RemoteAuthenticatorView component on the Authenticator.razor page. This component exposes all sort of properties that covers so many scenarios, i.e. OnLogInSucceeded, OnLogOutSucceeded, OnLogOutFailed, and so forth.
In my case, I wanted to queries user's profile data as soon as the login succeeds so I can display which organization's he/she belongs to.
#page "/authentication/{action}"
#using Microsoft.AspNetCore.Components.WebAssembly.Authentication
<RemoteAuthenticatorView
AuthenticationState="RemoteAuthenticationState"
OnLogInSucceeded="#RestoreAppState" //this is the event I was looking for
Action="#Action" />
OnLoggInSucceeded is what I was looking for
public partial class Authentication
{
[Inject] AppState AppState { get; set; }
[Inject] HttpClient Http { get; set; }
[Parameter] public string Action { get; set; }
public AppAuthenticationState RemoteAuthenticationState { get; set; } =
new AppAuthenticationState();
protected override void OnInitialized()
{
if (RemoteAuthenticationActions.IsAction(RemoteAuthenticationActions.LogIn, Action))
{
// Preserve the current order so that we don't loose it
//RemoteAuthenticationState.Order = OrderState.Order;
}
}
private async Task RestoreAppState()
{
if (string.IsNullOrEmpty(AppState.RootInstitutionName))
{
try
{
var profileData = await Http.GetFromJsonAsync<UserProfileData>("userprofiles");
AppState.RootInstitutionName = profileData.RootInstitution.InstitutionName;
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
}
}
}
There's even a possibility to preserve data between operations, such login, logout. It was good to watch Daniel Roth explaining security works in Blazor.
Might be trying to solve this the wrong way, but here's the situation.
I have a component designed to redirect to login if the user trying to access it isn't authenticated, and display not found if they aren't authorized for the page requested.
<AuthorizeViewWithPermissions RequiredPermission="RequiredPermission">
<Authorized>
#ChildContent
</Authorized>
<NotAuthenticated>
<LoginRedirect />
</NotAuthenticated>
<NotAuthorized>
<NotFoundRedirect />
</NotAuthorized>
</AuthorizeViewWithPermissions>
#code {
[Parameter] public RenderFragment ChildContent { get; set; }
[Parameter] public Permissions RequiredPermission { get; set; }
protected override void OnInitialized()
{
}
}
LoginRedirect is this:
public class LoginRedirect : ComponentBase
{
[Inject] protected NavigationManager NavigationManager { get; set; }
protected override void OnInitialized()
{
NavigationManager.NavigateTo("/Login", true);
}
}
internals of AuthorizeViewWithPermissions:
/// <summary>
/// Largely borrowed from the original AuthorizeView, but cut up a bit to use custom permissions and cut out a lot of stuff that isn't needed.
/// </summary>
public class AuthorizeViewWithPermissions : ComponentBase
{
private AuthenticationState _currentAuthenticationState;
private bool _isAuthorized;
private bool _isAuthenticated;
/// <summary>
/// The permission type required to display the content
/// </summary>
[Parameter] public Permissions RequiredPermission { get; set; }
/// <summary>
/// The content that will be displayed if the user is authorized.
/// </summary>
[Parameter] public RenderFragment<AuthenticationState> ChildContent { get; set; }
/// <summary>
/// The content that will be displayed if the user is not authorized.
/// </summary>
[Parameter] public RenderFragment<AuthenticationState> NotAuthorized { get; set; }
/// <summary>
/// The content that will be displayed if the user is not authenticated.
/// </summary>
[Parameter] public RenderFragment<AuthenticationState> NotAuthenticated { get; set; }
/// <summary>
/// The content that will be displayed if the user is authorized.
/// If you specify a value for this parameter, do not also specify a value for <see cref="ChildContent"/>.
/// </summary>
[Parameter] public RenderFragment<AuthenticationState> Authorized { get; set; }
/// <summary>
/// The content that will be displayed while asynchronous authorization is in progress.
/// </summary>
[Parameter] public RenderFragment Authorizing { get; set; }
/// <summary>
/// The resource to which access is being controlled.
/// </summary>
[Parameter] public object Resource { get; set; }
[CascadingParameter] private Task<AuthenticationState> AuthenticationState { get; set; }
/// <inheritdoc />
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
// We're using the same sequence number for each of the content items here
// so that we can update existing instances if they are the same shape
if (_currentAuthenticationState == null)
{
builder.AddContent(0, Authorizing);
}
else if (_isAuthorized)
{
var authorized = Authorized ?? ChildContent;
builder.AddContent(0, authorized?.Invoke(_currentAuthenticationState));
}
else if (!_isAuthenticated)
{
builder.AddContent(0, NotAuthenticated?.Invoke(_currentAuthenticationState));
}
else
{
builder.AddContent(0, NotAuthorized?.Invoke(_currentAuthenticationState));
}
}
/// <inheritdoc />
protected override async Task OnParametersSetAsync()
{
// We allow 'ChildContent' for convenience in basic cases, and 'Authorized' for symmetry
// with 'NotAuthorized' in other cases. Besides naming, they are equivalent. To avoid
// confusion, explicitly prevent the case where both are supplied.
if (ChildContent != null && Authorized != null)
{
throw new InvalidOperationException($"Do not specify both '{nameof(Authorized)}' and '{nameof(ChildContent)}'.");
}
if (AuthenticationState == null)
{
throw new InvalidOperationException($"Authorization requires a cascading parameter of type Task<{nameof(AuthenticationState)}>. Consider using {typeof(CascadingAuthenticationState).Name} to supply this.");
}
// First render in pending state
// If the task has already completed, this render will be skipped
_currentAuthenticationState = null;
// Then render in completed state
// Importantly, we *don't* call StateHasChanged between the following async steps,
// otherwise we'd display an incorrect UI state while waiting for IsAuthorizedAsync
_currentAuthenticationState = await AuthenticationState;
SetAuthorizedAndAuthenticated(_currentAuthenticationState.User);
}
private void SetAuthorizedAndAuthenticated(ClaimsPrincipal user)
{
var userWithData = SessionHelper.GetCurrentUser(user);
_isAuthenticated = userWithData != null;
_isAuthorized = userWithData?.Permissions.Any(p => p == RequiredPermission || p == Permissions.SuperAdmin) ?? false;
}
}
The authentication and authorization checks are working perfectly fine, but the issue is that the page OnInitializedAsync or OnParametersSetAsync fire before the LoginRedirect OnInitialized.
I'm kicking off my calls for data (to an API, that uses a token stored on the logged in user's data) in OnInitializedAsync which results in it attempting to load the data (with no auth token) instead of just redirecting. If I comment out the data call the redirect works as expected without issue, so it's just a timing/sequence of events issue.
Is there a solution to this? Should I just be changing my api client code to silently fail instead of throwing an unauthorized exception if the auth token is missing?
This is also my app.razor component:
CascadingAuthenticationState>
<Router AppAssembly="#typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="#routeData" DefaultLayout="#typeof(MainLayout)">
<NotAuthorized>
<LoginRedirect />
</NotAuthorized>
<Authorizing>
<p>Checking authorization...</p>
</Authorizing>
</AuthorizeRouteView>
</Found>
<NotFound>
<LayoutView Layout="#typeof(MainLayout)">
<p>These aren't the droids you're looking for.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
Simply check if you have a user before the call to the api:
#inject AuthenticationStateProvider AuthenticationStateProvider
...
#code {
protected override async Task OnInitializedAsync()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
if (!user.Identity.IsAuthenticated)
{
return;
}
}
}
doc
Not being familiar with the internals AuthorizeViewWithPermissions, I can only venture to say that the LoginRedirect component should be exposed from the NotAuthorized property element, as NotAuthorized convey the dual meaning of having no permissions to access a resource, as well as not authenticated. You can be authenticated, but not authorized, and you can be authorized if you are authenticated only (this is the default), unless you define policies to be more specific about your requirements.
In terms of coding, please see how it is done in the default VS template:
<AuthorizeRouteView RouteData="#routeData" DefaultLayout="#typeof(MainLayout)">
<NotAuthorized>
#if (!context.User.Identity.IsAuthenticated)
{
<RedirectToLogin />
}
else
{
<p>You are not authorized to access this resource.</p>
}
</NotAuthorized>
</AuthorizeRouteView>
Again, your posted code is partial, and I can only guess... But I think that you have an issue with the design of your AuthorizeViewWithPermissions, related to what I was referring to above. Try to design it otherwise. Don't look for workaround, for this can prove fatal in the long run. Just try to change the design, based on understanding how the system works...
Hope this helps...
Originally, I found a tutorial online that recommends using a custom route view, however that would not allow me to perform any asynchronous calls to determine if the user was logged in.
So I found another solution that didn't require me to create a custom component or add code to every page.
Instead, I just added some code to my Main Layout and it works like a charm:
#using System.Net
#layout TelerikLayout
#inherits LayoutComponentBase
#inject NavigationManager NavigationManager;
#inject IIdentityService IdentityService;
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<div class="main" style="height: 100vh; display: flex; flex-direction: column;">
<div class="top-row px-4">
<a onclick="#LogOut" target="_blank">Log Out</a>
</div>
<div class="content px-4" style="flex: 1;">
#Body
</div>
</div>
</div>
#code {
protected override async Task OnInitializedAsync()
{
await CheckLoginState();
}
private async Task CheckLoginState()
{
bool isloggedin = await IdentityService.IsLoggedIn();
if (isloggedin == false && NavigationManager.Uri.Split("?").ToList().FirstOrDefault()?.EndsWith("/login") != true)
{
var returnUrl = WebUtility.UrlEncode(new Uri(NavigationManager.Uri).PathAndQuery);
NavigationManager.NavigateTo($"login?returnUrl={returnUrl}");
}
await base.OnInitializedAsync();
}
private async void LogOut()
{
await IdentityService.Logout();
await CheckLoginState();
}
}
I have not tested this solution in a production environment, but so far, it seems to be good.
I'm using Xamarin.Forms 4.4.0.9916 and Prism.Unity.Forms 7.2.0.1422
In a basic prism application, I would like to perform a task before navigating to the MainPage (for the example I replace the task with await Task.Delay(500);)
The problem is that the app doesn't navigate to MainPage.
If I comment out the await Task.Delay(500); then the app navigates to the MainPage
So, what is the problem with that code? How can I have an awaitable method before navigating to the MainPage?
When the await Task.Delay(x); is fast (i.e. await Task.Delay(50);) then the app navigates to the MainPage
public partial class App : PrismApplication
{
public App() : this(null) { }
public App(IPlatformInitializer initializer = null) : base(initializer) { }
protected override async void OnInitialized()
{
InitializeComponent();
await Task.Delay(500);
await NavigationService.NavigateAsync(new Uri("http://www.somewhere.com/MainPage", UriKind.Absolute));
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<MainPage, MainPageViewModel>();
}
}
There is more than one way of doing things here however what you will want to implement is some sort of LoadingPage. This can be a rather "Dumb" Page if needed (without a ViewModel). Alternatively you can migrate your startup logic into the LoadingPageViewModel and then Navigate from there.
protected override async void OnInitialized()
{
// This ensures the MainPage is set and prevents the app from being killed
// by the platform. Also provides better User Experience...
await NavigationService.NavigateAsync("/LoadingPage");
await Task.Delay(TimeSpan.FromSeconds(30));
await NavigationService.NavigateAsync("/MainPage");
}
I have this code that is supposed to set claims for a user. It works fine when I use identity and the default login. However, when I use jwt as authentication in another application, I don't have ApplicationUser as my ApplicationUser is stored in the other application that authenticates the user. How can I customize this code so that it works with jwt?
private readonly SignInManager<TIdentityUser> _signInManager;
public CustomClaimsCookieSignInHelper(SignInManager<TIdentityUser> signInManager)
{
_signInManager = signInManager;
}
public async Task SignInUserAsync(TIdentityUser user, bool isPersistent, IEnumerable<Claim> customClaims)
{
var claimsPrincipal = await _signInManager.CreateUserPrincipalAsync(user);
var identity = claimsPrincipal.Identity as ClaimsIdentity;
var claims = (from c in claimsPrincipal.Claims select c).ToList();
var savedClaims = claims;
if (customClaims != null)
{
identity.AddClaims(customClaims);
}
await _signInManager.Context.SignInAsync(IdentityConstants.ApplicationScheme,
claimsPrincipal,
new AuthenticationProperties { IsPersistent = isPersistent });
}
I guess my main intention is to set my users claims in the httpcontext and not in a cookie and I want to do that without using identity.
EDIT:
My application structure
AuthenticationApp (server)
Responsible for authenticating users
Generates and Decodes Jwt
Checks if the user has the appropriate roles and returns true/false via rest api
MainApp (client)
Makes an api call to AuthenticationApp
Does not use identity at all
Sends Jwt everytime I need to check the role of the user
I understand that I will be able to decode the jwt client side. However, I do not know where I can store the decoded jwt details so that I can use it in the view. My initial idea was to use Httpcontext like normal applications that user Identity. However, I am stuck with the code above.
For sharing the Identity information between Controller and View, you could sign the User information by HttpContext.SignInAsync.
Try steps below to achieve your requirement:
Controller Action
public async Task<IActionResult> Index()
{
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "edward"));
identity.AddClaim(new Claim(ClaimTypes.Name, "edward zhou"));
//add your own claims from jwt token
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties { IsPersistent = true });
return View();
}
View
#foreach (var item in Context.User.Claims)
{
<p>#item.Value</p>
};
To make above code work, register Authentication in Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//your rest code
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//your rest code
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
I'm just trying to authentication a user with Asp.Identity in DelegatingHandler.
Like this code above:
public class TokenAuthentication : DelegatingHandler {
private readonly AuthenticationIdentityManager _identityManager;
public TokenAuthentication() {
_identityManager = new AuthenticationIdentityManager(new IdentityStore(new NFeDb()));
}
private Microsoft.Owin.Security.IAuthenticationManager AuthenticationManager {
get {
return HttpContext.Current.GetOwinContext().Authentication;
}
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
if (request.Headers.Contains("X-TokenCliente")) {
var tokenCliente = request.Headers.GetValues("X-TokenCliente").First();
var s = _identityManager.Authentication.SignIn(this.AuthenticationManager, tokenCliente, false);
if (s.Success) {
return await base.SendAsync(request, cancellationToken);
}
}
return request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
But, at my controller with the Authorize notation:
[Authorize]
public HttpResponseMessage Get() {
return Request.CreateResponse(HttpStatusCode.OK);
}
I recive 302 status e redirected to Login page. Is possible to authenticate in DelegatingHandler?
UPDATE: I don't know if I need to use OwinMiddleware
The 302 redirection is probably from Cookie middleware.
If you are going to use token authentication, you'd better use the OWIN bearer token middleware.
Please check out: https://blogs.msdn.microsoft.com/webdev/2013/09/20/understanding-security-features-in-the-spa-template-for-vs2013-rc/
The blog covers how to use bearer token in web api and how to work side by side with cookie middleware.