ASP.NET Core で外部プロバイダーからの追加の要求とトークンを保持するPersist additional claims and tokens from external providers in ASP.NET Core

ASP.NET Core アプリは、Facebook、Google、Microsoft、Twitter などの外部認証プロバイダーからの追加の要求とトークンを確立できます。An ASP.NET Core app can establish additional claims and tokens from external authentication providers, such as Facebook, Google, Microsoft, and Twitter. 各プロバイダーは、プラットフォーム上のユーザーに関するさまざまな情報を明らかにしますが、ユーザーデータを受け取って追加の要求に変換するパターンは同じです。Each provider reveals different information about users on its platform, but the pattern for receiving and transforming user data into additional claims is the same.

サンプル コードを表示またはダウンロードします (ダウンロード方法)。View or download sample code (how to download)

必須コンポーネントPrerequisites

アプリでサポートする外部認証プロバイダーを決定します。Decide which external authentication providers to support in the app. 各プロバイダーについて、アプリを登録し、クライアント ID とクライアントシークレットを取得します。For each provider, register the app and obtain a client ID and client secret. 詳細については、「Facebook、Google、ASP.NET Core での外部プロバイダーの認証」を参照してください。For more information, see Facebook、Google、ASP.NET Core での外部プロバイダーの認証. このサンプルアプリでは、 Google 認証プロバイダーを使用します。The sample app uses the Google authentication provider.

クライアント ID とクライアントシークレットを設定するSet the client ID and client secret

OAuth 認証プロバイダーは、クライアント ID とクライアントシークレットを使用して、アプリとの信頼関係を確立します。The OAuth authentication provider establishes a trust relationship with an app using a client ID and client secret. アプリがプロバイダーに登録されるときに、外部認証プロバイダーによってアプリのクライアント ID とクライアントシークレットの値が作成されます。Client ID and client secret values are created for the app by the external authentication provider when the app is registered with the provider. アプリが使用する各外部プロバイダーは、プロバイダーのクライアント ID とクライアントシークレットとは別に構成する必要があります。Each external provider that the app uses must be configured independently with the provider's client ID and client secret. 詳細については、シナリオに当てはまる外部認証プロバイダーのトピックを参照してください。For more information, see the external authentication provider topics that apply to your scenario:

このサンプルアプリでは、google によって提供されるクライアント ID とクライアントシークレットを使用して Google 認証プロバイダーを構成します。The sample app configures the Google authentication provider with a client ID and client secret provided by Google:

services.AddAuthentication().AddGoogle(options =>
{
    // Provide the Google Client ID
    options.ClientId = "XXXXXXXXXXXXXXX.apps.googleusercontent.com";
    // Register with User Secrets using:
    // dotnet user-secrets set "Authentication:Google:ClientId" "{Client ID}"

    // Provide the Google Client Secret
    options.ClientSecret = "{Client Secret}";
    // Register with User Secrets using:
    // dotnet user-secrets set "Authentication:Google:ClientSecret" "{Client Secret}"

    options.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url");
    options.ClaimActions.MapJsonKey("urn:google:locale", "locale", "string");
    options.SaveTokens = true;

    options.Events.OnCreatingTicket = ctx =>
    {
        List<AuthenticationToken> tokens = ctx.Properties.GetTokens().ToList(); 

        tokens.Add(new AuthenticationToken()
        {
            Name = "TicketCreated", 
            Value = DateTime.UtcNow.ToString()
        });

        ctx.Properties.StoreTokens(tokens);

        return Task.CompletedTask;
    };
});

認証スコープを確立するEstablish the authentication scope

を指定して、プロバイダーから取得するアクセス許可の一覧を指定し Scope ます。Specify the list of permissions to retrieve from the provider by specifying the Scope. 共通外部プロバイダーの認証スコープは、次の表に表示されます。Authentication scopes for common external providers appear in the following table.

プロバイダーProvider スコープScope
FacebookFacebook https://www.facebook.com/dialog/oauth
GoogleGoogle profile, email, openidprofile, email, openid
MicrosoftMicrosoft https://login.microsoftonline.com/common/oauth2/v2.0/authorize
TwitterTwitter https://api.twitter.com/oauth/authenticate

サンプルアプリで profile は、がで呼び出されると、Google の、、の各 email openid スコープがフレームワークによって自動的に追加され AddGoogle AuthenticationBuilder ます。In the sample app, Google's profile, email, and openid scopes are automatically added by the framework when AddGoogle is called on the AuthenticationBuilder. アプリに追加のスコープが必要な場合は、それらをオプションに追加します。If the app requires additional scopes, add them to the options. 次の例で https://www.googleapis.com/auth/user.birthday.read は、ユーザーの誕生日を取得するために Google スコープが追加されています。In the following example, the Google https://www.googleapis.com/auth/user.birthday.read scope is added to retrieve a user's birthday:

options.Scope.Add("https://www.googleapis.com/auth/user.birthday.read");

ユーザーデータキーをマップして要求を作成するMap user data keys and create claims

プロバイダーのオプションで、 MapJsonKey MapJsonSubKey サインイン時に読み取るアプリ id の外部プロバイダーの JSON ユーザーデータのキー/サブキーごとに、またはを指定します。In the provider's options, specify a MapJsonKey or MapJsonSubKey for each key/subkey in the external provider's JSON user data for the app identity to read on sign in. 要求の種類の詳細については、「」を参照してください ClaimTypesFor more information on claim types, see ClaimTypes.

このサンプルアプリでは、 urn:google:locale urn:google:picture locale Google ユーザーデータのキーとキーから、ロケール () と画像 () の要求を作成し picture ます。The sample app creates locale (urn:google:locale) and picture (urn:google:picture) claims from the locale and picture keys in Google user data:

services.AddAuthentication().AddGoogle(options =>
{
    // Provide the Google Client ID
    options.ClientId = "XXXXXXXXXXXXXXX.apps.googleusercontent.com";
    // Register with User Secrets using:
    // dotnet user-secrets set "Authentication:Google:ClientId" "{Client ID}"

    // Provide the Google Client Secret
    options.ClientSecret = "{Client Secret}";
    // Register with User Secrets using:
    // dotnet user-secrets set "Authentication:Google:ClientSecret" "{Client Secret}"

    options.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url");
    options.ClaimActions.MapJsonKey("urn:google:locale", "locale", "string");
    options.SaveTokens = true;

    options.Events.OnCreatingTicket = ctx =>
    {
        List<AuthenticationToken> tokens = ctx.Properties.GetTokens().ToList(); 

        tokens.Add(new AuthenticationToken()
        {
            Name = "TicketCreated", 
            Value = DateTime.UtcNow.ToString()
        });

        ctx.Properties.StoreTokens(tokens);

        return Task.CompletedTask;
    };
});

では、 Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal.ExternalLoginModel.OnPostConfirmationAsync IdentityUser ( ApplicationUser ) がを使用してアプリにサインイン SignInAsync します。In Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal.ExternalLoginModel.OnPostConfirmationAsync, an IdentityUser (ApplicationUser) is signed into the app with SignInAsync. サインインプロセス中に、で UserManager<TUser> ApplicationUser 使用可能なユーザーデータの要求を格納でき Principal ます。During the sign in process, the UserManager<TUser> can store an ApplicationUser claims for user data available from the Principal.

サンプルアプリでは、 OnPostConfirmationAsync ( Account/externallogin. cshtml ) によって、 urn:google:locale urn:google:picture ApplicationUser 次の要求を含む、サインインしたのロケール () と画像 () の要求が確立 GivenName されます。In the sample app, OnPostConfirmationAsync ( Account/ExternalLogin.cshtml.cs ) establishes the locale (urn:google:locale) and picture (urn:google:picture) claims for the signed in ApplicationUser, including a claim for GivenName:

public async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    // Get the information about the user from the external login provider
    var info = await _signInManager.GetExternalLoginInfoAsync();

    if (info == null)
    {
        ErrorMessage = 
            "Error loading external login information during confirmation.";

        return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
    }

    if (ModelState.IsValid)
    {
        var user = new IdentityUser
        {
            UserName = Input.Email, 
            Email = Input.Email 
        };

        var result = await _userManager.CreateAsync(user);

        if (result.Succeeded)
        {
            result = await _userManager.AddLoginAsync(user, info);

            if (result.Succeeded)
            {
                // If they exist, add claims to the user for:
                //    Given (first) name
                //    Locale
                //    Picture
                if (info.Principal.HasClaim(c => c.Type == ClaimTypes.GivenName))
                {
                    await _userManager.AddClaimAsync(user, 
                        info.Principal.FindFirst(ClaimTypes.GivenName));
                }

                if (info.Principal.HasClaim(c => c.Type == "urn:google:locale"))
                {
                    await _userManager.AddClaimAsync(user, 
                        info.Principal.FindFirst("urn:google:locale"));
                }

                if (info.Principal.HasClaim(c => c.Type == "urn:google:picture"))
                {
                    await _userManager.AddClaimAsync(user, 
                        info.Principal.FindFirst("urn:google:picture"));
                }

                // Include the access token in the properties
                var props = new AuthenticationProperties();
                props.StoreTokens(info.AuthenticationTokens);
                props.IsPersistent = true;

                await _signInManager.SignInAsync(user, props);

                _logger.LogInformation(
                    "User created an account using {Name} provider.", 
                    info.LoginProvider);

                return LocalRedirect(returnUrl);
            }
        }

        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    LoginProvider = info.LoginProvider;
    ReturnUrl = returnUrl;
    return Page();
}

既定では、ユーザーの要求は認証に格納され cookie ます。By default, a user's claims are stored in the authentication cookie. 認証 cookie が大きすぎると、次の理由により、アプリでエラーが発生する可能性があります。If the authentication cookie is too large, it can cause the app to fail because:

  • ブラウザーは、 cookie ヘッダーが長すぎることを検出します。The browser detects that the cookie header is too long.
  • 要求の全体的なサイズが大きすぎます。The overall size of the request is too large.

ユーザーの要求を処理するために大量のユーザーデータが必要な場合は、次のようにします。If a large amount of user data is required for processing user requests:

  • 要求処理のユーザー要求の数とサイズは、アプリで必要なもののみに制限します。Limit the number and size of user claims for request processing to only what the app requires.
  • 認証ミドルウェアのカスタムを使用し ITicketStore て、 Cookie SessionStore 要求間で id を格納します。Use a custom ITicketStore for the Cookie Authentication Middleware's SessionStore to store identity across requests. 小さいセッション識別子キーをクライアントに送信するだけで、サーバー上の大量の id 情報を保持します。Preserve large quantities of identity information on the server while only sending a small session identifier key to the client.

アクセストークンを保存するSave the access token

SaveTokens 承認が成功した後に、アクセストークンと更新トークンをに格納するかどうかを定義し AuthenticationProperties ます。SaveTokens defines whether access and refresh tokens should be stored in the AuthenticationProperties after a successful authorization. SaveTokensfalse 、最終的な認証のサイズを小さくするために、既定でに設定され cookie ます。SaveTokens is set to false by default to reduce the size of the final authentication cookie.

サンプルアプリでは、の値をに設定し SaveTokens true GoogleOptions ます。The sample app sets the value of SaveTokens to true in GoogleOptions:

services.AddAuthentication().AddGoogle(options =>
{
    // Provide the Google Client ID
    options.ClientId = "XXXXXXXXXXXXXXX.apps.googleusercontent.com";
    // Register with User Secrets using:
    // dotnet user-secrets set "Authentication:Google:ClientId" "{Client ID}"

    // Provide the Google Client Secret
    options.ClientSecret = "{Client Secret}";
    // Register with User Secrets using:
    // dotnet user-secrets set "Authentication:Google:ClientSecret" "{Client Secret}"

    options.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url");
    options.ClaimActions.MapJsonKey("urn:google:locale", "locale", "string");
    options.SaveTokens = true;

    options.Events.OnCreatingTicket = ctx =>
    {
        List<AuthenticationToken> tokens = ctx.Properties.GetTokens().ToList(); 

        tokens.Add(new AuthenticationToken()
        {
            Name = "TicketCreated", 
            Value = DateTime.UtcNow.ToString()
        });

        ctx.Properties.StoreTokens(tokens);

        return Task.CompletedTask;
    };
});

を実行するときに、 OnPostConfirmationAsync の外部プロバイダーからアクセストークン (Externallogininfo. authenticationtokens) をに格納 ApplicationUser AuthenticationProperties します。When OnPostConfirmationAsync executes, store the access token (ExternalLoginInfo.AuthenticationTokens) from the external provider in the ApplicationUser's AuthenticationProperties.

サンプルアプリでは、アクセストークン OnPostConfirmationAsync (新しいユーザー登録) と OnGetCallbackAsync (以前に登録されたユーザー) を Account/externallogin. cshtml. cs に保存します。The sample app saves the access token in OnPostConfirmationAsync (new user registration) and OnGetCallbackAsync (previously registered user) in Account/ExternalLogin.cshtml.cs :

public async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    // Get the information about the user from the external login provider
    var info = await _signInManager.GetExternalLoginInfoAsync();

    if (info == null)
    {
        ErrorMessage = 
            "Error loading external login information during confirmation.";

        return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
    }

    if (ModelState.IsValid)
    {
        var user = new IdentityUser
        {
            UserName = Input.Email, 
            Email = Input.Email 
        };

        var result = await _userManager.CreateAsync(user);

        if (result.Succeeded)
        {
            result = await _userManager.AddLoginAsync(user, info);

            if (result.Succeeded)
            {
                // If they exist, add claims to the user for:
                //    Given (first) name
                //    Locale
                //    Picture
                if (info.Principal.HasClaim(c => c.Type == ClaimTypes.GivenName))
                {
                    await _userManager.AddClaimAsync(user, 
                        info.Principal.FindFirst(ClaimTypes.GivenName));
                }

                if (info.Principal.HasClaim(c => c.Type == "urn:google:locale"))
                {
                    await _userManager.AddClaimAsync(user, 
                        info.Principal.FindFirst("urn:google:locale"));
                }

                if (info.Principal.HasClaim(c => c.Type == "urn:google:picture"))
                {
                    await _userManager.AddClaimAsync(user, 
                        info.Principal.FindFirst("urn:google:picture"));
                }

                // Include the access token in the properties
                var props = new AuthenticationProperties();
                props.StoreTokens(info.AuthenticationTokens);
                props.IsPersistent = true;

                await _signInManager.SignInAsync(user, props);

                _logger.LogInformation(
                    "User created an account using {Name} provider.", 
                    info.LoginProvider);

                return LocalRedirect(returnUrl);
            }
        }

        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    LoginProvider = info.LoginProvider;
    ReturnUrl = returnUrl;
    return Page();
}

カスタムトークンを追加する方法How to add additional custom tokens

の一部として格納されているカスタムトークンを追加する方法を示すために、 SaveTokens サンプルアプリでは、の AuthenticationToken.Name の現在のを使用してを追加し AuthenticationToken DateTime AuthenticationToken.Name TicketCreated ます。To demonstrate how to add a custom token, which is stored as part of SaveTokens, the sample app adds an AuthenticationToken with the current DateTime for an AuthenticationToken.Name of TicketCreated:

services.AddAuthentication().AddGoogle(options =>
{
    // Provide the Google Client ID
    options.ClientId = "XXXXXXXXXXXXXXX.apps.googleusercontent.com";
    // Register with User Secrets using:
    // dotnet user-secrets set "Authentication:Google:ClientId" "{Client ID}"

    // Provide the Google Client Secret
    options.ClientSecret = "{Client Secret}";
    // Register with User Secrets using:
    // dotnet user-secrets set "Authentication:Google:ClientSecret" "{Client Secret}"

    options.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url");
    options.ClaimActions.MapJsonKey("urn:google:locale", "locale", "string");
    options.SaveTokens = true;

    options.Events.OnCreatingTicket = ctx =>
    {
        List<AuthenticationToken> tokens = ctx.Properties.GetTokens().ToList(); 

        tokens.Add(new AuthenticationToken()
        {
            Name = "TicketCreated", 
            Value = DateTime.UtcNow.ToString()
        });

        ctx.Properties.StoreTokens(tokens);

        return Task.CompletedTask;
    };
});

要求の作成と追加Creating and adding claims

フレームワークには、要求を作成してコレクションに追加するための一般的なアクションと拡張メソッドが用意されています。The framework provides common actions and extension methods for creating and adding claims to the collection. 詳細については、「 ClaimActionCollectionMapExtensions 」および「 ClaimActionCollectionUniqueExtensions」を参照してください。For more information, see the ClaimActionCollectionMapExtensions and ClaimActionCollectionUniqueExtensions.

ユーザーは、から派生し、抽象メソッドを実装することによって、カスタムアクションを定義でき ClaimAction Run ます。Users can define custom actions by deriving from ClaimAction and implementing the abstract Run method.

詳細については、「Microsoft.AspNetCore.Authentication.OAuth.Claims」を参照してください。For more information, see Microsoft.AspNetCore.Authentication.OAuth.Claims.

要求アクションと要求の削除Removal of claim actions and claims

Claimactioncollection。 Remove (String) は、指定されたのすべての要求アクションを ClaimType コレクションから削除します。ClaimActionCollection.Remove(String) removes all claim actions for the given ClaimType from the collection. DeleteClaim (ClaimActionCollection, String) は、指定されたの要求を ClaimType id から削除します。ClaimActionCollectionMapExtensions.DeleteClaim(ClaimActionCollection, String) deletes a claim of the given ClaimType from the identity. DeleteClaim は、主に OpenID connect (OIDC) と共に使用して、プロトコルによって生成された要求を削除します。DeleteClaim is primarily used with OpenID Connect (OIDC) to remove protocol-generated claims.

サンプルアプリの出力Sample app output

User Claims

http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier
    9b342344f-7aab-43c2-1ac1-ba75912ca999
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
    someone@gmail.com
AspNet.Identity.SecurityStamp
    7D4312MOWRYYBFI1KXRPHGOSTBVWSFDE
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
    Judy
urn:google:locale
    en
urn:google:picture
    https://lh4.googleusercontent.com/-XXXXXX/XXXXXX/XXXXXX/XXXXXX/photo.jpg

Authentication Properties

.Token.access_token
    yc23.AlvoZqz56...1lxltXV7D-ZWP9
.Token.token_type
    Bearer
.Token.expires_at
    2019-04-11T22:14:51.0000000+00:00
.Token.TicketCreated
    4/11/2019 9:14:52 PM
.TokenNames
    access_token;token_type;expires_at;TicketCreated
.persistent
.issued
    Thu, 11 Apr 2019 20:51:06 GMT
.expires
    Thu, 25 Apr 2019 20:51:06 GMT

プロキシまたはロード バランサーによる要求情報の転送Forward request information with a proxy or load balancer

アプリがプロキシ サーバーまたはロード バランサーの背後に展開されると、元の要求情報の一部が要求ヘッダー内でアプリに転送される場合があります。If the app is deployed behind a proxy server or load balancer, some of the original request information might be forwarded to the app in request headers. 通常、この情報にはセキュアな要求スキーム (https)、ホスト、およびクライアント IP アドレスが含まれます。This information usually includes the secure request scheme (https), host, and client IP address. アプリでは、これらの要求ヘッダーを自動的に読み取って、元の要求情報を検出して使用することはありません。Apps don't automatically read these request headers to discover and use the original request information.

スキームは、外部プロバイダーによる認証フローに影響を及ぼすリンクの生成に使用されます。The scheme is used in link generation that affects the authentication flow with external providers. セキュアなスキーム (https) が失われると、アプリでは、安全ではない不正なリダイレクト URL が生成されます。Losing the secure scheme (https) results in the app generating incorrect insecure redirect URLs.

Forwarded Headers Middleware を使用して、アプリが要求を処理する際に元の要求情報を利用できるようにします。Use Forwarded Headers Middleware to make the original request information available to the app for request processing.

詳細については、「プロキシ サーバーとロード バランサーを使用するために ASP.NET Core を構成する」を参照してください。For more information, see プロキシ サーバーとロード バランサーを使用するために ASP.NET Core を構成する.

ASP.NET Core アプリは、Facebook、Google、Microsoft、Twitter などの外部認証プロバイダーからの追加の要求とトークンを確立できます。An ASP.NET Core app can establish additional claims and tokens from external authentication providers, such as Facebook, Google, Microsoft, and Twitter. 各プロバイダーは、プラットフォーム上のユーザーに関するさまざまな情報を明らかにしますが、ユーザーデータを受け取って追加の要求に変換するパターンは同じです。Each provider reveals different information about users on its platform, but the pattern for receiving and transforming user data into additional claims is the same.

サンプル コードを表示またはダウンロードします (ダウンロード方法)。View or download sample code (how to download)

必須コンポーネントPrerequisites

アプリでサポートする外部認証プロバイダーを決定します。Decide which external authentication providers to support in the app. 各プロバイダーについて、アプリを登録し、クライアント ID とクライアントシークレットを取得します。For each provider, register the app and obtain a client ID and client secret. 詳細については、「Facebook、Google、ASP.NET Core での外部プロバイダーの認証」を参照してください。For more information, see Facebook、Google、ASP.NET Core での外部プロバイダーの認証. このサンプルアプリでは、 Google 認証プロバイダーを使用します。The sample app uses the Google authentication provider.

クライアント ID とクライアントシークレットを設定するSet the client ID and client secret

OAuth 認証プロバイダーは、クライアント ID とクライアントシークレットを使用して、アプリとの信頼関係を確立します。The OAuth authentication provider establishes a trust relationship with an app using a client ID and client secret. アプリがプロバイダーに登録されるときに、外部認証プロバイダーによってアプリのクライアント ID とクライアントシークレットの値が作成されます。Client ID and client secret values are created for the app by the external authentication provider when the app is registered with the provider. アプリが使用する各外部プロバイダーは、プロバイダーのクライアント ID とクライアントシークレットとは別に構成する必要があります。Each external provider that the app uses must be configured independently with the provider's client ID and client secret. 詳細については、シナリオに当てはまる外部認証プロバイダーのトピックを参照してください。For more information, see the external authentication provider topics that apply to your scenario:

このサンプルアプリでは、google によって提供されるクライアント ID とクライアントシークレットを使用して Google 認証プロバイダーを構成します。The sample app configures the Google authentication provider with a client ID and client secret provided by Google:

services.AddAuthentication().AddGoogle(options =>
{
    // Provide the Google Client ID
    options.ClientId = "XXXXXXXXXXXXXXX.apps.googleusercontent.com";
    // Register with User Secrets using:
    // dotnet user-secrets set "Authentication:Google:ClientId" "{Client ID}"

    // Provide the Google Client Secret
    options.ClientSecret = "{Client Secret}";
    // Register with User Secrets using:
    // dotnet user-secrets set "Authentication:Google:ClientSecret" "{Client Secret}"

    options.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url");
    options.ClaimActions.MapJsonKey("urn:google:locale", "locale", "string");
    options.SaveTokens = true;

    options.Events.OnCreatingTicket = ctx =>
    {
        List<AuthenticationToken> tokens = ctx.Properties.GetTokens().ToList(); 

        tokens.Add(new AuthenticationToken()
        {
            Name = "TicketCreated", 
            Value = DateTime.UtcNow.ToString()
        });

        ctx.Properties.StoreTokens(tokens);

        return Task.CompletedTask;
    };
});

認証スコープを確立するEstablish the authentication scope

を指定して、プロバイダーから取得するアクセス許可の一覧を指定し Scope ます。Specify the list of permissions to retrieve from the provider by specifying the Scope. 共通外部プロバイダーの認証スコープは、次の表に表示されます。Authentication scopes for common external providers appear in the following table.

プロバイダーProvider スコープScope
FacebookFacebook https://www.facebook.com/dialog/oauth
GoogleGoogle https://www.googleapis.com/auth/userinfo.profile
MicrosoftMicrosoft https://login.microsoftonline.com/common/oauth2/v2.0/authorize
TwitterTwitter https://api.twitter.com/oauth/authenticate

サンプルアプリで userinfo.profile AddGoogle は、がで呼び出されたときに、Google のスコープがフレームワークによって自動的に追加され AuthenticationBuilder ます。In the sample app, Google's userinfo.profile scope is automatically added by the framework when AddGoogle is called on the AuthenticationBuilder. アプリに追加のスコープが必要な場合は、それらをオプションに追加します。If the app requires additional scopes, add them to the options. 次の例で https://www.googleapis.com/auth/user.birthday.read は、ユーザーの誕生日を取得するために Google スコープが追加されています。In the following example, the Google https://www.googleapis.com/auth/user.birthday.read scope is added in order to retrieve a user's birthday:

options.Scope.Add("https://www.googleapis.com/auth/user.birthday.read");

ユーザーデータキーをマップして要求を作成するMap user data keys and create claims

プロバイダーのオプションで、 MapJsonKey MapJsonSubKey サインイン時に読み取るアプリ id の外部プロバイダーの JSON ユーザーデータのキー/サブキーごとに、またはを指定します。In the provider's options, specify a MapJsonKey or MapJsonSubKey for each key/subkey in the external provider's JSON user data for the app identity to read on sign in. 要求の種類の詳細については、「」を参照してください ClaimTypesFor more information on claim types, see ClaimTypes.

このサンプルアプリでは、 urn:google:locale urn:google:picture locale Google ユーザーデータのキーとキーから、ロケール () と画像 () の要求を作成し picture ます。The sample app creates locale (urn:google:locale) and picture (urn:google:picture) claims from the locale and picture keys in Google user data:

services.AddAuthentication().AddGoogle(options =>
{
    // Provide the Google Client ID
    options.ClientId = "XXXXXXXXXXXXXXX.apps.googleusercontent.com";
    // Register with User Secrets using:
    // dotnet user-secrets set "Authentication:Google:ClientId" "{Client ID}"

    // Provide the Google Client Secret
    options.ClientSecret = "{Client Secret}";
    // Register with User Secrets using:
    // dotnet user-secrets set "Authentication:Google:ClientSecret" "{Client Secret}"

    options.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url");
    options.ClaimActions.MapJsonKey("urn:google:locale", "locale", "string");
    options.SaveTokens = true;

    options.Events.OnCreatingTicket = ctx =>
    {
        List<AuthenticationToken> tokens = ctx.Properties.GetTokens().ToList(); 

        tokens.Add(new AuthenticationToken()
        {
            Name = "TicketCreated", 
            Value = DateTime.UtcNow.ToString()
        });

        ctx.Properties.StoreTokens(tokens);

        return Task.CompletedTask;
    };
});

では、 Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal.ExternalLoginModel.OnPostConfirmationAsync IdentityUser ( ApplicationUser ) がを使用してアプリにサインイン SignInAsync します。In Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal.ExternalLoginModel.OnPostConfirmationAsync, an IdentityUser (ApplicationUser) is signed into the app with SignInAsync. サインインプロセス中に、で UserManager<TUser> ApplicationUser 使用可能なユーザーデータの要求を格納でき Principal ます。During the sign in process, the UserManager<TUser> can store an ApplicationUser claims for user data available from the Principal.

サンプルアプリでは、 OnPostConfirmationAsync ( Account/externallogin. cshtml ) によって、 urn:google:locale urn:google:picture ApplicationUser 次の要求を含む、サインインしたのロケール () と画像 () の要求が確立 GivenName されます。In the sample app, OnPostConfirmationAsync ( Account/ExternalLogin.cshtml.cs ) establishes the locale (urn:google:locale) and picture (urn:google:picture) claims for the signed in ApplicationUser, including a claim for GivenName:

public async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    // Get the information about the user from the external login provider
    var info = await _signInManager.GetExternalLoginInfoAsync();

    if (info == null)
    {
        ErrorMessage = 
            "Error loading external login information during confirmation.";

        return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
    }

    if (ModelState.IsValid)
    {
        var user = new IdentityUser
        {
            UserName = Input.Email, 
            Email = Input.Email 
        };

        var result = await _userManager.CreateAsync(user);

        if (result.Succeeded)
        {
            result = await _userManager.AddLoginAsync(user, info);

            if (result.Succeeded)
            {
                // If they exist, add claims to the user for:
                //    Given (first) name
                //    Locale
                //    Picture
                if (info.Principal.HasClaim(c => c.Type == ClaimTypes.GivenName))
                {
                    await _userManager.AddClaimAsync(user, 
                        info.Principal.FindFirst(ClaimTypes.GivenName));
                }

                if (info.Principal.HasClaim(c => c.Type == "urn:google:locale"))
                {
                    await _userManager.AddClaimAsync(user, 
                        info.Principal.FindFirst("urn:google:locale"));
                }

                if (info.Principal.HasClaim(c => c.Type == "urn:google:picture"))
                {
                    await _userManager.AddClaimAsync(user, 
                        info.Principal.FindFirst("urn:google:picture"));
                }

                // Include the access token in the properties
                var props = new AuthenticationProperties();
                props.StoreTokens(info.AuthenticationTokens);
                props.IsPersistent = true;

                await _signInManager.SignInAsync(user, props);

                _logger.LogInformation(
                    "User created an account using {Name} provider.", 
                    info.LoginProvider);

                return LocalRedirect(returnUrl);
            }
        }

        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    LoginProvider = info.LoginProvider;
    ReturnUrl = returnUrl;
    return Page();
}

既定では、ユーザーの要求は認証に格納され cookie ます。By default, a user's claims are stored in the authentication cookie. 認証 cookie が大きすぎると、次の理由により、アプリでエラーが発生する可能性があります。If the authentication cookie is too large, it can cause the app to fail because:

  • ブラウザーは、 cookie ヘッダーが長すぎることを検出します。The browser detects that the cookie header is too long.
  • 要求の全体的なサイズが大きすぎます。The overall size of the request is too large.

ユーザーの要求を処理するために大量のユーザーデータが必要な場合は、次のようにします。If a large amount of user data is required for processing user requests:

  • 要求処理のユーザー要求の数とサイズは、アプリで必要なもののみに制限します。Limit the number and size of user claims for request processing to only what the app requires.
  • 認証ミドルウェアのカスタムを使用し ITicketStore て、 Cookie SessionStore 要求間で id を格納します。Use a custom ITicketStore for the Cookie Authentication Middleware's SessionStore to store identity across requests. 小さいセッション識別子キーをクライアントに送信するだけで、サーバー上の大量の id 情報を保持します。Preserve large quantities of identity information on the server while only sending a small session identifier key to the client.

アクセストークンを保存するSave the access token

SaveTokens 承認が成功した後に、アクセストークンと更新トークンをに格納するかどうかを定義し AuthenticationProperties ます。SaveTokens defines whether access and refresh tokens should be stored in the AuthenticationProperties after a successful authorization. SaveTokensfalse 、最終的な認証のサイズを小さくするために、既定でに設定され cookie ます。SaveTokens is set to false by default to reduce the size of the final authentication cookie.

サンプルアプリでは、の値をに設定し SaveTokens true GoogleOptions ます。The sample app sets the value of SaveTokens to true in GoogleOptions:

services.AddAuthentication().AddGoogle(options =>
{
    // Provide the Google Client ID
    options.ClientId = "XXXXXXXXXXXXXXX.apps.googleusercontent.com";
    // Register with User Secrets using:
    // dotnet user-secrets set "Authentication:Google:ClientId" "{Client ID}"

    // Provide the Google Client Secret
    options.ClientSecret = "{Client Secret}";
    // Register with User Secrets using:
    // dotnet user-secrets set "Authentication:Google:ClientSecret" "{Client Secret}"

    options.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url");
    options.ClaimActions.MapJsonKey("urn:google:locale", "locale", "string");
    options.SaveTokens = true;

    options.Events.OnCreatingTicket = ctx =>
    {
        List<AuthenticationToken> tokens = ctx.Properties.GetTokens().ToList(); 

        tokens.Add(new AuthenticationToken()
        {
            Name = "TicketCreated", 
            Value = DateTime.UtcNow.ToString()
        });

        ctx.Properties.StoreTokens(tokens);

        return Task.CompletedTask;
    };
});

を実行するときに、 OnPostConfirmationAsync の外部プロバイダーからアクセストークン (Externallogininfo. authenticationtokens) をに格納 ApplicationUser AuthenticationProperties します。When OnPostConfirmationAsync executes, store the access token (ExternalLoginInfo.AuthenticationTokens) from the external provider in the ApplicationUser's AuthenticationProperties.

サンプルアプリでは、アクセストークン OnPostConfirmationAsync (新しいユーザー登録) と OnGetCallbackAsync (以前に登録されたユーザー) を Account/externallogin. cshtml. cs に保存します。The sample app saves the access token in OnPostConfirmationAsync (new user registration) and OnGetCallbackAsync (previously registered user) in Account/ExternalLogin.cshtml.cs :

public async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    // Get the information about the user from the external login provider
    var info = await _signInManager.GetExternalLoginInfoAsync();

    if (info == null)
    {
        ErrorMessage = 
            "Error loading external login information during confirmation.";

        return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
    }

    if (ModelState.IsValid)
    {
        var user = new IdentityUser
        {
            UserName = Input.Email, 
            Email = Input.Email 
        };

        var result = await _userManager.CreateAsync(user);

        if (result.Succeeded)
        {
            result = await _userManager.AddLoginAsync(user, info);

            if (result.Succeeded)
            {
                // If they exist, add claims to the user for:
                //    Given (first) name
                //    Locale
                //    Picture
                if (info.Principal.HasClaim(c => c.Type == ClaimTypes.GivenName))
                {
                    await _userManager.AddClaimAsync(user, 
                        info.Principal.FindFirst(ClaimTypes.GivenName));
                }

                if (info.Principal.HasClaim(c => c.Type == "urn:google:locale"))
                {
                    await _userManager.AddClaimAsync(user, 
                        info.Principal.FindFirst("urn:google:locale"));
                }

                if (info.Principal.HasClaim(c => c.Type == "urn:google:picture"))
                {
                    await _userManager.AddClaimAsync(user, 
                        info.Principal.FindFirst("urn:google:picture"));
                }

                // Include the access token in the properties
                var props = new AuthenticationProperties();
                props.StoreTokens(info.AuthenticationTokens);
                props.IsPersistent = true;

                await _signInManager.SignInAsync(user, props);

                _logger.LogInformation(
                    "User created an account using {Name} provider.", 
                    info.LoginProvider);

                return LocalRedirect(returnUrl);
            }
        }

        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    LoginProvider = info.LoginProvider;
    ReturnUrl = returnUrl;
    return Page();
}

カスタムトークンを追加する方法How to add additional custom tokens

の一部として格納されているカスタムトークンを追加する方法を示すために、 SaveTokens サンプルアプリでは、の AuthenticationToken.Name の現在のを使用してを追加し AuthenticationToken DateTime AuthenticationToken.Name TicketCreated ます。To demonstrate how to add a custom token, which is stored as part of SaveTokens, the sample app adds an AuthenticationToken with the current DateTime for an AuthenticationToken.Name of TicketCreated:

services.AddAuthentication().AddGoogle(options =>
{
    // Provide the Google Client ID
    options.ClientId = "XXXXXXXXXXXXXXX.apps.googleusercontent.com";
    // Register with User Secrets using:
    // dotnet user-secrets set "Authentication:Google:ClientId" "{Client ID}"

    // Provide the Google Client Secret
    options.ClientSecret = "{Client Secret}";
    // Register with User Secrets using:
    // dotnet user-secrets set "Authentication:Google:ClientSecret" "{Client Secret}"

    options.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url");
    options.ClaimActions.MapJsonKey("urn:google:locale", "locale", "string");
    options.SaveTokens = true;

    options.Events.OnCreatingTicket = ctx =>
    {
        List<AuthenticationToken> tokens = ctx.Properties.GetTokens().ToList(); 

        tokens.Add(new AuthenticationToken()
        {
            Name = "TicketCreated", 
            Value = DateTime.UtcNow.ToString()
        });

        ctx.Properties.StoreTokens(tokens);

        return Task.CompletedTask;
    };
});

要求の作成と追加Creating and adding claims

フレームワークには、要求を作成してコレクションに追加するための一般的なアクションと拡張メソッドが用意されています。The framework provides common actions and extension methods for creating and adding claims to the collection. 詳細については、「 ClaimActionCollectionMapExtensions 」および「 ClaimActionCollectionUniqueExtensions」を参照してください。For more information, see the ClaimActionCollectionMapExtensions and ClaimActionCollectionUniqueExtensions.

ユーザーは、から派生し、抽象メソッドを実装することによって、カスタムアクションを定義でき ClaimAction Run ます。Users can define custom actions by deriving from ClaimAction and implementing the abstract Run method.

詳細については、「Microsoft.AspNetCore.Authentication.OAuth.Claims」を参照してください。For more information, see Microsoft.AspNetCore.Authentication.OAuth.Claims.

要求アクションと要求の削除Removal of claim actions and claims

Claimactioncollection。 Remove (String) は、指定されたのすべての要求アクションを ClaimType コレクションから削除します。ClaimActionCollection.Remove(String) removes all claim actions for the given ClaimType from the collection. DeleteClaim (ClaimActionCollection, String) は、指定されたの要求を ClaimType id から削除します。ClaimActionCollectionMapExtensions.DeleteClaim(ClaimActionCollection, String) deletes a claim of the given ClaimType from the identity. DeleteClaim は、主に OpenID connect (OIDC) と共に使用して、プロトコルによって生成された要求を削除します。DeleteClaim is primarily used with OpenID Connect (OIDC) to remove protocol-generated claims.

サンプルアプリの出力Sample app output

User Claims

http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier
    9b342344f-7aab-43c2-1ac1-ba75912ca999
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
    someone@gmail.com
AspNet.Identity.SecurityStamp
    7D4312MOWRYYBFI1KXRPHGOSTBVWSFDE
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
    Judy
urn:google:locale
    en
urn:google:picture
    https://lh4.googleusercontent.com/-XXXXXX/XXXXXX/XXXXXX/XXXXXX/photo.jpg

Authentication Properties

.Token.access_token
    yc23.AlvoZqz56...1lxltXV7D-ZWP9
.Token.token_type
    Bearer
.Token.expires_at
    2019-04-11T22:14:51.0000000+00:00
.Token.TicketCreated
    4/11/2019 9:14:52 PM
.TokenNames
    access_token;token_type;expires_at;TicketCreated
.persistent
.issued
    Thu, 11 Apr 2019 20:51:06 GMT
.expires
    Thu, 25 Apr 2019 20:51:06 GMT

プロキシまたはロード バランサーによる要求情報の転送Forward request information with a proxy or load balancer

アプリがプロキシ サーバーまたはロード バランサーの背後に展開されると、元の要求情報の一部が要求ヘッダー内でアプリに転送される場合があります。If the app is deployed behind a proxy server or load balancer, some of the original request information might be forwarded to the app in request headers. 通常、この情報にはセキュアな要求スキーム (https)、ホスト、およびクライアント IP アドレスが含まれます。This information usually includes the secure request scheme (https), host, and client IP address. アプリでは、これらの要求ヘッダーを自動的に読み取って、元の要求情報を検出して使用することはありません。Apps don't automatically read these request headers to discover and use the original request information.

スキームは、外部プロバイダーによる認証フローに影響を及ぼすリンクの生成に使用されます。The scheme is used in link generation that affects the authentication flow with external providers. セキュアなスキーム (https) が失われると、アプリでは、安全ではない不正なリダイレクト URL が生成されます。Losing the secure scheme (https) results in the app generating incorrect insecure redirect URLs.

Forwarded Headers Middleware を使用して、アプリが要求を処理する際に元の要求情報を利用できるようにします。Use Forwarded Headers Middleware to make the original request information available to the app for request processing.

詳細については、「プロキシ サーバーとロード バランサーを使用するために ASP.NET Core を構成する」を参照してください。For more information, see プロキシ サーバーとロード バランサーを使用するために ASP.NET Core を構成する.

その他の資料Additional resources

  • dotnet/AspNetCore engineering の社会 Alsample アプリ: リンクされたサンプルアプリは、 Dotnet/AspNetCore GitHub リポジトリの master エンジニアリングブランチにあります。dotnet/AspNetCore engineering SocialSample app: The linked sample app is on the dotnet/AspNetCore GitHub repo's master engineering branch. ブランチには、 master ASP.NET Core の次のリリースでアクティブな開発のコードが含まれています。The master branch contains code under active development for the next release of ASP.NET Core. リリースされたバージョンの ASP.NET Core 用のサンプルアプリのバージョンを表示するには、[ ブランチ ] ドロップダウンリストを使用してリリースブランチを選択します (たとえば、 release/{X.Y} )。To see a version of the sample app for a released version of ASP.NET Core, use the Branch drop down list to select a release branch (for example release/{X.Y}).