ASP.NET OWIN OpenID Connect не создает аутентификацию пользователя - PullRequest
2 голосов
/ 22 апреля 2019

У меня есть веб-приложение ASP.NET 4.6, в котором я пытаюсь добавить OpenId Connect с помощью OWIN.

Я добавил свой класс запуска Owin, и все, кажется, настроено правильно, но проблема в том, что яИмеющий ASP Identity / Authenticated пользователь никогда не создается.Я получаю бесконечный цикл, где страница обратного вызова OpenId перенаправляет обратно на исходную страницу, которая затем перенаправляет на страницу входа в систему и т. Д.

Вот мой класс запуска:

public void Configuration(IAppBuilder app)
    {


     app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);


        app.UseKentorOwinCookieSaver();
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Login.aspx"),
            ExpireTimeSpan = TimeSpan.FromDays(7)
        });

        app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
        {                
            ClientId = _clientId,
            ClientSecret = _clientSecret,
            Authority = _authority,
            RedirectUri = _redirectUri, // LoginCallback
            PostLogoutRedirectUri = "http://localhost:60624/Logout.aspx",

            ResponseType = OpenIdConnectResponseType.CodeIdToken,
            Scope = "openid profile email",

            TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = "name"
            },

            Notifications = new OpenIdConnectAuthenticationNotifications
            {
                AuthorizationCodeReceived = async n =>
                {
                    // Exchange code for access and ID tokens
                    var tokenClient = new TokenClient($"{_authority}/as/token.oauth2", _clientId, _clientSecret);

                    var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(n.Code, _redirectUri);
                    if (tokenResponse.IsError)
                    {
                        throw new Exception(tokenResponse.Error);
                    }

                    var userInfoClient = new UserInfoClient($"{_authority}/idp/userinfo.openid");
                    var userInfoResponse = await userInfoClient.GetAsync(tokenResponse.AccessToken);

                    var claims = new List<Claim>(userInfoResponse.Claims)
                      {
                        new Claim("id_token", tokenResponse.IdentityToken),
                        new Claim("access_token", tokenResponse.AccessToken)
                      };

                    n.AuthenticationTicket.Identity.AddClaims(claims);



                    //// create the identity
                    //var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationType);

                    //System.Web.HttpContext.Current.GetOwinContext().Authentication.SignIn(new AuthenticationProperties
                    //{
                    //    IsPersistent = true
                    //}, identity);
                }
            }
        });
    }

Вот страница Login.aspx:

 protected void Page_Load(object sender, EventArgs e)
    {

        if (!Request.IsAuthenticated)
        {
            HttpContext.Current.GetOwinContext().Authentication.Challenge(
              new AuthenticationProperties { RedirectUri = Request["ReturnUrl"] ?? "Default.aspx" },
              OpenIdConnectAuthenticationDefaults.AuthenticationType);
        }        
    }

Поток страниц выглядит так:

1) Запрос: http://localhost:60624/Page.aspx Ответ: 302 - перенаправление на Login.aspx

2) Запрос: http://localhost:60624/Login.aspx?ReturnUrl=%2FPage.aspx Ответ 302 - перенаправление на https://auth.myprovider.com

Некоторые файлы cookie, установленные здесь в заголовках ответа:

Set-Cookie: OpenIdConnect.nonce.KIsuj4RUmGKJIynLrkEScxBvGrZzkMo6ylZ% 2F4lRknPM% 3D = XXXXXXXXX;Путь = /;истекает = понедельник, 22 апреля 2019 года 14:12:00 по Гринвичу;HttpOnly Set-Cookie: OpenIdConnect.nonce.KIsuj4RUmGKJIynLrkEScxBvGrZzkMo6ylZ% 2F4lRknPM% 3D = ггггггггг;истекает = понедельник, 22 апреля 2019 года 14:12:00 по Гринвичу;Путь = /;HttpOnly

3) Провайдер аутентификации, вход, и он 302 перенаправляет на / LoginCallback

4) Запрос: http://localhost:60624/LoginCallback Ответ 302 - перенаправить на /Page.aspx

Здесь очищаются файлы cookie, которые были установлены на шаге 2.

Set-Cookie: OpenIdConnect.nonce.KIsuj4RUmGKJIynLrkEScxBvGrZzkMo6ylZ% 2F4lRknPM% 3D =;Путь = /;expires = Четверг, 01 января 1970 года, 00:00:00 GMT Набор файлов cookie: OpenIdConnect.nonce.KIsuj4RUmGKJIynLrkEScxBvGrZzkMo6ylZ% 2F4lRknPM% 3D =;истекает = чт, 01 января 1970 г. 00:00:00 по Гринвичу;путь = /

5) Назад к Page.aspx, пользователь не аутентифицирован;Перейти к шагу 1

Я выполнил некоторую отладку, и AuthorizationCodeReceived запускается при запуске, а бэкэнд успешно вызывает конечную точку сведений о пользователе.Я пытался вызвать System.Web.HttpContext.Current.GetOwinContext (). Authentication.SignIn () из этого уведомления, но это, похоже, ничего не делает.

На данный момент язастрял.Почему cookie-файл для аутентификации пользователя не устанавливается?Кажется, это должно происходить автоматически.Я должен вручную создать это сам?( Как я могу вручную создать файл cookie для аутентификации вместо метода по умолчанию? )

РЕДАКТИРОВАТЬ: После просмотра ответа @ Zaxxon я смог его получитьза работой.В уведомлении AuthorizationCodeReceived было 2 ошибки:

  1. Мне нужно было создать ClaimsIdentity.В моем исходном коде, который я представил выше, я закомментировал это, но это также было неверно.
  2. Мне пришлось заменить AuthenticationTicket на новый с новым идентификатором, который я только что создал.Затем добавьте претензии к этому новому идентификатору.

Вот рабочий код:

ClaimsIdentity identity = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie, ClaimTypes.GivenName, ClaimTypes.Role);
 n.AuthenticationTicket = new AuthenticationTicket(identity, n.AuthenticationTicket.Properties);
 n.AuthenticationTicket.Identity.AddClaims(claims);

1 Ответ

1 голос
/ 29 апреля 2019

Да, я должен был заставить Proof of Concept работать над этим некоторое время назад в VB.Net, что было несколько болезненно. Вот мой тестовый код (то есть не рабочий код), который основан на некоторых других интернет-примерах C #, которые я видел:

Imports System.Security.Claims
Imports System.Threading.Tasks
Imports IdentityModel
Imports IdentityModel.Client
Imports Microsoft.AspNet.Identity
Imports Microsoft.AspNet.Identity.Owin
Imports Microsoft.IdentityModel.Protocols.OpenIdConnect
Imports Microsoft.Owin
Imports Microsoft.Owin.Security
Imports Microsoft.Owin.Security.Cookies
Imports Microsoft.Owin.Security.Notifications
Imports Microsoft.Owin.Security.OAuth
Imports Microsoft.Owin.Security.OpenIdConnect
Imports Owin

Partial Public Class Startup
    Private Shared _oAuthOptions As OAuthAuthorizationServerOptions
    Private Shared _publicClientId As String

    Private Shared _clientId As String
    Private Shared _clientSecret As String

    ' Enable the application to use OAuthAuthorization. You can then secure your Web APIs
    Shared Sub New()

        _clientId = System.Configuration.ConfigurationManager.AppSettings("OAuth:ClientID").ToString()
        _clientSecret = System.Configuration.ConfigurationManager.AppSettings("OAuth:SecretKey").ToString()

        PublicClientId = _clientId

        OAuthOptions = New OAuthAuthorizationServerOptions() With {
            .TokenEndpointPath = New PathString("/Token"), 'New PathString("https://authtesteria.domain.com/as/token.oauth2"), ' 
            .AuthorizeEndpointPath = New PathString("/Account/Authorize"), 'New PathString("https://authtesteria.domain.com/as/authorization.oauth2"), '
            .Provider = New ApplicationOAuthProvider(PublicClientId),
            .AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            .AllowInsecureHttp = True
        }
    End Sub

    Public Shared Property OAuthOptions() As OAuthAuthorizationServerOptions
        Get
            Return _oAuthOptions
        End Get
        Private Set
            _oAuthOptions = Value
        End Set
    End Property

    Public Shared Property PublicClientId() As String
        Get
            Return _publicClientId
        End Get
        Private Set
            _publicClientId = Value
        End Set
    End Property

    ' For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864
    Public Sub ConfigureAuth(app As IAppBuilder)
        ' Configure the db context, user manager and signin manager to use a single instance per request
        app.CreatePerOwinContext(AddressOf ApplicationDbContext.Create)
        app.CreatePerOwinContext(Of ApplicationUserManager)(AddressOf ApplicationUserManager.Create)
        app.CreatePerOwinContext(Of ApplicationSignInManager)(AddressOf ApplicationSignInManager.Create)

        ' Enable the application to use a cookie to store information for the signed in user
        ' and to use a cookie to temporarily store inforation about a user logging in with a third party login provider
        ' Configure the sign in cookie
        ' OnValidateIdentity enables the application to validate the security stamp when the user logs in.
        ' This is a security feature which is used when you change a password or add an external login to your account.
        app.UseCookieAuthentication(New CookieAuthenticationOptions() With {
            .AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            .Provider = New CookieAuthenticationProvider() With {
                .OnValidateIdentity = SecurityStampValidator.OnValidateIdentity(Of ApplicationUserManager, ApplicationUser)(
                    validateInterval:=TimeSpan.FromMinutes(30),
                    regenerateIdentity:=Function(manager, user) user.GenerateUserIdentityAsync(manager))},
            .LoginPath = New PathString("/Account/Login")})


        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie)

        ' Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
        app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5))

        ' Enables the application to remember the second login verification factor such as phone or email.
        ' Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
        ' This is similar to the RememberMe option when you log in.
        app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie)

        ' Enable the application to use bearer tokens to authenticate users
        app.UseOAuthBearerTokens(OAuthOptions)

        Dim controller As New AccountController()

        'Dim validator As OpenIdConnectProtocolValidator = New OpenIdConnectProtocolValidator()
        'validator.ShowPII = False

        Dim oidcAuth As New Microsoft.Owin.Security.OpenIdConnect.OpenIdConnectAuthenticationOptions() With {
            .ClientId = _clientId,
            .ClientSecret = _clientSecret,
            .Authority = "https://authtesteria.domain.com",
            .Notifications = New Microsoft.Owin.Security.OpenIdConnect.OpenIdConnectAuthenticationNotifications() With {
                .RedirectToIdentityProvider = AddressOf OnRedirectToIdentityProvider,
                .MessageReceived = AddressOf OnMessageReceived,
                .SecurityTokenReceived = AddressOf OnSecurityTokenReceived,
                .SecurityTokenValidated = AddressOf OnSecurityTokenValidated,
                .AuthorizationCodeReceived = AddressOf OnAuthorizationCodeReceived,
                .AuthenticationFailed = AddressOf OnAuthenticationFailed
        }}
        app.UseOpenIdConnectAuthentication(oidcAuth)

    End Sub

    Private Function OnRedirectToIdentityProvider(arg As RedirectToIdentityProviderNotification(Of Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage, OpenIdConnectAuthenticationOptions)) As Task
        Debug.WriteLine("*** RedirectToIdentityProvider")

        If arg.ProtocolMessage.RequestType = Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectRequestType.Logout Then
            Dim idTokenHint = arg.OwinContext.Authentication.User.FindFirst("id_token")

            If idTokenHint IsNot Nothing Then
                arg.ProtocolMessage.IdTokenHint = idTokenHint.Value
            End If
        End If
        Return Task.FromResult(0)
    End Function

    Private Function OnMessageReceived(arg As MessageReceivedNotification(Of Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage, OpenIdConnectAuthenticationOptions)) As Task
        Debug.WriteLine("*** MessageReceived")
        Return Task.FromResult(0)
    End Function

    Private Function OnAuthorizationCodeReceived(arg As AuthorizationCodeReceivedNotification) As Task
        Debug.WriteLine("*** AuthorizationCodeReceived")
        'Upon successful sign in, get & cache a token if you want here
        Return Task.FromResult(0)
    End Function

    Private Function OnAuthenticationFailed(arg As AuthenticationFailedNotification(Of Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage, OpenIdConnectAuthenticationOptions)) As Task
        Debug.WriteLine("*** AuthenticationFailed")
        Return Task.FromResult(0)
    End Function

    Private Function OnSecurityTokenReceived(arg As SecurityTokenReceivedNotification(Of Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage, OpenIdConnectAuthenticationOptions)) As Task
        Debug.WriteLine("*** SecurityTokenReceived")
        Return Task.FromResult(0)
    End Function

    Private Async Function OnSecurityTokenValidated(arg As SecurityTokenValidatedNotification(Of Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage, OpenIdConnectAuthenticationOptions)) As Task
        Debug.WriteLine("*** SecurityTokenValidated")
        'Verify the user signing in should have access or not.  Here I just pass folk thru.
        Dim nid = New ClaimsIdentity(
              DefaultAuthenticationTypes.ApplicationCookie, 'arg.AuthenticationTicket.Identity.AuthenticationType,
              ClaimTypes.GivenName,
              ClaimTypes.Role)

        Dim tokenClient = New TokenClient("https://authtesteria.domain.com/as/token.oauth2",
             _clientId,
             _clientSecret)

        Dim tokenResponse = Await tokenClient.RequestAuthorizationCodeAsync(arg.ProtocolMessage.Code, arg.ProtocolMessage.RedirectUri)

        ' get userinfo data
        Dim userInfoClient = New IdentityModel.Client.UserInfoClient("https://authtesteria.domain.com/idp/userinfo.openid")

        Dim userInfo = Await userInfoClient.GetAsync(tokenResponse.AccessToken)
        userInfo.Claims.ToList().ForEach(Sub(ui) nid.AddClaim(New Claim(ui.Type, ui.Value)))

        '' keep the id_token for logout
        'nid.AddClaim(New Claim("id_token", arg.ProtocolMessage.IdToken))

        '' add access token for sample API
        'nid.AddClaim(New Claim("access_token", arg.ProtocolMessage.AccessToken))

        '' keep track of access token expiration
        'nid.AddClaim(New Claim("expires_at", DateTimeOffset.Now.AddSeconds(Integer.Parse(arg.ProtocolMessage.ExpiresIn)).ToString()))

        '' add some other app specific claim
        'nid.AddClaim(New Claim("app_specific", "some data"))

        nid.AddClaim(New Claim(ClaimTypes.Role, "group1"))

        arg.AuthenticationTicket = New AuthenticationTicket(nid, arg.AuthenticationTicket.Properties)
        arg.AuthenticationTicket.Properties.RedirectUri = HttpContext.Current.Session("PageRedirect").ToString() 
    End Function
End Class

Теперь я запускаю логин так:

Private Sub SomePageName_Load(sender As Object, e As EventArgs) Handles Me.Load
    If Not IsPostBack Then
        If User.Identity.IsAuthenticated Then
            Console.WriteLine(User.Identity.GetUserName())
        Else
            Session("PageRedirect") = Request.Url
            Response.Redirect("/")
        End If
    End If
End Sub

У нас есть несколько отличий:

  1. Я использую OnSecurityTokenValidated, но я не уверен, имеет ли это значение или нет
  2. Я заполняю переменную Session текущей страницей Request.Url,
  3. затем используйте это при запуске в параметре уведомления OnSecurityTokenValidated: arg.AuthenticationTicket.Properties.RedirectUri =… (см. Мой код).

Надеюсь, это поможет. Наслаждайтесь!

...