web-dev-qa-db-ja.com

ADFSを使用したASP.NETWebAPIへの認証

認証にADFSを使用しているASP.NETWebApiにアクセスする必要がある状況にあります。 ADFSログインポータルを経由して関連するFedAuthCookieを取得することにより、ブラウザーから確実にヒットできます。残念ながら、モバイルアプリで使用するには、専用ブラウザの外部からアクセスする必要があります。このプロジェクトは、職場および学校の​​認証(オンプレミス)用にセットアップされ、Cookie認証用にセットアップされた標準のVisual Studio WebAPIテンプレートのわずかに変更されたバージョンです。

startup.Auth.csからのコードのビット:

public void Configuration(IAppBuilder app)
{
    app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
    app.UseWsFederationAuthentication(
        new WsFederationAuthenticationOptions
        {
            Wtrealm = realm,
            MetadataAddress = adfsMetadata
        });
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType
    });
}

どこから始めればいいのかわからないようです。 ADFSからアクセストークンをリクエストしようとしましたが、関連するログイン情報を使用してさまざまなバージョンのSAMLアサーションを取得できますが、WebAPIによって拒否されます。それがどのように機能するのか誤解しましたか?

私の理解から、それは次のようになるはずです: 私はそれがどのように機能すると思うか

  1. アプリがADFSに認証トークンを要求します
  2. 提供された情報が正しかった場合、ADFSは要求先に認証トークンを提供します
  3. アプリはWebAPIにリクエストを送信し、FedAuth(デフォルトではとにかく)と呼ばれるCookie内でトークンをbase64でエンコードされた文字列として送信します
  4. Web ApiはトークンをADFSに送信して、トークンが正しいかどうかを確認します。
  5. ADFSはある種の成功メッセージで応答します
  6. Web Apiは、認証の方法に応じて、拒否またはデータの一部でアプリに応答します。

これは、正しいトークンを取得する方法を見つけようとしているときに私が今持っているものです。

using System;
using System.IdentityModel.Protocols.WSTrust;
using System.IdentityModel.Tokens;
using System.Net;
using System.Net.Http;
using System.ServiceModel;
using System.ServiceModel.Security;
using Thinktecture.IdentityModel.Extensions;
using Thinktecture.IdentityModel.WSTrust;

namespace ConsoleApplication1
{    
    class Program
    {
        private const string UserName     = "USERNAME";
        private const string Password     = "PASSWORD";
        private const string Domain       = "DOMAIN";
        private const string ADFSEndpoint = "ADFS ENDPOINT";
        private const string ApiBaseUri   = "THE API";
        private const string ApiEndPoint  = "AN ENDPOINT";

        static void Main(string[] args)
        {
            SecurityToken token = RequestSecurityToken(); // Obtain security token from ADFS.
            CallApi(token);                               // Call api. 
            Console.ReadKey();                            // Stop console from closing
        }

        private static SecurityToken RequestSecurityToken()
        {
            var trustChannelFactory =
                new WSTrustChannelFactory(new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
                    new EndpointAddress(new Uri(ADFSEndpoint)))
                {
                    TrustVersion = TrustVersion.WSTrust13,
                    Credentials = { UserName = { UserName = UserName + "@" + Domain, Password = Password } },
                };

            var requestSecurityToken = new RequestSecurityToken
            {
                RequestType = RequestTypes.Issue,
                KeyType = KeyTypes.Bearer,
                AppliesTo = new EndpointReference(ApiBaseUri)
            };

            RequestSecurityTokenResponse response;
            var securityToken = trustChannelFactory.CreateChannel().Issue(requestSecurityToken, out response);

            return securityToken;
        }

        private static async void CallApi(SecurityToken securityToken)
        {
            using (var handler = new HttpClientHandler { CookieContainer = new CookieContainer() })
            {
                using (var client = new HttpClient(handler))
                {
                    handler.CookieContainer.MaxCookieSize = 8000; // Trying to make sure I can fit it in the cookie

                    var cookie = new Cookie {
                        Name = "FedAuth",
                        Value = Base64Encode(securityToken.ToTokenXmlString()),
                        HttpOnly = true,
                        Secure = true
                    };
                    handler.CookieContainer.Add(new Uri(ApiBaseUri), cookie);
                    var response = client.GetAsync(new Uri(ApiBaseUri + ApiEndPoint)).Result;
                    string result = await response.Content.ReadAsStringAsync();
                    Console.WriteLine(result);
                }
            }
        }

        public static string Base64Encode(string plainText)
        {
            var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
            return System.Convert.ToBase64String(plainTextBytes);
        }
    }
}

自分の例に基づいたコードを完全に思い出せませんが、誰かが私を正しい方向に向けたり、どこでめちゃくちゃにしたか教えてくれたら、ありがたいです。

編集:申し訳ありませんが、私が得ているものを追加するのを忘れました。 Web Apiは、例外がスローされたために大量のデバッグ情報を吐き出し、明らかに取得しているsaml:Assertionの代わりにSecurityContextTokenが必要であることを通知します。たぶん私のgooglefooは十分に強力ではありませんが、これからどこから始めればよいのか理解できないようです。 SAMLアサーションを受け入れるようにAPIを設定できますか、それとも別の方法でトークンをリクエストする必要がありますか?

7
Tennaheim

WS-Fedを使用してWebAPIを呼び出すことはできません。 OpenID Connect/OAuthのように AzureADとOpenIDConnectを使用してWebアプリでWebAPIを呼び出す が必要です。

これはAzureAD用ですが、フローを示しています。

ADFSのどのバージョンですか?

6
nzpcmad