2017-11-03 4 views
2

私はWebサービスを持つWebアプリケーションを持っています。クライアントはWebアプリケーションを使用してアプリケーションを登録します。エンドポイントをセキュアにするwebapiのトークンベースの実装

クライアントはタイプSPAまたはモバイルアプリのアプリケーションを持ち、アプリケーションから自分のWebサービスを消費します。

私はトークンベースのメカニズムを実装して、エンドポイントへのアクセスを保護します。

1)しかし、ここで私は、私がアクセストークンを生成するために、任意のフレームワークを使用しなければならないか、私はこのようなresponse.forインスタンス何かに送信されます任意のランダムな文字列を生成する任意のライブラリを使用することができる混乱しています:

TokenId = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace("+", "_") 

クライアントがアプリケーションの認証を有効にしている場合、アプリケーションを登録している間にユーザーが検証され、アクセストークンが返され、そのユーザーIDでデータベースにaccesstokenを保存します。

だから私のデータベーステーブルが検証さaccesstokenを格納するために、以下のようになります:

Id(autogenerated) accesstoken userid clientid createdat expiresat 

したがって、ユーザーが認証され、ユーザが任意の保護されたリソースにアクセスしたい場合は、今、ユーザーは、後続の呼び出しで、このアクセストークンを渡す必要がありますされた後、ヘッダ内にある。

私は何をすればよいのでしょうか?ヘッダーからアクセストークンを取得し、そのデータベースに対してaccesstokenを検証して、保護されたリソースへのアクセスを許可します。

これに関連することがたくさんあるので、これは基本的にはoauth2であり、これを実装したいと思います。

私は(このプロジェクトはコンパイルされていません)がoauth2の上にあり、認証に使用されており、oauth2が承認に使用されていることがわかりました。

しかし、ここで私はここに私のデータベースにアクセストークンを格納していてそれに関連した私の疑問です:

2)今私はopenconnectidが必要です(ただし、このプロジェクトでもコンパイルされません)アクセストークンまたはを検証するためのデータベースにアクセストークンを格納しているのでopenconnectidは必要ありませんか?

3)私はasp.netアイデンティティを実装したいですが、次に動的データベース接続文字列を受け取ります。asp.netアイデンティティが主にエンティティフレームワークで動作するのを見たので、私はどこでもどこでもソースを見つけることができませんでした。 SQLクエリを使用してユーザー名とパスワードを検証します。 here

public class UserStoreService 
    : IUserStore<CustomUser>, IUserPasswordStore<CustomUser> 

を実装するカスタム・ユーザー・ストアを定義し説明したようたIUserを実装するカスタム・ユーザー・クラスを作成します

しかし、私は、私のように、この情報を持つことはありません。私はこのような何かを行うことができます知っています固定接続文字列を持っていません。接続文字列は、クライアント登録されたデータベースに再度格納されます。

4)私たちは、クライアントが管理者を作成するための固定エンドポイントを与えました。そのために私はRSAアルゴリズムを使用してパスワードハッシュを作成し、それをデータベースに格納します。だから今これで私はasp.netのアイデンティティを使用する必要がありますか?

5)トークンベースの実装では、次のようなリンクがたくさんありましたが、私はどこにいるのかを確認していませんが、データベースに格納されているaccesstokenを持っているので、次の実装?

http://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/

http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/

6)また、任意のクライアントのために、クライアントは、私が何をするか、そのそれぞれのアプリケーションの認証をしたい私はそのユーザ名・パスワードの検証を持っていないだろうでない場合は場合はI単にaccesstokenを生成してから、それ以降のすべての要求に応答して送信します。アクセストークンは保護されたリソースにアクセスするために渡されます。これは意味をなさないと思いますか?

アクセストークンがデータベースに格納され、データベースにアクセストークンを格納する際の問題は、各エンドポイントのアクセストークンを検証するたびにデータベースを呼び出す必要があるという例は見たことがありません。

アップデート:私のWebサービスエンジンの

ユースケースは次のようになります。

1)複数のクライアント・アプリケーションをサポートしています。

2)各クライアントアプリケーションのトークン管理の形式でユーザーセッションを管理します。したがって、記事のほとんどはアイデンティティにアクセス権を格納しており、そのアイデンティティは[アクセス権]属性内で検証され、アクセス権も検証され、そのユーザーに基づいて保護されたリソースにアクセスすることが許可されています。

私はまた、複数のクライアントアプリケーションをサポートするIDの中にユーザーIDとストアユーザーコンテキストを置くのが良い考えですか?

+0

ハイラーニング。私は、あなたが大文字小文字とアポストロフィの規則を守るために何か努力しようとしているかどうか、ここで(https://stackoverflow.com/q/46450952)と[ここ](https://stackoverflow.com/q/46687358)あなたの文章、そしてここで私はあなたのポストに別の30の誤りを修正しています。あなたの英語ははっきりとしているので、私は携帯電話であなたの記事を作っているのかどうか、あるいはこれらのエラーが文体的なのかどうか疑問に思っています。 Stack Overflowはフォーラムではないことを覚えておいてください。これはQ&Aサイトであり、明確にするために少なくともいくつかの基準を維持しようとしています。 – halfer

+0

投稿を調整し改善するのに役立つボランティア編集者が多数いますが、私たちはあなたの自動修正ではありません。あなたは助けてもらえますか? – halfer

+0

私はこの質問をコンピュータから投稿していますので、時々私はアポストロフィや他のいくつかのエラー(私は推測します)がありますが、私の質問の目的は簡単に理解でき、はっきりしていますが、 –

答えて

1

"7.1。アクセストークン表現」は 『Full-Scratch Implementor of OAuth and OpenID Connect Talks About Findings

どのようにアクセストークンを表現すべきか?主に2つの方法があります。

  1. 意味のないランダムな文字列です。 トークンに関連する情報は、認証サーバーの背後のデータベース表に保管されます。

  2. アクセストークン の情報をbase64urlなどでエンコードした結果の自己完結型の文字列です。

長所とこれらの2つの方法の短所は、ブログで説明されています。

アクセストークンがランダムストリングの場合、アクセストークンに関連付けられた情報(ユーザーID、クライアントID、スコープ、有効期間など)は、アクセスを発行した承認サーバーによって管理されるデータベースに格納されますトークン。

APIを公開するリソースサーバーがクライアントアプリケーションからAPI呼び出しを受け入れるたびに、リソースサーバーは何らかの方法でアクセストークンに関する情報を取得する必要があります。

リソースサーバーが承認サーバーによって管理されているデータベースにアクセスできる場合(つまり、リソースサーバーと承認サーバーがデータベースを共有する場合)、リソースサーバーはデータベースからアクセストークンに関する情報を直接取得できます。

それ以外の場合、リソースサーバーは、情報を取得するために認証サーバーへのAPI呼び出しを行う必要があります。この場合、認証サーバーはRFC 7662(OAuth 2.0トークンイントロスペクション)に準拠するAPIを公開することが期待できます。実装によっては、RFC 7662(たとえば、"4. Introspection Access Token")よりも、開発者に優しいAPIを提供することがあります。

とにかく、サーバがメモリトークンまたは他の適切な場所にあるアクセストークンに関する情報をキャッシュするたびに、リソースサーバはDB呼び出し(または認証サーバへのイントロスペクションAPIコール)を行う必要はありません。

ところで、APIを保護したいときは、トークンにアクセスする必要があります。したがって、お使いのシステムは、IDトークンを要求し発行する方法に関する仕様であるOpenID Connectをサポートする必要はありません。 OpenID ConnectをサポートするサーバーがIDトークンに加えてアクセストークンも発行できるので、混乱するかもしれません。 OpenID Connectの問題をサポートしているサーバーを理解するには、"Diagrams of All The OpenID Connect Flows"を参照してください。

最後に、アイデンティティ管理、ユーザ認証、OAuth 2.0 & OpenID Connectは、必ずしもモノリシックな方法で実装する必要はありません。詳細については、New Architecture of OAuth 2.0 and OpenID Connect Implementationを参照してください。

+0

私の手伝ってくれてありがとう、あなたの親切な努力のためにUpvoted。私はこのリンク(http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api)に従おうとしています。 -2-owin-asp-net-identity /)基本的に私はそれを理解しています。今まで私が見たことは、ユーザーが検証された後、ユーザーIDがcontext.userのように作成されたとき間違ったし、accesstokenが生成されます。しかし、唯一の混乱は、どのようにaccesstoken情報がコンテキスト内に置かれ、保護されたリソースへのアクセスを許可するためのリソースサーバーによって検証されますか? –

+1

アイデンティティ管理、ユーザ認証、OAuth 2.0&OpenID Connectを実装する方法はいろいろあります。あなたが参照している記事は、さまざまな実装のうちの1つです。 IMHOでは、アクセストークン情報をユーザーコンテキストに入れるべきではありませんが、あなたが参照している記事のソリューションは、ユーザー情報とアクセストークン情報を1つの場所(ユーザーコンテキスト)に入れることができますが、わかりません。 –

+0

私の質問にユースケースを追加しました。あなたの意見を教えてください。 –

2

いいえ、データベースにaccess_tokenを格納する必要はありません。秘密鍵で暗号化したJWTを復号化して情報を読み取ることができます。 (デフォルトではマシンキーです。)

アイデンティティには、Oauthの自己サポートがありません。正しく構成するだけです。 Startup.Auth.csのOAuthAuthorizationServerOptionsの構成を設定できます。サンプルコードは次のとおりです。私はコードのコメントであなたの質問のほとんどに答えようとしました。

public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; } 

    public static string PublicClientId { get; private set; } 

    public void ConfigureOAuth(IAppBuilder app) 
    { 
     // Configure the application for OAuth based flow 
     PublicClientId = "theDragonIsAlive"; 
     OAuthOptions = new OAuthAuthorizationServerOptions 
     { 
      TokenEndpointPath = new PathString("/Token"), 
      Provider = new YourOwnApplicationOAuthProvider(PublicClientId), 
      //AuthorizeEndpointPath = new PathString("/Access/Account"), 
      AccessTokenExpireTimeSpan = TimeSpan.FromDays(7) 
      //AllowInsecureHttp = true 
     }; 

     // Enable the application to use bearer tokens to authenticate users 
     app.UseOAuthBearerTokens(OAuthOptions); 

    } 

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>(); 
     // This where you are validating the username and password credentials. 
     ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password); 

     if (user == null) 
     { 
      context.SetError("Dragon Fire:", "The user name or password is incorrect. You shall be burnt."); 
      return; 
     } 

     ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, 
      OAuthDefaults.AuthenticationType); 

     AuthenticationProperties properties = CreateProperties(user.UserName); 
     AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties); 
     context.Validated(ticket); 
     context.Request.Context.Authentication.SignIn(oAuthIdentity); 
    } 

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

    // This method is where you will create the client access token. 
    // First you get the client, you can place values from the client record into the tokens claim collection. 
    // You then create a new ClaimsIdentity. 
    // You add some claims, in the example client name is added. 
    // Create an AuthenticationTicket using your claims identity. 
    // Validate the ticket (you do need to do this or the client will be considered unauthenticated) 
    //public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context) 
    //{ 
    // var client = clientService.GetClient(context.ClientId); 
    // var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType); 
    // oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, client.ClientName)); 
    // var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties()); 
    // context.Validated(ticket); 
    // return base.GrantClientCredentials(context); 
    //} 

    // This method has to be implmented when you are maintaining a list of clients which you will allow. 
    // This method is for validating the input, you can used this method to verify the client id and secret are valid. 
    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) 
    { 
     //string clientId; 
     //string clientSecret; 
     //context.TryGetFormCredentials(out clientId, out clientSecret); 

     //if (clientId == "1234" && clientSecret == "12345") 
     //{ 
     // context.Validated(clientId); 
     //} 

     //return base.ValidateClientAuthentication(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) 
     { 
      Uri expectedRootUri = new Uri(context.Request.Uri, "/"); 

      if (expectedRootUri.AbsoluteUri == context.RedirectUri) 
      { 
       context.Validated(); 
      } 
     } 

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

    public static AuthenticationProperties CreateProperties(string userName) 
    { 
     IDictionary<string, string> data = new Dictionary<string, string> 
     { 
      { "userName", userName } 
     }; 
     return new AuthenticationProperties(data); 
    } 
} 

上記のサンプルコードには、クライアントの分類がありません。すべてのユーザーを1つのタイプのクライアントとして扱います。しかし、私はあなたが正しい方向に着手するための手引きとなるコメントにいくつかのサンプルコードを与えました。

免責事項:私はこれに関する専門家ではなく(まだ)、私の設定は異なります。私はOwinとの既存のMVCアプリケーションを持っていて、その上にwebapiを構築しなければなりませんでした。これは私のプロトタイプコードであり、それは仕事をしました。プロダクションコードのために改善する必要があります。楽しさと幸運を持ってください。

+0

私が助けてくれるよう努力していますか?ValidateClientRedirectUriをスキップしたいのですが、それはリダイレクトURIを与えたいからです。この検証をスキップしてください。可能でしょうか? –

+0

authorizeエンドポイントを処理するとき、登録された "client_id"と "redirect_uri"は、context.validatedを呼び出す前に検証されなければなりません。 redirect_uriを検証しないと、アプリケーションはフィッシング攻撃を受ける可能性があります。 MSDNのドキュメントによると、「アプリケーションはこの呼び出しを実装しなければなりません」と言っています。あなたがそれをしたくない理由は何ですか? – mrtyormaa

+0

あなたは、暗黙の許可タイプの場合、ユーザーがclientidを渡しているところに、登録されたredirecturiがあるはずですか? –

関連する問題