web-dev-qa-db-ja.com

アクセストークンを保存する方法は? (Oauth 2、認証コードフロー)

私が承認コードフローの目的を理解しているのは、アクセストークンの認証コードを交換することです。この交換は、ページを提供するサーバーと承認サーバーの間で行われるため、実際のアクセストークンはクライアントユーザーに公開されません。

アクセストークンを取得したら、ページサーバーはどのように保存する必要がありますか?コードのこの部分があるPluralsightの例から学んでいました。

    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;
    }
}

これにより、各リクエストはアクセストークンがCookieに保存されているかどうかを確認します。そうでない場合、フローが開始されます。コールバックは次のようになります。

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);
    }
}

Cookieにアクセストークンを保存すると、認証コードフローの目的全体が損なわれませんか? Cookieはクライアントブラウザに送信されるため、クライアントに公開されますか?何か不足していますか?これはトークンを保存する正しい方法ではありませんが、どのように保存する必要がありますか?

10
BodzioSamolot

OAuth用語では、クライアントはリソースサーバーにリクエストを行うコンポーネントです。この場合、クライアントはブラウザではなくWebアプリケーションのサーバーです。

したがって、アクセストークンはWebアプリケーションサーバーのみに保存する必要があります。ブラウザーがリソースサーバーに直接要求することはないため、ブラウザーに公開する必要はありません。また、その必要はありません。代わりに、Webアプリケーションサーバーと通信し、アクセストークンを使用してリソースサーバーにリクエストを送信します。

ブラウザがWebアプリケーションサーバーで自身を認証する方法は、OAuth 2.0とは関係ありません。たとえば、通常のセッションCookieであり、Webアプリケーションサーバーが各セッションまたは各ユーザーを関連付ける場合がありますアクセストークン付き。

アクセストークンの認証コードを交換するトークンリクエストは、Webアプリケーションサーバーによって行われ、Webアプリケーションサーバーは承認サーバーで自身を認証する必要があります(たとえば、共有client_secret)。

認証コードフローにより、クライアントが認証され、正当なクライアントを装った悪意のあるクライアントから保護されます。すべてのWebアプリケーションクライアントにサーバーコンポーネントがあるわけではなく、場合によっては、リソースサーバーへの要求がブラウザーのJavaScriptコードによって直接行われます。このような状況では、ブラウザーはクライアントであり、アクセストークンはブラウザーによって(JavaScript変数、ローカルストレージ、またはCookieに)保存される必要があります。この場合、クライアントは認証できません(ただし、TLSと登録済みエンドポイントURLのみにリダイレクトするサーバーを使用することにより、妥当な量のセキュリティを実現できます)。

OAuth 2.0セキュリティ: https://tools.ietf.org/html/rfc6819#section-4.3. (RFC 6819)に関する推奨読書

18
Florian Winter

Cookieがブラウザーに公開されることはありません。これは、許可サーバーからクライアントに返される応答の一部であり、クライアント自体はブラウザーではなくサーバーです。リダイレクトエンドポイントを実装するCallbackControllerは、応答からCookieを抽出します。

クッキーがブラウザに渡されることはありません。ブラウザがクライアントのアプリケーションサーバーで自身を認証する方法はサンプルコードには示されておらず、OAuthの一部ではありません。

認可サーバーは、トークンをCookieではなくリクエスト本体(JSON形式など)に格納できます。ただし、クライアントは応答全体を確認して処理することができるため、これは違いはありません。

詳細については、他の回答を参照してください: https://stackoverflow.com/a/44655679/2279059

サイドノート:CallbackControllerstateを使用して、ブラウザをリダイレクトする最終URLを保存します。これは非標準ですが、機能します。ただし、stateは実際には、CSRF攻撃からリダイレクトエンドポイントを保護することを目的としています。 CallbackControllerstateを検証しませんが、指定されたURLに盲目的にリダイレクトします。コードは例としてのものであるため、おそらくこの詳細は省略されました。ただし、このコードはおそらく完全に本番用ではないことを示しています。

2
Florian Winter