web-dev-qa-db-fra.com

AddopenidConnect et Actualiser les jetons dans le noyau ASP.NET

J'ai ajouté AddOpenIdConnect à la méthode ConfigureServices de mon application ASP.NET CORE 3.1 RAZOR. Cela fonctionne bien jusqu'à ce que le jeton expire, puis je reçois 401 réponses de mon PDI.

J'ai vu un exemple qui montre un moyen de rafraîchir les jetons de rafraîchir manuellement.

Mais je suis hésitant à faire ça. Il semble super peu probable que les gens de Microsoft ne pensaient pas à rafraîchir les jetons.

ASP.NET CORE 3.1 ont un moyen d'avoir actuellement des jetons de rafraîchissement à jour automatiquement le jeton d'accès?

7
Vaccano

Voici ce que je suis venu avec. Comme il n'y a pas beaucoup d'exemples que je pouvais trouver sur la manière de rafraîchir les jetons dans le noyau ASP.NET avec des cookies, je pensais que je posterais cela ici. (Celui que je lie à la question a des problèmes.)

C'est juste ma tentative d'obtenir ce travail. Il n'a pas été utilisé dans aucun cadre de production. Ce code va dans la méthode ConfigureServices.

services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
    options.Events = new CookieAuthenticationEvents
    {
        // After the auth cookie has been validated, this event is called.
        // In it we see if the access token is close to expiring.  If it is
        // then we use the refresh token to get a new access token and save them.
        // If the refresh token does not work for some reason then we redirect to 
        // the login screen.
        OnValidatePrincipal = async cookieCtx =>
        {
            var now = DateTimeOffset.UtcNow;
            var expiresAt = cookieCtx.Properties.GetTokenValue("expires_at");
            var accessTokenExpiration = DateTimeOffset.Parse(expiresAt);
            var timeRemaining = accessTokenExpiration.Subtract(now);
            // TODO: Get this from configuration with a fall back value.
            var refreshThresholdMinutes = 5;
            var refreshThreshold = TimeSpan.FromMinutes(refreshThresholdMinutes);

            if (timeRemaining < refreshThreshold)
            {
                var refreshToken = cookieCtx.Properties.GetTokenValue("refresh_token");
                // TODO: Get this HttpClient from a factory
                var response = await new HttpClient().RequestRefreshTokenAsync(new RefreshTokenRequest
                {
                    Address = tokenUrl,
                    ClientId = clientId,
                    ClientSecret = clientSecret,
                    RefreshToken = refreshToken
                });

                if (!response.IsError)
                {
                    var expiresInSeconds = response.ExpiresIn;
                    var updatedExpiresAt = DateTimeOffset.UtcNow.AddSeconds(expiresInSeconds);
                    cookieCtx.Properties.UpdateTokenValue("expires_at", updatedExpiresAt.ToString());
                    cookieCtx.Properties.UpdateTokenValue("access_token", response.AccessToken);
                    cookieCtx.Properties.UpdateTokenValue("refresh_token", response.RefreshToken);

                    // Indicate to the cookie middleware that the cookie should be remade (since we have updated it)
                    cookieCtx.ShouldRenew = true;
                }
                else
                {
                    cookieCtx.RejectPrincipal();
                    await cookieCtx.HttpContext.SignOutAsync();
                }
            }
        }
    };
})
.AddOpenIdConnect(options =>
{
    options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;

    options.Authority = oidcDiscoveryUrl;
    options.ClientId = clientId;
    options.ClientSecret = clientSecret;

    options.RequireHttpsMetadata = true;

    options.ResponseType = OidcConstants.ResponseTypes.Code;
    options.UsePkce = true;
    // This scope allows us to get roles in the service.
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("offline_access");

    // This aligns the life of the cookie with the life of the token.
    // Note this is not the actual expiration of the cookie as seen by the browser.
    // It is an internal value stored in "expires_at".
    options.UseTokenLifetime = true;
    options.SaveTokens = true;
});

Ce code a deux parties:

  1. AddOpenIdConnect: cette partie du code établit l'OIDC pour l'application. Paramètres de clé ici sont:
    • SignInScheme: cela permet à ASP.NET CORE savez que vous souhaitez utiliser des cookies pour stocker vos informations d'authentification.
    • * UseTokenLifetime: si je comprends bien, cela définit une valeur interne "expire_at" dans le cookie pour être la durée de vie du jeton d'accès. (Pas l'expiration réelle des cookies, qui reste au niveau de la session.)
    • * SaveTokens: si je comprends bien, c'est ce qui provoque l'enregistrement des jetons dans le cookie.
  2. OnValidatePrincipal: cette section est appelée lorsque le cookie a été validé. Dans cette section, nous vérifions si le jeton d'accès est une expiration proche ou passée. Si c'est alors il est actualisé et que les valeurs mises à jour sont stockées dans le cookie. Si le jeton ne peut pas être actualisé, l'utilisateur est redirigé vers l'écran de connexion.

Le code utilise ces valeurs qui doivent provenir de votre fichier de configuration:

  • clientId: ID client OAUTH2. Également appelé clé client, clé de consommation, etc.
  • clientSecret: Secret client Oauth2. Également appelé secret de consommation, etc.
  • oidcDiscoveryUrl: une partie de base de l'URL au document de configuration bien connu de votre IDP. Si votre document de configuration bien connu est à https://youridp.domain.com/oauth2/oidcdiscovery/.well-known/openid-configuration alors cette valeur serait https://youridp.domain.com/oauth2/oidcdiscovery.
  • tokenUrl: URL sur le point de terminaison de votre jeton de votre IDP. Par exemple: https:/youridp.domain.com/oauth2/token
  • refreshThresholdMinutes: Si vous attendez que le jeton d'accès soit très proche de l'expiration, vous courez le risque d'échec des appels qui comptent sur le jeton d'accès. (S'il s'agit de 5 milisecondes de l'expiration, il pourrait expirer et échouer un appel, avant d'avoir une chance de vous rafraîchir.) Ce paramètre est le nombre de minutes avant l'expiration que vous souhaitez envisager un jeton d'accès prêt à être rafraîchi.

* Je suis nouveau au noyau ASP.NET. En tant que tel, je ne suis pas sûr à 100% que ces paramètres font ce que je pense. Ceci est juste un peu de code qui travaille pour moi et je pensais que je le parrains. Cela peut ou peut ne pas fonctionner pour vous.

4
Vaccano

AddOpenIdConnect est utilisé pour configurer le gestionnaire qui effectue le protocole OpenID Connect pour obtenir des jetons de votre fournisseur d'identité. Mais cela ne sait pas où vous voulez sauver les jetons. Cela pourrait être l'une des suivantes:

  • Cookie
  • Mémoire
  • Base de données

Vous pouvez stocker les jetons dans un cookie puis vérifier l'heure d'expiration du jeton et rafraîchissez les jetons en interceptant l'événement de validation de la cookie (comme l'exemple de l'exemple).

Mais AddOpenIdConnect _ N'a pas la logique de contrôler l'emplacement de l'utilisateur de stocker les jetons et de mettre en œuvre automatiquement le jeton Actualiser.

Vous pouvez également essayer d'envelopper le middleware comme l'adal.net/msal.net pour fournir des fonctions de cache, puis vous pouvez acquérir/rafraîchir les jetons en silence.

0
Nan Yu