Sign-in with External Identity Providers

ASP.NETコアは、外部認証に柔軟に対応します。これにはいくつかのステップが必要です。

注釈

ASP.NET IDを使用している場合、基礎となる技術的詳細の多くはあなたから隠されています。また、Microsoftのドキュメントを読み、ASP.NET IDのクイックスタートを行うことをお勧めします。

外部プロバイダの認証ハンドラの追加

外部プロバイダと通信するために必要なプロトコル実装は、認証ハンドラにカプセル化されています。一部のプロバイダは独自のプロトコル(Facebookなどのソーシャルプロバイダなど)を使用しており、一部のプロトコルではOpenID Connect、WS-Federation、SAML2pなどの標準プロトコルを使用しています。

外部認証を追加して構成する手順については、このクイックスタートを参照してください。

クッキーの役割

外部認証ハンドラの1つのオプションが呼び出されますSignInScheme。例:

services.AddAuthentication()
    .AddGoogle("Google", options =>
    {
        options.SignInScheme = "scheme of cookie handler to use";

        options.ClientId = "...";
        options.ClientSecret = "...";
    })

サインイン方式は、外部プロバイダの結果を一時的に保存するクッキーハンドラの名前を指定します。通常、外部認証プロセスを完了するまで、数回のリダイレクトが行われるため、これは必要です。

これがあまり一般的でない場合、IdentityServerはこの外部プロバイダワークフロー専用のCookieハンドラを登録します。スキームはIdentityServerConstants.ExternalCookieAuthenticationScheme定数によって表されます。外部のCookieハンドラを使用する場合は、SignInScheme上記の値をIdentityServerConstants.ExternalCookieAuthenticationScheme定数に割り当てます。

services.AddAuthentication()
    .AddGoogle("Google", options =>
    {
        options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;

        options.ClientId = "...";
        options.ClientSecret = "...";
    })

次のように独自のカスタムCookieハンドラを登録することもできます。

services.AddAuthentication()
    .AddCookie("YourCustomScheme")
    .AddGoogle("Google", options =>
    {
        options.SignInScheme = "YourCustomScheme";

        options.ClientId = "...";
        options.ClientSecret = "...";
    })

注釈

特別なシナリオでは、外部のCookieメカニズムを短絡し、外部のユーザーをメインのCookieハンドラに直接転送することもできます。これには通常、外部ハンドラでイベントを処理して、外部IDソースから正しいクレーム変換を実行する必要があります。

認証ハンドラのトリガ

(またはMVCを使用して)ChallengeAsync拡張メソッドをHttpContext使用して外部認証ハンドラを呼び出しChallengeResultます。

通常、コールバックページへのパスや簿記のプロバイダ名など、チャレンジ操作にいくつかのオプションを渡します。

var callbackUrl = Url.Action("ExternalLoginCallback");

var props = new AuthenticationProperties
{
    RedirectUri = callbackUrl,
    Items =
    {
        { "scheme", provider },
        { "returnUrl", returnUrl }
    }
};

return Challenge(provider, props);

コールバックの処理とユーザのサインイン

コールバックページでは、一般的なタスクは次のとおりです。

  • 外部プロバイダから返されたIDを調べます。
  • そのユーザーに対処する方法を決定します。これは、新規ユーザーまたは再帰ユーザーの場合は、これが実際の状況に基づいて異なる可能性があります。
  • 新しいユーザーは、許可される前に追加の手順とUIが必要になることがあります。
  • おそらく外部プロバイダーにリンクされている新しい内部ユーザーアカウントを作成します。
  • 保管したい外部クレームを保管してください。
  • 一時的なクッキーを削除する
  • ユーザーにサインインする

外部IDの検査

// read external identity from the temporary cookie
var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
if (result?.Succeeded != true)
{
    throw new Exception("External authentication error");
}

// retrieve claims of the external user
var externalUser = result.Principal;
if (externalUser == null)
{
    throw new Exception("External authentication error");
}

// retrieve claims of the external user
var claims = externalUser.Claims.ToList();

// try to determine the unique id of the external user - the most common claim type for that are the sub claim and the NameIdentifier
// depending on the external provider, some other claim type might be used
var userIdClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject);
if (userIdClaim == null)
{
    userIdClaim = claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier);
}
if (userIdClaim == null)
{
    throw new Exception("Unknown userid");
}

var externalUserId = userIdClaim.Value;
var externalProvider = userIdClaim.Issuer;

// use externalProvider and externalUserId to find your user, or provision a new user

クリーンアップとサインイン:

// issue authentication cookie for user
await HttpContext.SignInAsync(user.SubjectId, user.Username, provider, props, additionalClaims.ToArray());

// delete temporary cookie used during external authentication
await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);

// validate return URL and redirect back to authorization endpoint or a local page
if (_interaction.IsValidReturnUrl(returnUrl) || Url.IsLocalUrl(returnUrl))
{
    return Redirect(returnUrl);
}

return Redirect("~/");

State, URL length, and ISecureDataFormat

サインインのために外部プロバイダーにリダイレクトするとき、クライアントアプリケーションからの頻繁な状態は往復する必要があります。これは、クライアントを離れる前に状態がキャプチャされ、ユーザーがクライアントアプリケーションに戻るまで保持されることを意味します。OpenID Connectを含む多くのプロトコルは、要求の一部としてパラメータとして何らかの状態を渡すことを許可し、アイデンティティプロバイダはその状態を応答に返します。ASP.NET Coreによって提供されるOpenID Connect認証ハンドラは、このプロトコルの機能を利用しており、これがreturnUrl上記の機能を実装する方法です。

リクエストパラメータに状態を格納する際の問題は、リクエストURLが(2000文字の共通の制限を超えて)大きくなりすぎる可能性があることです。OpenID Connect認証ハンドラは、リクエストURLではなくサーバに状態を保存するための拡張ポイントを提供します。OpenIdConnectOptionsISecureDataFormat<AuthenticationProperties>で実装して設定することで、これを実装することができます。

幸いにも、IdentityServerはIDistributedCache、DIコンテナ(標準などMemoryDistributedCache)に登録されている実装に基づいて、この実装を提供します。IdentityServerが提供する安全なデータフォーマットの実装を使用するには、DIを設定AddOidcStateDataFormatterCacheするIServiceCollectionときに拡張メソッドを呼び出します。パラメータが渡されない場合、設定されたすべてのOpenID Connectハンドラは、IdentityServerが提供する安全なデータフォーマットの実装を使用します。

public void ConfigureServices(IServiceCollection services)
{
    // configures the OpenIdConnect handlers to persist the state parameter into the server-side IDistributedCache.
    services.AddOidcStateDataFormatterCache();

    services.AddAuthentication()
        .AddOpenIdConnect("demoidsrv", "IdentityServer", options =>
        {
            // ...
        })
        .AddOpenIdConnect("aad", "Azure AD", options =>
        {
            // ...
        })
        .AddOpenIdConnect("adfs", "ADFS", options =>
        {
            // ...
        });
}

特定のスキームのみを設定する場合は、それらのスキームをパラメータとして渡します。

public void ConfigureServices(IServiceCollection services)
{
    // configures the OpenIdConnect handlers to persist the state parameter into the server-side IDistributedCache.
    services.AddOidcStateDataFormatterCache("aad", "demoidsrv");

    services.AddAuthentication()
        .AddOpenIdConnect("demoidsrv", "IdentityServer", options =>
        {
            // ...
        })
        .AddOpenIdConnect("aad", "Azure AD", options =>
        {
            // ...
        })
        .AddOpenIdConnect("adfs", "ADFS", options =>
        {
            // ...
        });
}