web-dev-qa-db-ja.com

ユーザーインターフェースなしのWeb APIにおけるトークンベースの認証

私はASP.NET Web APIでREST AP​​Iを開発しています。私のAPIは非ブラウザベースのクライアントを介してのみアクセス可能になります。トークンベースの認証を使用することにしたので、私は自分のAPIにセキュリティを実装する必要があります。私はトークンベースの認証についてかなり理解しており、いくつかのチュートリアルを読んでいますが、それらはすべてログイン用のユーザーインターフェイスを持っています。ログインの詳細はHTTP POSTを介してクライアントから渡されるので、ログイン用のUIは必要ありません。これはデータベースから認証されます。 APIにトークンベースの認証を実装する方法を教えてください。私のAPIは頻繁にアクセスされるので注意してください - 私もパフォーマンスの面倒を見なければなりません。これ以上説明できないか教えてください。

59
Souvik Ghosh

MVCとWeb Apiの違いについて混乱があると思います。つまり、MVCでは、ログインフォームを使用してCookieを使用してセッションを作成することができます。 Web Apiの場合、セッションはありません。だからトークンを使いたいのです。

あなたはログインフォームを必要としません。トークンエンドポイントはあなたが必要とするすべてです。 Winが説明したように、あなたはそれが処理されるトークンエンドポイントにあなたが資格情報を送るでしょう。

これは、トークンを取得するためのクライアント側のC#コードです。

    //using System;
    //using System.Collections.Generic;
    //using System.Net;
    //using System.Net.Http;
    //string token = GetToken("https://localhost:<port>/", userName, password);

    static string GetToken(string url, string userName, string password) {
        var pairs = new List<KeyValuePair<string, string>>
                    {
                        new KeyValuePair<string, string>( "grant_type", "password" ), 
                        new KeyValuePair<string, string>( "username", userName ), 
                        new KeyValuePair<string, string> ( "Password", password )
                    };
        var content = new FormUrlEncodedContent(pairs);
        ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
        using (var client = new HttpClient()) {
            var response = client.PostAsync(url + "Token", content).Result;
            return response.Content.ReadAsStringAsync().Result;
        }
    }

トークンを使用するには、それをリクエストのヘッダに追加します。

    //using System;
    //using System.Collections.Generic;
    //using System.Net;
    //using System.Net.Http;
    //var result = CallApi("https://localhost:<port>/something", token);

    static string CallApi(string url, string token) {
        ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
        using (var client = new HttpClient()) {
            if (!string.IsNullOrWhiteSpace(token)) {
                var t = JsonConvert.DeserializeObject<Token>(token);

                client.DefaultRequestHeaders.Clear();
                client.DefaultRequestHeaders.Add("Authorization", "Bearer " + t.access_token);
            }
            var response = client.GetAsync(url).Result;
            return response.Content.ReadAsStringAsync().Result;
        }
    }

トークンは次のとおりです。

//using Newtonsoft.Json;

class Token
{
    public string access_token { get; set; }
    public string token_type { get; set; }
    public int expires_in { get; set; }
    public string userName { get; set; }
    [JsonProperty(".issued")]
    public string issued { get; set; }
    [JsonProperty(".expires")]
    public string expires { get; set; }
}

今サーバー側のために:

Startup.Auth.cs内

        var oAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new ApplicationOAuthProvider("self"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            // https
            AllowInsecureHttp = false
        };
        // Enable the application to use bearer tokens to authenticate users
        app.UseOAuthBearerTokens(oAuthOptions);

ApplicationOAuthProvider.csでは、実際にアクセスを許可または拒否するコードは次のようになります。

//using Microsoft.AspNet.Identity.Owin;
//using Microsoft.Owin.Security;
//using Microsoft.Owin.Security.OAuth;
//using System;
//using System.Collections.Generic;
//using System.Security.Claims;
//using System.Threading.Tasks;

public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
    private readonly string _publicClientId;

    public ApplicationOAuthProvider(string publicClientId)
    {
        if (publicClientId == null)
            throw new ArgumentNullException("publicClientId");

        _publicClientId = publicClientId;
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();

        var user = await userManager.FindAsync(context.UserName, context.Password);
        if (user == null)
        {
            context.SetError("invalid_grant", "The user name or password is incorrect.");
            return;
        }

        ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager);
        var propertyDictionary = new Dictionary<string, string> { { "userName", user.UserName } };
        var properties = new AuthenticationProperties(propertyDictionary);

        AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
        // Token is validated.
        context.Validated(ticket);
    }

    public override Task TokenEndpoint(OAuthTokenEndpointContext context)
    {
        foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
        {
            context.AdditionalResponseParameters.Add(property.Key, property.Value);
        }
        return Task.FromResult<object>(null);
    }

    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        // Resource owner password credentials does not provide a client ID.
        if (context.ClientId == null)
            context.Validated();

        return Task.FromResult<object>(null);
    }

    public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
    {
        if (context.ClientId == _publicClientId)
        {
            var expectedRootUri = new Uri(context.Request.Uri, "/");

            if (expectedRootUri.AbsoluteUri == context.RedirectUri)
                context.Validated();
        }
        return Task.FromResult<object>(null);
    }

}

ご覧のとおり、トークンの取得に関与するコントローラはありません。実際、Web Apiだけが必要な場合は、すべてのMVC参照を削除できます。読みやすくするためにサーバー側のコードを簡略化しました。セキュリティを向上させるためにコードを追加することができます。

必ずSSLのみを使用してください。これを強制するためにRequireHttpsAttributeを実装します。

Authorize/AllowAnonymous属性を使用してWeb Apiを保護することができます。さらにあなたのWeb Apiをより安全にするために(RequireHttpsAttributeのような)フィルタを追加することができます。これが役に立つことを願っています。

75

ASP.Net Web APIには既にAuthorization Serverが組み込まれています。 Web APIテンプレートを使用して新しいASP.Net Webアプリケーションを作成すると、 Startup.cs の中に表示されます。

OAuthOptions = new OAuthAuthorizationServerOptions
{
    TokenEndpointPath = new PathString("/Token"),
    Provider = new ApplicationOAuthProvider(PublicClientId),
    AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
    // In production mode set AllowInsecureHttp = false
    AllowInsecureHttp = true
};

あなたがしなければならないのは、クエリ文字列の中にURLエンコードされたユーザー名とパスワードを投稿することだけです。

/Token/userName=johndoe%40example.com&password=1234&grant_type=password

詳細を知りたい場合は、 をご覧ください。ユーザー登録とログイン - Angular Deborah KurataによるWeb APIを使用したフロントツーバック

18
Win