web-dev-qa-db-ja.com

Web APIからMVCでBearer Tokenを保存する場所

シナリオ

OAuthパスワードフローを使用してベアラートークンを提供し、そのリソースにアクセスするASP.NET Web APIがあります。

現在、このAPIを使用する必要のあるMVCアプリを作成しています。

計画では、クライアントブラウザーに代わってMVCコントローラーがAPIを呼び出すようにします。

ブラウザからのajaxリクエストはMVCコントローラーにヒットし、API呼び出しが行われます。その後、結果はJSONとしてクライアントにフィードバックされ、Javaスクリプトで処理されます。

クライアントはAPIと直接通信しないでください。

認証取得

Web APIトークンエンドポイントへの正常な呼び出しを介してMVCアプリで受信したベアラートークンを処理する最適な方法を見つける必要があります。

APIへの後続の呼び出しでこのベアラートークンを使用する必要があります。

私の計画はSystem.Web.HttpContext.Current.Session["BearerToken"]に保存することです

次に、現在のHttpContextにBearerTokenが存在するかどうかを確認するカスタムAuthorizationAttributeを作成できます。存在しない場合、クライアントはトークンエンドポイントを再訪する必要があります。

これは実行可能ですか?

私はこれが私のプロジェクトにとって最良の解決策であると確信していないので、これについて人々の意見を求めています。

18
Derek

私はうまくいくと思う何かを思いついた。

Cookie認証にOwinミドルウェアを使用しています。

MVCアプリケーション内には、Cookie認証が構成されているOwinスタートアップファイルがあります。

 public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // For more information on how to configure your application, visit http://go.Microsoft.com/fwlink/?LinkID=316888

            app.UseCookieAuthentication(new CookieAuthenticationOptions()
            {
                AuthenticationType = "ApplicationCookie",
                LoginPath = new PathString("/Account/Login"),

            });
        }
    }

次に、ログインとログアウトの2つのアクションメソッドを使用してAccountControllerを作成しました。

ログイン

public ActionResult Login(LoginModel model,string returnUrl)
        {
            var getTokenUrl = string.Format(ApiEndPoints.AuthorisationTokenEndpoint.Post.Token, ConfigurationManager.AppSettings["ApiBaseUri"]);

            using (HttpClient httpClient = new HttpClient())
            {
                HttpContent content = new FormUrlEncodedContent(new[]
                {
                    new KeyValuePair<string, string>("grant_type", "password"), 
                    new KeyValuePair<string, string>("username", model.EmailAddress), 
                    new KeyValuePair<string, string>("password", model.Password)
                });

                HttpResponseMessage result = httpClient.PostAsync(getTokenUrl, content).Result;

                string resultContent = result.Content.ReadAsStringAsync().Result;

                var token = JsonConvert.DeserializeObject<Token>(resultContent);

                AuthenticationProperties options = new AuthenticationProperties();

                options.AllowRefresh = true;
                options.IsPersistent = true;
                options.ExpiresUtc = DateTime.UtcNow.AddSeconds(int.Parse(token.expires_in));

                var claims = new[]
                {
                    new Claim(ClaimTypes.Name, model.EmailAddress),
                    new Claim("AcessToken", string.Format("Bearer {0}", token.access_token)),
                };

                var identity = new ClaimsIdentity(claims, "ApplicationCookie");

                Request.GetOwinContext().Authentication.SignIn(options, identity);

            }

            return RedirectToAction("Index", "Home");
        }

ログアウト

  public ActionResult LogOut()
            {
                Request.GetOwinContext().Authentication.SignOut("ApplicationCookie");

                return RedirectToAction("Login");
            }

リソースの保護

    [Authorize]
    public class HomeController : Controller
    {

        private readonly IUserSession _userSession;

        public HomeController(IUserSession userSession)
        {
            _userSession = userSession;
        }

        // GET: Home
        public ActionResult Index()
        {

            ViewBag.EmailAddress = _userSession.Username;
            ViewBag.AccessToken = _userSession.BearerToken;

            return View();
        }
    }


 public interface IUserSession
    {
        string Username { get; }
        string BearerToken { get; }
    }

public class UserSession : IUserSession
    {

        public string Username
        {
            get { return ((ClaimsPrincipal)HttpContext.Current.User).FindFirst(ClaimTypes.Name).Value; }
        }

        public string BearerToken
        {
            get { return ((ClaimsPrincipal)HttpContext.Current.User).FindFirst("AcessToken").Value; }
        }

    }
19
Derek

言及したので、HttpClient()を使用しています。 HttpClient()を使用して同様のことを行いました。

トークンを取得-

    static Dictionary<string, string> GetTokenDetails(string userName, string password)
    {
        Dictionary<string, string> tokenDetails = null;
        try
        {
            using (var client = new HttpClient())
            {
                var login = new Dictionary<string, string>
                   {
                       {"grant_type", "password"},
                       {"username", userName},
                       {"password", password},
                   };

                var resp = client.PostAsync("http://localhost:61086/token", new FormUrlEncodedContent(login));
                resp.Wait(TimeSpan.FromSeconds(10));

                if (resp.IsCompleted)
                {
                    if (resp.Result.Content.ReadAsStringAsync().Result.Contains("access_token"))
                    {
                        tokenDetails = JsonConvert.DeserializeObject<Dictionary<string, string>>(resp.Result.Content.ReadAsStringAsync().Result);
                    }
                }
            }
        }
        catch (Exception ex)
        {

        }
        return tokenDetails;
    }

トークンを使用してデータを投稿する

static string PostData(string token, List<KeyValuePair<string, string>> lsPostContent)
{
    string response = String.Empty;
    try
    {
        using (var client = new HttpClient())
        {
            FormUrlEncodedContent cont = new FormUrlEncodedContent(lsPostContent);
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
            var resp = client.PostAsync("https://localhost:61086/api/<your API controller>/", cont);

            resp.Wait(TimeSpan.FromSeconds(10));

            if (resp.IsCompleted)
            {
                if (resp.Result.StatusCode == HttpStatusCode.Unauthorized)
                {
                    Console.WriteLine("Authorization failed. Token expired or invalid.");
                }
                else
                {
                    response = resp.Result.Content.ReadAsStringAsync().Result;
                    Console.WriteLine(response);
                }
            }
        }
    }
    catch (Exception ex)
    {

    }
    return response;
}

BearerトークンをHttpContextに格納する場合でも、Web APIで設定されているトークンの有効期限に注意する必要があります。古いトークンは有効期限が切れると無効になるため、セッション内でのみトークンの存在を検証しても効果はありません。

2
Souvik Ghosh