Infinite loop between Episerver and Okta

Vote:
 

I am having trouble setting up Okta with Episerver (ASP.NET).

Here a description of my issue.

I start by going to http://localhost:58597/ - This page requires authentication, so I am sent to the Okta login page and after logging in, I am returned to http://localhost:58597/
So far so good. I now go to http://localhost:58597/episerver - which I don't have access to and this starts an infinite loop between Episerver and Okta.

Here's some info from my network tab in Chrome.

GET http://localhost:58597/episerver 302 Found (Location: https://dev-140964.okta.com/oauth2/default/v1/authorize?...)
GET https://dev-140964.okta.com/oauth2/default/v1/authorize?... 200 OK
POST http://localhost:58597/authorization-code/callback 302 Found (Location: http://localhost:58597/episerver)
... and then it starts over

I would rather have the user sent to a static "you do not have access" page than back to Okta.
How on earth do I do that?


Here is my Startup.cs class

using EPiServer.Cms.UI.AspNetIdentity;
using EPiServer.ServiceLocation;
using HYG.Com.Logic.Authorization.Helpers;
using HYG.Com.Logic.Authorization.Models;
using HYG.Com.Logic.Authorization.Services;
using HYG.Com.Logic.Constants;
using Microsoft.Owin;
using Microsoft.Owin.Extensions;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Okta.AspNet;
using Owin;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web.Helpers;

[assembly: OwinStartup(typeof(HYG.Com.Web.Startup))]

namespace HYG.Com.Web
{
public class Startup
{
  public void Configuration(IAppBuilder app)
  {
    // Instantiate the Okta options using the settings from the web.config.
    OktaMvcOptions oktaMvcOptions = new OktaMvcOptions
    {
      OktaDomain = ConfigurationManager.AppSettings["okta:OktaDomain"],
      ClientId = ConfigurationManager.AppSettings["okta:ClientId"],
      ClientSecret = ConfigurationManager.AppSettings["okta:ClientSecret"],
      RedirectUri = ConfigurationManager.AppSettings["okta:RedirectUri"],
      PostLogoutRedirectUri = ConfigurationManager.AppSettings["okta:PostLogoutRedirectUri"],
      GetClaimsFromUserInfoEndpoint = true,
      Scope = new List { "openid", "profile", "email" }
    };

    // Configure the OWIN cookie authentication middleware.
    CookieAuthenticationOptions cookieAuthenticationOptions = new CookieAuthenticationOptions
    {
      LoginPath = new PathString("/account/login"),
    };

    // Register the OWIN middleware components.
    app.AddCmsAspNetIdentity();
    app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
    app.UseCookieAuthentication(cookieAuthenticationOptions);
    app.UseOktaMvc(oktaMvcOptions);

  // Remap logout.
    app.Map("/util/logout.aspx", map =>
    {
      map.Run(ctx =>
      {
        // Log out the user.
        ctx.Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType, OktaDefaults.MvcAuthenticationType);
        return Task.FromResult(0);
      });
    });

    // Map some of the claims from Okta to Microsoft's schemas in order for Episerver to pick up on them.
    app.Use((context, next) =>
    {
      if (context.Authentication.User.Identity is ClaimsIdentity claimsIdentity && claimsIdentity.IsAuthenticated)
      {
        // Add name claim as http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
        var name = claimsIdentity.Claims.Where(claim => claim.Type == "name").Select(claim => claim.Value).FirstOrDefault() ??
        claimsIdentity.Claims.Where(claim => claim.Type == "preferred_username").Select(claim => claim.Value).FirstOrDefault();

       claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, name));

       // Add all group claims for the user as http://schemas.microsoft.com/ws/2008/06/identity/claims/role
      IEnumerable<Claim> groups = claimsIdentity.Claims.Where(claim => claim.Type == "groups");

      foreach (var group in groups)
      {
        claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, group.Value));
      }
     }

      return next.Invoke();
    });

    app.UseStageMarker(PipelineStage.PostAuthenticate);

    AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;
    }
  }
}

#204838
Edited, Jun 19, 2019 17:56
Vote:
 

Hi Terji, most likely the infinite loop is caused by HTTP response 401 but the user is already logged in but just don't access rights to the resource requested, so you should change the response code to HTTP status 403 (forbidden).

Have a look how this is handled in this sample code with Episerver and OIDC. Same 401 to 403 status sample is in the Episerver WSFederation sample.

So have a look at those samples and check the HTTP status in debugger when the redirect loop starts.

#204839
Jun 19, 2019 19:41