Blazor (Server Side) and Okta

倖福魔咒の 提交于 2020-05-09 06:09:39

问题


I am currently going through this post to integrate okta into a Blazor Server Side App

https://developer.okta.com/blog/2019/10/16/csharp-blazor-authentication

I am currently getting “Sorry, there's nothing at this address”.

I was hoping someone could provide a suggestion to my problem.

Or does anyone know of an example to integrate okta into a Blazor Server Side App?

Please let me know.

Any help would be appreciated. I am totally spinning my wheels.

Here are my okta General Settings:

Below is code from my solution (I reviewed the post so many times to make sure it is right. But I guess I could of missed something.)

StartUp ConfigureServices

       services.AddAuthorizationCore();
        services.AddAuthentication(sharedOptions =>
            {
                sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
            })
            .AddCookie()
            .AddOpenIdConnect(options =>
            {
                options.ClientId = Configuration["Okta:ClientId"];
                options.ClientSecret = Configuration["Okta:ClientSecret"];
                options.Authority = Configuration["Okta:Issuer"];
                options.CallbackPath = "/authorization-code/callback";
                options.ResponseType = "code";
                options.SaveTokens = true;
                options.UseTokenLifetime = false;
                options.GetClaimsFromUserInfoEndpoint = true;
                options.Scope.Add("openid");
                options.Scope.Add("profile");
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "name"
                };
            });
    }

StartUp Configure

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();

        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapBlazorHub();
            endpoints.MapFallbackToPage("/_Host");
        });
    }

MainLayout.razor

<div class="top-row px-4">

    <AuthorizeView>
        <Authorized>
            <a href="Identity/Account/Manage">Hello, @context.User.Identity.Name!</a>
            <a href="Identity/Account/LogOut">Log out</a>
        </Authorized>
        <NotAuthorized>
            <a href="Identity/Account/Register">Register</a>
            <a href="Identity/Account/Login">Log in</a>
        </NotAuthorized>
    </AuthorizeView>

    <a href="https://docs.microsoft.com/en-us/aspnet/" target="_blank">About</a>
</div>

App.razor

<CascadingAuthenticationState>
    <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>
</CascadingAuthenticationState> ```




回答1:


When you click on the "Log in" anchor element, the "Identity/Account/Login" path cannot be found by the Router, the result of which is displaying

Sorry, there's nothing at this address.

.Of course you are not directed to the okta web site, and no authentication takes place, right ?

It is beyond me to understand why she set up the AuthorizeView component like this. It seems to me, though, that her whole article was designed by a municipal committee, and assembled from spare code snippets. However, this is beyond the scope of this answer.

Solution

  1. Create a component named LoginDisplay (LoginDisplay.razor), and place it in the Shared folder. This component is used in the MainLayout component

    <AuthorizeView> <Authorized> <a href="logout">Hello, @context.User.Identity.Name!</a> <form method="get" action="logout"> <button type="submit" class="nav-link btn btn-link">Log out</button> </form> </Authorized> <NotAuthorized> <a href="login?redirectUri=/">Log in</a> </NotAuthorized> </AuthorizeView>

    Add the LoginDisplay component to the MainLayout component, just above the About anchor element, like this <div class="top-row px-4"> <LoginDisplay /> <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a> </div>

Note: In order to redirect requests for login and for logout to okta, we have to create two Razor pages as follows: 1. Create a Login Razor page Login.cshtml (Login.cshtml.cs) and place them in the Pages folder as follow:

Login.cshtml.cs

 using Microsoft.AspNetCore.Authentication;
 using Microsoft.AspNetCore.Authentication.OpenIdConnect;
 using Microsoft.AspNetCore.Authentication.Cookies;
 using Microsoft.IdentityModel.Tokens;

public class LoginModel : PageModel
{
    public async Task OnGet(string redirectUri)
    {
        await 
    HttpContext.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, 
    new AuthenticationProperties
    {
            RedirectUri = redirectUri
    });
  }
}

This code starts the challenge for the Open Id Connect authentication scheme you defined in the Startup class.

  1. Create a Logout Razor page Logout.cshtml (Logout.cshtml.cs) and place them in the Pages folder as well:

    Logout.cshtml.cs

using Microsoft.AspNetCore.Authentication; public class LogoutModel : PageModel { public async Task<IActionResult> OnGetAsync() { await HttpContext.SignOutAsync(); return Redirect("/"); } } This code sign you out, redirecting you to the Home page of your Blazor app.

Replace the code in App.razor with the following code:

<CascadingAuthenticationState>

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" 
      DefaultLayout="@typeof(MainLayout)">
            <NotAuthorized>

                  @*<RedirectToLogin />*@
            </NotAuthorized>
            <Authorizing>
                Wait...
            </Authorizing>
        </AuthorizeRouteView>
    </Found>
    <NotFound>

            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>

    </NotFound>

 </Router>
 </CascadingAuthenticationState>

Run your app, click on the log in button to authenticate

Note: I've not finished my answer...I've more to add, but I can't do it now. I'll do it as soon as I can. If you have questions, please don't hesitate to ask. I'll answer them, if I can...;)

Note: This sample works for me great.

Please don't forget your appsettings.json file. It should be something like this:

{
  "Okta": {
    "Issuer": "https://dev-621531.okta.com/oauth2/default",
    "ClientId": "0o1a5bsivg6wFDw6Jr347",
    "ClientSecret": "ffolkG3sd2NgQ_E909etXRU3cXX3wBpgE0XxcmF5"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Don't try to use my settings, as I've changed them... The code in the Startup class is correct, but you may need to add some namespace references, perhaps nuget packages which I added,etc.

Note: While you're experimenting with your app, you should clear the browsing data, if you want to be redirected to okta's login page, otherwise, your browser may use the cached data.

Update Note that creating a login mechanism as is done here does not make your app more secured than before. Any user can access your web resources without needing to log in at all. In order to secure parts of your web site, you have to implement authorization as well, conventionally, an authenticated user is authorized to access secured resource, unless other measures are implemented, such as roles, policies, etc. The following is a demonstration how you can secured your Fetchdata page from unauthorized users (again, authenticated user is considered authorized to access the Fetchdata page).

  1. At the top of the Fetchdata component page add the @attribute directive for the Authorize attribute, like this: @attribute [Authorize] When an unauthenticated user tries to access the Fetchdata page, the AuthorizeRouteView.NotAuthorized delegate property is executed, so we can add some code to redirect the user to the same okta's login page to authenticate.
  2. place this code within the NotAuthorized element, like this:

    <NotAuthorized> @{ var returnUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); NavigationManager.NavigateTo($"login?redirectUri= {returnUrl}", forceLoad: true); } </NotAuthorized>

This retrieves the url of the last page you were trying to access, the Fetchdata page, and then navigate to the Login Razor page from which a password challenge is performed, that is the user is redirected to okta's login page to authenticate.

After the user has authenticated he's redirected to the Fetchdata page.

Last but not least, add the following at the top of the Fetchdata page: @page "/authorization-code/fetchdata"

Note that the Fetchdata component has already have a @page directive, and this one is the second. Multiple route templates are legitimate in Blazor... But why do I add it ? This is because the return url passed from okta to our app is "/authorization-code/fetchdata", and not "/fetchdata", as we might have expected, as the returnUrl we extracted from the navigation manager is "fetchdata". I'm not sure I understand the role of "/authorization-code/", and why it is enforced on us. I really did not have the time to investigate. However, this url segment is required by okta, and any attempt to do without it has failed, both in my code and in the dashboard in okta web site. I can get rid of this url segment in code, but this is not an issue. I just want to know why we need to provide it, so I can deal with it better. If you happen to learn more about it, please report it here, as I cannot spend more time investigating...

Incidentally, the solution provided in the article is by no mean complete; I dare say, rather partial.

Good luck...



来源:https://stackoverflow.com/questions/59459500/blazor-server-side-and-okta

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!