web-dev-qa-db-fra.com

Comment stocker le jeton d'accès? (Oauth 2, flux de code d'authentification)

D'après ce que je comprends, l'objectif du flux de codes d'autorisation est d'échanger le code d'autorisation contre un jeton d'accès. Cet échange a lieu entre le serveur qui dessert la page et le serveur d'autorisation afin que le jeton d'accès réel ne soit pas exposé à l'utilisateur client.

Comment le serveur de pages doit-il stocker le jeton d'accès une fois qu'il est obtenu? J'apprenais d'un exemple Pluralsight dans lequel il y a cette partie de code:

    public static HttpClient GetClient()
    {
        HttpClient client = new HttpClient();
        var accessToken = RequestAccessTokenAuthorizationCode();
        client.SetBearerToken(accessToken);

        client.BaseAddress = new Uri(IdentityConstants.API);
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/json"));

        return client;
    }

    private static string RequestAccessTokenAuthorizationCode()
    {
        // did we store the token before?
        var cookie = HttpContext.Current.Request.Cookies.Get("ClientMVCCookie.AuthCode");
        if (cookie != null && cookie["access_token"] != null && !string.IsNullOrEmpty(cookie["access_token"]))
        {
            return cookie["access_token"];
        }

        // no token found - request one

        // we'll pass through the URI we want to return to as state
        var state = HttpContext.Current.Request.Url.OriginalString;

        var authorizeRequest = new IdentityModel.Client.AuthorizeRequest(
            IdentityConstants.AuthEndoint);

        var url = authorizeRequest.CreateAuthorizeUrl(IdentityConstants.MVCClientSecret, "code", "management secret",
            IdentityConstants.MVCAuthCodeCallback, state);

        HttpContext.Current.Response.Redirect(url);

        return null;
    }
}

Ainsi, chaque demande vérifiera si un jeton d'accès est stocké dans le cookie. Sinon, le flux sera lancé. Le rappel ressemble à ceci:

public class CallbackController : Controller
{
    // GET: STSCallback
    public async Task<ActionResult> Index()
    {
        // get the authorization code from the query string
        var authCode = Request.QueryString["code"];

        // with the auth code, we can request an access token.
        var client = new TokenClient(
            IdentityConstants.TokenEndoint,
            "mvc_client_auth_code",
             IdentityConstants.MVCClientSecretAuthCode);

        var tokenResponse = await client.RequestAuthorizationCodeAsync(
            authCode,
            IdentityConstants.MVCAuthCodeCallback);

        // we save the token in a cookie for use later on
        var cookie = Response.Cookies["ClientMVCCookie.AuthCode"];
        cookie.Expires = DateTime.Now.AddMinutes(1);
        cookie["access_token"] = tokenResponse.AccessToken;

        // get the state (uri to return to)
        var state = Request.QueryString["state"];

        // redirect to the URI saved in state
        return Redirect(state);
    }
}

Le fait de stocker le jeton d'accès dans le cookie ne défraie-t-il pas la finalité du flux de codes d'autorisation? Le cookie sera transmis au navigateur du client, l'exposant ainsi au client? Est-ce que je manque quelque chose? Si ce n'est pas la bonne façon de stocker le jeton, comment devrait-il être stocké?

7
BodzioSamolot

Le client, dans la terminologie OAuth, est le composant qui envoie des requêtes au serveur de ressources. Dans votre cas, le client est le serveur d'une application Web (PAS le navigateur).

Par conséquent, le jeton d'accès doit être stocké sur le serveur d'applications Web uniquement. Il ne doit pas être exposé au navigateur, ce n'est pas nécessaire, car le navigateur ne demande jamais directement au serveur de ressources. Au lieu de cela, il communique avec le serveur d'applications Web, qui envoie ensuite des requêtes au serveur de ressources à l'aide du jeton d'accès.

La manière dont le navigateur s'authentifie auprès du serveur d'applications Web n'a rien à voir avec OAuth 2.0. Par exemple, il peut s'agir d'un cookie de session ordinaire et le serveur d'applications Web peut associer chaque session ou chaque utilisateur à un jeton d'accès.

La demande de jeton, qui échange le code d'authentification pour un jeton d'accès, est effectuée par le serveur d'applications Web. Le serveur d'applications Web doit s'authentifier auprès du serveur d'autorisation (par exemple, à l'aide d'un client_secret partagé).

Le flux de code d'autorisation garantit que le client peut être authentifié, ce qui protège contre les clients malveillants qui se présentent comme des clients légitimes. Tous les clients d'applications Web ne disposent pas d'un composant serveur et, dans certains cas, les demandes adressées au serveur de ressources sont effectuées directement par le code JavaScript dans le navigateur. Dans de telles situations, le navigateur est le client et le jeton d'accès doit être stocké par le navigateur (dans une variable JavaScript, un stockage local ou un cookie). Dans ce cas, le client ne peut pas être authentifié (mais une sécurité raisonnable peut être obtenue en utilisant TLS et en redirigeant le serveur uniquement vers les URL de point final enregistrées).

Lecture recommandée concernant la sécurité OAuth 2.0: https://tools.ietf.org/html/rfc6819#section-4.3.3 (RFC 6819)

14
Florian Winter

Le cookie n'est jamais exposé au navigateur. Cela fait partie de la réponse renvoyée par le serveur d'autorisation au client, qui est lui-même un serveur et non un navigateur. La CallbackController, qui implémente le point de terminaison de la redirection, extrait le cookie de la réponse.

Le cookie n'est jamais transmis au navigateur. La manière dont le navigateur s'authentifie auprès du serveur d'applications du client n'apparaît pas dans votre exemple de code et ne fait pas partie de OAuth.

Le serveur d'autorisation peut stocker le jeton dans le corps de la demande (au format JSON, par exemple) plutôt que dans un cookie. Cependant, cela ne fait aucune différence, car le client peut quand même voir et traiter la totalité de la réponse.

Pour plus de détails, voir mon autre réponse: https://stackoverflow.com/a/44655679/2279059

Note latérale: La CallbackController utilise state pour stocker une URL finale vers laquelle rediriger le navigateur. Ceci est non standard mais fonctionne. Cependant, state est en réalité destiné à protéger les points finaux de redirection contre les attaques CSRF. CallbackController ne valide pas state mais redirige aveuglément vers n'importe quelle URL donnée. Ce détail a probablement été laissé de côté, car le code était conçu à titre d'exemple. Cependant, cela montre que ce code n'est probablement pas entièrement prêt pour la production.

2
Florian Winter