OWIN OpenId Connect auth и авторизация на основе ролей - PullRequest
0 голосов
/ 27 марта 2020

У меня есть приложение WebForms (не MVC, не WebApi), которое я портирую на внешнюю аутентификацию OpenID Connect (. net 4.7.2, последние пакеты OWIN NuGet). Это веб-приложение использует авторизацию на основе ролей, чтобы предотвратить доступ неуполномоченных пользователей к некоторым частям приложения. Роли предоставляются через утверждения OID C и указываются в веб-приложении как web.config записи авторизации.

Основной поток работает: приложение правильно перенаправляет пользователей поставщику OID C, если пользователь не аутентифицированы, и пользователь имеет соответствующие роли в своем ClaimsIdentity.

Проблема возникает, когда пользователь вошел в систему, но не имеет роли, необходимой для доступа к областям с ограниченными ролями. В этом случае UrlAuthorizationModule возвращает 401 Несанкционированную ошибку (разве это не должно быть 403 Запрещено ?!), которая вызывает вызов OID C, который отправляется поставщику OID C, который возвращает ту же самую пользователь с теми же утверждениями, который запускает другой 401, и пользователь испытывает неограниченное перенаправление l oop.

. Я могу обнаружить условие в уведомлении RedirectToIdentityProvider и использовать метод Response.Redirect для визуализации сообщение об ошибке пользователю, но это приводит к последовательности ошибок 302> 403 и изменяет URL-адрес браузера на страницу 403.aspx. Я предпочел бы переписать ответ, сохранив текущий URL, но не могу понять, как (отметьте * отмеченные строки внутри фрагмента Startup.cs).

У вас есть идеи?

Соответствующий фрагмент файла Startup.cs:

app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions() { });

app.UseOpenIdConnectAuthentication(
    new OpenIdConnectAuthenticationOptions {
        ClientId = ClientId,
        ClientSecret = ClientSecret,
        Authority = Authority,
        Scope = Scopes,
        TokenValidationParameters = new TokenValidationParameters()
        {
            NameClaimType = ClaimTypes.NameIdentifier,
        },
        Notifications = new OpenIdConnectAuthenticationNotifications
        {
            SecurityTokenValidated = n =>
            {
                // Store the ID Token in the auth ticket to be able to remote logout
                if (n.ProtocolMessage.IdToken != null)
                {
                    var idTokenClaim = new Claim("id_token", n.ProtocolMessage.IdToken, ClaimValueTypes.String);
                    n.AuthenticationTicket.Identity.AddClaim(idTokenClaim);
                }

                return Task.FromResult(0);
            },
            RedirectToIdentityProvider = n =>
            {
                // If authenticating, compute the redirect URI from the current request
                if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
                {
                    n.ProtocolMessage.RedirectUri = new Uri(n.Request.Uri, "/authorization-code/callback").ToString();

                    // Make sure we're here because we're unauthenticated users and not
                    // just authenticated users without a valid role
                    if (n.OwinContext.Authentication.User.Identity.IsAuthenticated &&
                        n.OwinContext.Authentication.User.FindFirst(t => t.Type == ClaimTypes.Role && t.Value == "UserEmailValid") == null)
                    {
*                       // Pass control to the error message page
*                       // n.Response.Redirect(VirtualPathUtility.ToAbsolute("~/Errors/403.aspx"));
*                       n.OwinContext.Request.Path = new PathString(VirtualPathUtility.ToAbsolute("~/Errors/403.aspx"));
*** What here? I'm not in a middleware, this is a notification! ***
*                       n.HandleResponse();
                    }
                }

                // If signing out, add the id_token_hint
                if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
                {
                    var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");
                    if (idTokenHint != null)
                        n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
                }

                return Task.FromResult(0);
            }
        }
    })
    .UseStageMarker(PipelineStage.Authenticate);

Пример авторизации web.config:

<configuration>
  <system.web>
    <authorization>
      <allow roles="UserEmailValid" />
      <deny users="*" />
    </authorization>
  </system.web>
</configuration>
...