I expected using @attribute [Authorize]
to bounce me to my OpenID Connect login page, but instead it displays "Not Authorized" message.
Steps to reproduce the behavior:
3.0.0-preview9.19424.4
services.AddAuthentication(options =>
{
options.DefaultScheme = "My.WebApp";
options.DefaultChallengeScheme = "Accelist";
}).AddCookie("My.WebApp")
.AddOpenIdConnect("Accelist", "Accelist SSO", options =>
{
options.ClientId = Configuration["OIDC:ClientId"];
options.ClientSecret = Configuration["OIDC:ClientSecret"];
options.Authority = Configuration["OIDC:AuthorityBaseUri"];
// https://auth0.com/docs/api-auth/which-oauth-flow-to-use
options.ResponseType = OpenIdConnectResponseType.Code;
options.RequireHttpsMetadata = Env.IsProduction();
options.Scope.Add("email");
options.Scope.Add("phone");
// https://joonasw.net/view/adding-custom-claims-aspnet-core-2
// https://leastprivilege.com/2017/11/15/missing-claims-in-the-asp-net-core-2-openid-connect-handler/
// https://www.jerriepelser.com/blog/authenticate-oauth-aspnet-core-2/
options.GetClaimsFromUserInfoEndpoint = true;
options.ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
});
app.UseCookiePolicy();
app.UseStaticFiles();
app.UseAuthentication();
app.UseAuthorization(); // <-- Is this really needed? HELP!
app.UseRouting();
app.UseEndpoints(endpoints =>
{
//endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
@page "/"
@attribute [Authorize]
<h1>Hello, world!</h1>
Welcome to your new app.
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<CascadingAuthenticationState>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</CascadingAuthenticationState>
</NotFound>
</Router>
I should get redirected using my default challenge scheme
Add any other context about the problem here.
Include the output of dotnet --info
.NET Core SDK (reflecting any global.json):
Version: 3.0.100-preview9-014004
Commit: 8e7ef240a5
Runtime Environment:
OS Name: Windows
OS Version: 10.0.18362
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\3.0.100-preview9-014004\
Host (useful for support):
Version: 3.0.0-preview9-19423-09
Commit: 2be172345a
.NET Core SDKs installed:
2.1.801 [C:\Program Files\dotnet\sdk]
2.2.401 [C:\Program Files\dotnet\sdk]
3.0.100-preview9-014004 [C:\Program Files\dotnet\sdk]
.NET Core runtimes installed:
Microsoft.AspNetCore.All 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.All 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.0.0-preview9.19424.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.0.0-preview9-19423-09 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.0.0-preview9-19423-09 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
To install additional .NET Core runtimes or SDKs:
https://aka.ms/dotnet-download
Following tutorial from:
Thanks for contacting us, @ryanelian.
The behavior you're observing is by-design. The AuthorizeRouteView
has a property which allows to set view for unauthorized case, and you haven't defined it, which results in the default message to show up.
@danroth27 do you have updated docs regarding this already?
The AuthorizeRouteView has a property which allows to set view for unauthorized case
Thank you for the confirmation.
Can we have the redirect just so the app behaves like any other ASP.NET MVC web app? Our customers love (cough demand cough) the ASP.NET MVC redirect URI behavior / feature whenever an unauthorized user access a random page. (e.g. /something
--> /login?redirectUri=/something
--> /something
again)
It'll be a hard sell to our customers if Blazor cannot do that...
Please add a last-minute addition to Blazor for this specific feature? I know you guys are going GA at the end of September, but I would highly value this feature being available without waiting for version 3.1.0
This scenario can be accomplished by first defining a RedirectToLogin
component like this:
@inject NavigationManager Navigation
@code {
protected override void OnInitialized()
{
Navigation.NavigateTo("Identity/Account/Login", true);
}
}
and then use AuthorizeRouteView
like this:
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
<RedirectToLogin />
</NotAuthorized>
</AuthorizeRouteView>
Amazing. Thank you. This should probably do the trick...
// I'm not sure the redirectUri here is secure, but whatever, it works.
// razor page login page
public class LoginModel : PageModel
{
public async Task OnGet(string redirectUri)
{
await HttpContext.ChallengeAsync(new AuthenticationProperties
{
RedirectUri = redirectUri
});
}
}
// razor page logout page
public class LogoutModel : PageModel
{
public async Task<IActionResult> OnGet()
{
await HttpContext.SignOutAsync();
return Redirect("/");
}
}
// blazor component redirect to login WHEN NOT authenticated
@inject NavigationManager Navigation
@using Microsoft.AspNetCore.Http
@inject IHttpContextAccessor ctx
@code {
protected override void OnInitialized()
{
if (ctx.HttpContext.User.Identity.IsAuthenticated == false)
{
var challengeUri = "/login?redirectUri=" + System.Net.WebUtility.UrlEncode(Navigation.Uri);
Navigation.NavigateTo(challengeUri, true);
}
}
}
<p>
You are not authorized to access this page.
</p>
// app razor
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
<Challenge></Challenge>
</NotAuthorized>
</AuthorizeRouteView>
Although, I still insist that this feature is critical, and must be in the base framework without requiring developers to write that on our own.
This issue still seems to be present in the RTM release. I am authenticating with AzureAD so when I add the following to my Blazor page I am expecting it to automatically challenge, it is not.
@attribute [Authorize(Policy = Policies.AccessRole)]
Do I need to do something else for this to work?
@daver77 You need to follow @danroth27 suggestion, Blazor apps follow a different model than traditional web pages and performing a traditional ASP.NET Core challenge is not possible. As an alternative, add an additional endpoint, redirect the user there as @danroth27 suggests. perform the challenge from there and redirect back to the blazor app at the end of the login flow.
// razor page login page public class LoginModel : PageModel { public async Task OnGet(string redirectUri) { await HttpContext.ChallengeAsync(new AuthenticationProperties { RedirectUri = redirectUri }); } }
You should do validation over that redirect uri to ensure its local, otherwise you are opening yourself to open redirect attacks.
@javiercn thanks, that may be a work-around but the fact is that the Authorize attribute does not work.
@daver77 That is not the case.
The Authorize attribute is just metadata that each framework decides how to interpret. Performing a challenge is just the most common behavior, but it is not a prescriptive one.
@daver77 That is not the case.
The Authorize attribute is just metadata that each framework decides how to interpret. Performing a challenge is just the most common behavior, but it is not a prescriptive one.
I think the issue is that Blazor simply doesn't interpret the behavior as expected. I just started porting a bunch of our apps over and ran into this myself and was pretty surprised that it did not follow the same behavior we see in controllers and razor pages. Not a big deal to resolve it, but the documentation should be updated at the very least with the information on how to make it work generically.
This scenario can be accomplished by first defining a
RedirectToLogin
component like this:@inject NavigationManager Navigation @code { protected override void OnInitialized() { Navigation.NavigateTo("Identity/Account/Login", true); } }
and then use
AuthorizeRouteView
like this:<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"> <NotAuthorized> <RedirectToLogin /> </NotAuthorized> </AuthorizeRouteView>
Hi Dan,
I tried your suggestion, but it doesn't work, there is nothing happen when I come to an authorized page. Seems like "NotAuthorized" only displays content instead of a component.
Thanks.
I appreciate the workaround shown by @danroth27 and @ryanelian but seen from a .NET Core enthusiast we really need [Authorize]
attribute in Blazor components to redirect to Challenge. That is primarily because of consistency with the way MVC works with [Authorize].
Also, it seems that @attribute [AllowAnonymous]
has no place in Blazor Components as it is today, as we can not place a global Authorize on all components at once. Aka. instead of having everything open and then closing with [Authorize], I would prefer to have everything closed and then open with [AllowAnonymous]
@quoctuancqt You need to add a "using" statement to wherever you added the RedirectToLogin
component.
If you added it to a folder called Components
in your app (called BlazorApp2) then add the following to the _Imports.razor
file:
@using BlazorApp2.Components
The solution above will result in an "Microsoft.AspNetCore.Components.NavigationException" because you are disrupting the page build process.
If you want to secure the whole bloazor page use the app.net security mechanisms. This could be done by simply requesting an authorization for the whole _Host.cshtml file.
For example by adding: @attribute [Authorize]
Not sure if people are aware, you can use this Razor Page method to secure Blazor Pages
https://docs.microsoft.com/en-us/aspnet/core/security/authorization/razor-pages-authorization?view=aspnetcore-3.1#require-authorization-to-access-a-folder-of-pages
Not sure if people are aware, you can use this Razor Page method to secure Blazor Pages
https://docs.microsoft.com/en-us/aspnet/core/security/authorization/razor-pages-authorization?view=aspnetcore-3.1#require-authorization-to-access-a-folder-of-pages
But this isn't relevant to Blazor webassembly: those razor page options aren't available as methods on the WebAssemblyHostBuilder.Services property. However, it's difficult to ascertain whether or not the original question is regarding Blazor webassembly or not. I'm also encountering the same issue with the AuthroiseRouteView not redirecting, 'NotAuthorized' requests to my custom login component.
Apart from that, I'm really loving Blazor.
Putting this code
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
in the Host.cshtml
, provides the behavior that navigating to the main /
page redirects to the login as @StaticBR mentioned above.
However, putting that same code in a razor component such as fetchdata.razor
from the sample, does not produce a redirect, but instead produces the <NotAuthorized>
markup from the App.razor
page:
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
<h1>Sorry</h1>
<p>You're not authorized to reach this page.</p>
<p>You may need to log in as a different user.</p>
</NotAuthorized>
<Authorizing>
<h1>Authentication in progress</h1>
<p>Only visible while authentication is in progress.</p>
</Authorizing>
</AuthorizeRouteView>
</Found>
I would have expected a redirect in either case.
I am not a very experienced webdeveloper but it seems to me that the [Authorize] attribute not directly redirecting to the login page, is far more flexible because you can decide what happens when the user tries to access a page that requires authentication. Alternate content that explains the problem and offers a login button is far more user-friendly than redirect and gives the user an option to bail out and select non-authorized content instead of beeing confronted with a login dialog.
The solution as described here ( the razor page as model in _host.cshtml ) combined with both the
I've been busy with this problem for about 3 days now and the solution here is workable and flexible because it does not affect the authorization strategy of Blazor.
Hi,
I'm trying to make my app available only for logged in users.
I set App.razor:
< CascadingAuthenticationState> < AuthorizeView> < Authorized> < Router AppAssembly="@typeof(Program).Assembly"> < Found Context="routeData"> < AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> < /Found> < NotFound> < LayoutView Layout="@typeof(MainLayout)"> < p>Sorry, there's nothing at this address.< /p> < /LayoutView> < /NotFound> < /Router> < /Authorized> < NotAuthorized> < RedirectToLogin/> < /NotAuthorized> < /AuthorizeView> < /CascadingAuthenticationState>
And I get this exception:
When I click Continue application is redirected to login page. So it's working ok except this exception.
I'm doing something wrong ?
As far as i can see, the error is in the app.razor. You are using the Navigation Manager to navigate to the login page but for that to work, routing should be initialized... The problem is in the fact that you have the Routing initialization within an AuthorizeView which, of which the NotAuthorized section is triggered directly... before routing is initialized...
So, you should remove the AuthorizeView from the app.razor.
Another thing is that, if you use Role authorization, the application will check that if you reach a section that has an authorization attribute. If you are logged in, but not authorized for a section, the current implementation of the RedirecToLogin component will end up in a loop logging you in.
A way to resolve this is to create a check inside the RedirecToLogin component in order to determine if the user is currently logged in. A redirection is not necessary in that case.
Also, if you want to secure your entire (hosted) application, you should configure it in the or Startup.cs and modify:
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub().RequireAuthorization();
});
Anybody who thinks this reaction sucks, please correct me...
Adding RequireAuthorization() didn't do nothing :/ You can see index page without log in.
I also change App.razor, now routing is initialized:
And I still got exception described before.
Sorry, my fail
services.AddMvc(config =>
{
//only allow authenticated users
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
});
Regards,
Evert
Verzonden vanuit Mail voor Windows 10
Van: Tom Thunderforest
Verzonden: woensdag 6 mei 2020 07:42
Aan: dotnet/aspnetcore
CC: everttimmer; Comment
Onderwerp: Re: [dotnet/aspnetcore] Blazor Authorization Should Redirect toChallenge When Default Challenge Scheme is Set (#13709)
Adding RequireAuthorization() didn't do nothing :/ You can see index page without log in.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or unsubscribe.
@TomaszGrzmilas I think the NavigationException you are getting is normal - that's how the NavigationManager aborts the operation when Navigating to another view. You would only see it in debug, and you can select the VS option to not break on such navigation exceptions.
If you want to implement the login view also as a Blazor component, rather than a razor page, for consistent look and feel, then you can use the SignInManager from the Xomega.Framework.Blazor package to do the Challenge for the login redirect, as well as SignIn and SignOut.
Here is the full explanation of how it works with references to the appropriate code: https://github.com/dotnet/aspnetcore/issues/19148#issuecomment-624862445
Thanks to this post, I was able to achieve forcing the user to log in, after the MainLayout.razor has been loaded, which was not my desired behaviour. I was able to overcome this with the help of a couple SO answers.
```@inherits LayoutComponentBase
@Body
This will immedietaly redirect to RedirectToLogin.razor, which then redirects to Authentication.razor. This component tries to render the layout, which creates an infinite [loop](https://stackoverflow.com/a/61565996). To overcome that behaviour, you can create a seperate component redirecting to your Log In page, or change the Authentication.razor component, which I will show here.
2. [Changing the Authentication.razor component, by adding an Empty Layout](https://stackoverflow.com/questions/59518988/disable-layout-for-page-under-blazor)
```@page "/authentication/{action}"
@using Shared
@layout EmptyLayout
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
<RemoteAuthenticatorView Action="@Action" />
@code{
[Parameter] public string Action { get; set; }
}
```inherits LayoutComponentBase
```
When use you My Code if Authorized&NotAuthorized work
only for manage redirect page logout or any page NotAuthorized
App.razor
` <CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeView>
<Authorized>
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Authorized>
<NotAuthorized>
<CascadingAuthenticationState>
<RedirectToLogin />
</CascadingAuthenticationState>
</NotAuthorized>
</AuthorizeView>
</Found>
<NotFound>
<CascadingAuthenticationState>
<LayoutView Layout="@typeof(EmptyLayout)">
<p>No Found</p>
</LayoutView>
</CascadingAuthenticationState>
</NotFound>
</Router>
</CascadingAuthenticationState>`
RedirectToLogin .razor
@* only write redirect to login or any page you need *@
<iServicePayroll.Pages.Home.Login />
So what's the official solution for webassembly and third party authentication? I'm trying these various workaround, seems to getting infinite loop or not working.
@imtrobin Take a look at https://docs.microsoft.com/aspnet/core/blazor/security/webassembly/standalone-with-authentication-library and see if it helps.
Thanks dan, it's close what I have pieced together over the internet but it's really not situation. I'm not using ODIC, it's a custom authentication library with jwtoken/refresh tokens.
I used this which gets me closer , but it's missing quite a few bits.
https://www.mikesdotnetting.com/article/342/managing-authentication-token-expiry-in-webassembly-based-blazor
Thank you for contacting us. Due to a lack of activity on this discussion issue we're closing it in an effort to keep our backlog clean. If you believe there is a concern related to the ASP.NET Core framework, which hasn't been addressed yet, please file a new issue.
This issue will be locked after 30 more days of inactivity. If you still wish to discuss this subject after then, please create a new issue!
Most helpful comment
This scenario can be accomplished by first defining a
RedirectToLogin
component like this:and then use
AuthorizeRouteView
like this: