web-dev-qa-db-ja.com

ASP.NET Web APIとOpenID Connect:認証コードからアクセストークンを取得する方法

OpenID Connectを実行しようとしています...私のWeb APIのユーザーがOpenID Connectプロバイダーの認証コードを取得できました。このコードをASP.NET Web APIに渡すにはどうすればよいですか?認証コードを使用してアクセストークンを取得できるようにOWINミドルウェアを構成するにはどうすればよいですか?

更新:SPAはAJAX=を使用してWebサービス(ASP.NET Web API)と通信します。WebサービスではOWINミドルウェアを使用します。OpenIDConnectを認証メカニズムとして設定します。Webサービスがは、ユーザーがOpenID Connectプロバイダーのログインページに正常にリダイレクトされたときに初めて呼び出されます。ユーザーはログインし、結果として認証コードを取得できます。このコードは、(私のWebサービスによって)アクセストークン。ただし、このコードをWebサービスに戻す方法(これはヘッダーを使用して行われますか?)と、アクセストークンを取得するために何を構成するかがわかりません。手動でトークンエンドポイントを呼び出すことができると思いますが、代わりにOWINコンポーネントを利用したいと思います。

16
Dunken

推奨されるアプローチは、AuthorizationCodeReceivedイベントを使用して、認証コードをアクセストークンと交換することです。 Vittorioには、全体的なフローの概要を示すブログエントリがあります

これを設定するStartup.Auth.csコードの GitHubのこのサンプルアプリ の例を次に示します。

app.UseOpenIdConnectAuthentication(
    new OpenIdConnectAuthenticationOptions
    {
        ClientId = clientId,
        Authority = Authority,
        Notifications = new OpenIdConnectAuthenticationNotifications()
        {
            AuthorizationCodeReceived = (context) =>
           {
               var code = context.Code;
               ClientCredential credential = new ClientCredential(clientId, appKey);
               string tenantID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.Microsoft.com/identity/claims/tenantid").Value;
               string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
               AuthenticationContext authContext = new AuthenticationContext(string.Format("https://login.windows.net/{0}", tenantID), new EFADALTokenCache(signedInUserID));
               AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
                           code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceID);

               return Task.FromResult(0);
            },
            ...
    }

注:AuthorizationCodeReceivedイベントは、認証が実際に行われるときに一度だけ呼び出されます。認証コードがすでに生成および保存されている場合、このイベントは呼び出されません。このイベントを強制的に実行するには、ログアウトするかCookieをクリアする必要があります。

11
BenV

BenVはすでに質問に回答しましたが、考慮すべきことは他にもあります。

class partial Startup
{
    public void ConfigureAuth(IAppBuilder app)
    {
        // ...

        app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
          {
            ClientId = clientId,
            Authority = authority,
            Notifications = new OpenIdConnectAuthenticationNotifications() {
                AuthorizationCodeReceived = (context) => {
                   string authorizationCode = context.Code;
                   // (tricky) the authorizationCode is available here to use, but...
                   return Task.FromResult(0);
                }
            }
          }
    }
}

2つの問題:

  • まず、authorizationCodeはすぐに期限切れになります。保管しても意味がありません。
  • 2番目の問題は、authorizationCodeの有効期限が切れておらず、セッション内に保存されていない限り、ページの再読み込みでAuthorizationCodeReceivedイベントが発生しないことです。

何をする必要があるかAcquireTokenByAuthorizationCodeAsyncを呼び出すことで、それをキャッシュし、TokenCache.DefaultShare内で適切に処理します。

AuthorizationCodeReceived = (context) => {
    string authorizationCode = context.Code;
    AuthenticationResult tokenResult = await context.AcquireTokenByAuthorizationCodeAsync(authorizationCode, new Uri(redirectUri), credential);
    return Task.FromResult(0);
}

次に、リソースへのevery呼び出しの前に、AcquireTokenSilentAsyncを呼び出してaccessTokenを取得します(これはTokenCacheを使用するか、サイレントにrefreshTokenを使用します)。トークンの有効期限が切れている場合、AdalSilentTokenAcquisitionException例外が発生します(アクセスコード更新プロシージャを呼び出します)。

// currentUser for ClaimsPrincipal.Current.FindFirst("http://schemas.Microsoft.com/identity/claims/objectidentifier")
AuthenticationResult authResult = await context.AcquireTokenSilentAsync(resourceUri, credential, currentUser);

トークンがキャッシュされている場合、AcquireTokenSilentAsyncの呼び出しは非常に高速です。

11
andrew.fox

カスタム認証を行うには、デフォルトのowin検証をバイパスする必要があります。

           new OpenIdConnectAuthenticationOptions
            {
                ...,
                TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
                {
                    ValidateIssuer = false
                },
1
James