2

I'm trying to setup an ASP.net Core 3 MVC app that uses OIDC to connect to my company's SSO portal (OpenAM).

I used Visual Studio 2019 project generator to create a basic app with no authentication and then I added the OIDC client capabilities following the steps at http://docs.identityserver.io/en/latest/quickstarts/2_interactive_aspnetcore.html#creating-an-mvc-client . Logging in works great with minimal changes to the Startup class:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();

        // Setup Identity Server client
        JwtSecurityTokenHandler.DefaultMapInboundClaims = false;

        services.AddAuthentication(options =>
            {
                options.DefaultScheme = "Cookies";
                options.DefaultChallengeScheme = "oidc";
            })
            .AddCookie("Cookies")
            .AddOpenIdConnect("oidc", options =>
            {
                options.Authority = "https://mycompany.com/ssoservice/oauth2";
                options.RequireHttpsMetadata = false;

                options.ClientId = "openIdClient";
                options.ClientSecret = "secret";
                options.ResponseType = "code";
                options.ProtocolValidator.RequireNonce = false;

                options.SaveTokens = true;
            });
    }

    // 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();
            IdentityModelEventSource.ShowPII = true;
        }
        else
        {
            app.UseExceptionHandler("/Home/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.MapDefaultControllerRoute();
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
        });
    }

I also set up a Logout controller action:

    [Authorize]
    public IActionResult Logout()
    {
        return SignOut("Cookies", "oidc");
    }

The action actually works, i.e. when activated the cookie is deleted and the user is logged out from the SSO portal, but when the browser redirects to the /signout-callback-oidc endpoint it receives an HTTP 200 response without any content. I would have expected to have it automatically redirect to the site home page "/", which is the default value of the OpenIdConnectOptions.SignedOutRedirectUri property.

What am I missing?

  • No, the project is vanilla Visual Studio MVC template. The Startup.Configure method is already posted above. I'm kinda new to ASP.net Core MVC so I apologize if I misunderstood your request. – Luca Leonardo Scorcia Nov 17 '19 at 14:15
  • Debug output: https://pastebin.com/rDcTr5Fj ASP.net Core Web Server output: https://pastebin.com/f2NJvdzx Thanks for your patience. – Luca Leonardo Scorcia Nov 17 '19 at 15:32
  • 1
    The line in the log suggests that you are calling the url directly without passing the state: `Request starting HTTP/2.0 GET https://localhost:44301/signout-callback-oidc`. This should be: `https://localhost:44301/signout-callback-oidc?state=CfDJ8OM...` So the questing is, why is the state omitted? Can you show the code from Account.Logout in IdentityServer? –  Nov 19 '19 at 15:01
  • I think that the way you redirect the user in IdentityServer (`Account.Logout`) is the problem. In my answer [here](https://stackoverflow.com/questions/56477130/how-to-redirect-user-to-client-app-after-logging-out-from-identity-server/56604640#56604640) I explain how you can automatically redirect the user to the client. The id_token is not the problem, so you can skip the first part of the answer. –  Nov 19 '19 at 16:42
  • It wasn't clear to me that the server wasn't an implementation of IdentityServer, but it doesn't matter after all since it's all about implementing the same specifications. –  Nov 21 '19 at 09:15

2 Answers2

8

Ok, after fiddling some more time, I found out this is the result of a missing draft implementation in the latest community OpenAM release (and also in the current paid ForgeRock AM, but they are working on it: https://bugster.forgerock.org/jira/browse/OPENAM-13831). Basically, the .net core handler for /signout-callback-oidc relies on having the state parameter available in order to redirect, like Ruard van Elburg mentioned in the comments:

https://github.com/aspnet/AspNetCore/blob/4fa5a228cfeb52926b30a2741b99112a64454b36/src/Security/Authentication/OpenIdConnect/src/OpenIdConnectHandler.cs#L312-L315

OpenAM does not send back the state parameter, as reported in my logs. Therefore, we need to perform the redirect ourselves - the most straightforward way seems to be using the OnSignedOutCallbackRedirect event:

Startup.cs

    services.AddAuthentication(...)
        .AddCookie("Cookies")
        .AddOpenIdConnect("oidc", options =>
        {
            ...
            options.Events.OnSignedOutCallbackRedirect += context =>
            {
                context.Response.Redirect(context.Options.SignedOutRedirectUri);
                context.HandleResponse();

                return Task.CompletedTask;
            };
            ...
        });

Thanks to all the users that replied to the discussion, your contributions allowed me to find the clues to the correct solution.

1

you return SignOut,

instead, SignOut user and return RedirectToAction("Home","Index")

J S
  • 591
  • 6
  • 11
  • 1
    No, that's not it. This is [sample code](https://github.com/IdentityServer/IdentityServer4/blob/master/samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/Controllers/HomeController.cs#L22). –  Nov 16 '19 at 20:19
  • line 24 you are doing SignOut instead, call your service and signout user, then Redirect to other action. – J S Nov 17 '19 at 10:44
  • Please note the tag: `IdentityServer4`. There's nothing wrong with how the signout statement is used, as shown in de sample code of IdentityServer. OP wants to know why it doesn't work, not if there are alternative solutions. –  Nov 17 '19 at 13:08
  • That's he's the last call so either in SignOut he can redirect after signing out, or sign out and redirect. – J S Nov 17 '19 at 20:09
  • It turns out that J S is correct: IdentityServer4 plays no role here (I will edit the first post accordingly): in fact, it only provides the server capabilities; the client ones are part of the .net core framework and the latter doesn't any type of redirection on its own. I had misinterpreted the documentation of IdentityServer4 and wrongly extrapolated from there. – Luca Leonardo Scorcia Nov 20 '19 at 19:45