Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
En ASP.NET Core-app kan upprätta ytterligare anspråk och token från externa autentiseringsleverantörer, till exempel Facebook, Google, Microsoft och Twitter. Varje leverantör visar olika information om användare på sin plattform, men mönstret för att ta emot och omvandla användardata till ytterligare anspråk är detsamma.
Prerequisites
Bestäm vilka externa autentiseringsprovidrar som ska stödjas i appen. Registrera appen för varje provider och skaffa ett klient-ID och en klienthemlighet. Mer information finns i Använda externa inloggningsprovidrar med Identity i ASP.NET Core. Exempelappen använder Google-autentiseringsprovidern.
Ange klient-ID och klienthemlighet
OAuth-autentiseringsprovidern upprättar en förtroenderelation med en app med hjälp av ett klient-ID och en klienthemlighet. Klient-ID och klienthemlighetsvärden skapas för appen av den externa autentiseringsprovidern när appen registreras hos providern. Varje extern provider som appen använder måste konfigureras separat med providerns klient-ID och klienthemlighet. Mer information finns i avsnitten om extern autentiseringsprovider som gäller:
- Facebook-autentisering
- Google-autentisering
- Microsoft-autentisering
- Twitter-autentisering
- Andra autentiseringsprovidrar
- OpenIdConnect
Valfria anspråk som skickas i ID:t eller åtkomsttoken från autentiseringsprovidern konfigureras vanligtvis i leverantörens onlineportal. Till exempel tillåter Microsoft Entra-ID att tilldela valfria anspråk till appens ID-token på appregistreringens tokenkonfigurationsblad . Mer information finns i Hur du: Tillhandahålla valfria krav till din app (Azure-dokumentation). För andra leverantörer kan du läsa deras externa dokumentationsuppsättningar.
Exempelappen konfigurerar Google-autentiseringsprovidern med ett klient-ID och en klienthemlighet som tillhandahålls av Google:
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebGoogOauth.Data;
var builder = WebApplication.CreateBuilder(args);
var configuration = builder.Configuration;
builder.Services.AddAuthentication().AddGoogle(googleOptions =>
{
    googleOptions.ClientId = configuration["Authentication:Google:ClientId"];
    googleOptions.ClientSecret = configuration["Authentication:Google:ClientSecret"];
    googleOptions.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url");
    googleOptions.ClaimActions.MapJsonKey("urn:google:locale", "locale", "string");
    googleOptions.SaveTokens = true;
    googleOptions.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;
    };
});
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => 
                                  options.SignIn.RequireConfirmedAccount = true)
                                 .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
var app = builder.Build();
// Remaining code removed for brevity.
Upprätta autentiseringsomfånget
Ange listan över behörigheter som ska hämtas från providern genom att ange Scope. Autentiseringsomfång för vanliga externa leverantörer visas i följande tabell.
| Provider | Scope | 
|---|---|
| https://www.facebook.com/dialog/oauth | |
| profileemailopenid | |
| Microsoft | https://login.microsoftonline.com/common/oauth2/v2.0/authorize | 
| https://api.twitter.com/oauth/authenticate | 
I exempelappen läggs Googles profile, emailoch openid omfång automatiskt till av ramverket när AddGoogle anropas på AuthenticationBuilder. Om appen kräver ytterligare omfång lägger du till dem i alternativen. I följande exempel läggs Google-omfånget https://www.googleapis.com/auth/user.birthday.read till för att hämta en användares födelsedag:
options.Scope.Add("https://www.googleapis.com/auth/user.birthday.read");
Mappa användardatanycklar och skapa anspråk
I providerns alternativ anger du en MapJsonKey eller MapJsonSubKey för varje nyckel eller undernyckel i den externa providerns JSON-användardata för appidentiteten som ska läsas vid inloggning. Mer information om anspråkstyper finns i ClaimTypes.
Exempelappen skapar lokalanspråk (urn:google:locale) och bildanspråk (urn:google:picture) från nycklarna locale och picture i användardata från Google:
builder.Services.AddAuthentication().AddGoogle(googleOptions =>
{
    googleOptions.ClientId = configuration["Authentication:Google:ClientId"];
    googleOptions.ClientSecret = configuration["Authentication:Google:ClientSecret"];
    googleOptions.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url");
    googleOptions.ClaimActions.MapJsonKey("urn:google:locale", "locale", "string");
    googleOptions.SaveTokens = true;
    googleOptions.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;
    };
});
I Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal.ExternalLoginModel.OnPostConfirmationAsync loggar en IdentityUser (ApplicationUser) in med SignInAsync i appen. Under inloggningsprocessen kan UserManager<TUser> lagra anspråk ApplicationUser för användardata som är tillgängliga från Principal.
I exempelappen OnPostConfirmationAsync (Account/ExternalLogin.cshtml.cs) upprättas språkvariant (urn:google:locale) och bild (urn:google:picture) anspråk för den inloggade ApplicationUser, inklusive ett anspråk för 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 = CreateUser();
        await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
        await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
        var result = await _userManager.CreateAsync(user);
        if (result.Succeeded)
        {
            result = await _userManager.AddLoginAsync(user, info);
            if (result.Succeeded)
            {
                _logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);
                // 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
                // using Microsoft.AspNetCore.Authentication;
                var props = new AuthenticationProperties();
                props.StoreTokens(info.AuthenticationTokens);
                props.IsPersistent = false;
                var userId = await _userManager.GetUserIdAsync(user);
                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                var callbackUrl = Url.Page(
                    "/Account/ConfirmEmail",
                    pageHandler: null,
                    values: new { area = "Identity", userId = userId, code = code },
                    protocol: Request.Scheme);
                await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                    $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
                // If account confirmation is required, we need to show the link if we don't have a real email sender
                if (_userManager.Options.SignIn.RequireConfirmedAccount)
                {
                    return RedirectToPage("./RegisterConfirmation", new { Email = Input.Email });
                }
                await _signInManager.SignInAsync(user, props, info.LoginProvider);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }
    ProviderDisplayName = info.ProviderDisplayName;
    ReturnUrl = returnUrl;
    return Page();
}
Som standard lagras en användares anspråk i autentiseringen cookie. Om autentiseringen cookie är för stor kan den orsaka att appen misslyckas eftersom:
- Webbläsaren identifierar att cookie rubriken är för lång.
- Den totala storleken på begäran är för stor.
Om en stor mängd användardata krävs för bearbetning av användarbegäranden:
- Begränsa antalet och storleken på användaranspråk för bearbetning av begäranden till endast det som appen kräver.
- Använd en anpassad ITicketStore för att lagra identiteter mellan begäranden i autentiseringsmellanprogrammets CookieSessionStore. Bevara stora mängder identitetsinformation på servern samtidigt som du bara skickar en liten sessionsidentifierarnyckel till klienten.
Spara åtkomsttoken
              SaveTokens definierar om åtkomst- och uppdateringstoken ska lagras i AuthenticationProperties efter en lyckad auktorisering. 
              SaveTokens är inställd false på som standard för att minska storleken på den slutliga autentiseringen cookie.
Exempelappen anger värdet av SaveTokens till true i GoogleOptions:
builder.Services.AddAuthentication().AddGoogle(googleOptions =>
{
    googleOptions.ClientId = configuration["Authentication:Google:ClientId"];
    googleOptions.ClientSecret = configuration["Authentication:Google:ClientSecret"];
    googleOptions.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url");
    googleOptions.ClaimActions.MapJsonKey("urn:google:locale", "locale", "string");
    googleOptions.SaveTokens = true;
    googleOptions.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;
    };
});
När OnPostConfirmationAsync körs lagrar du åtkomsttoken (ExternalLoginInfo.AuthenticationTokens) från den externa providern i ApplicationUser's AuthenticationProperties.
Exempelappen sparar åtkomsttoken i OnPostConfirmationAsync (ny användarregistrering) och OnGetCallbackAsync (tidigare registrerad användare) i 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 = CreateUser();
        await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
        await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
        var result = await _userManager.CreateAsync(user);
        if (result.Succeeded)
        {
            result = await _userManager.AddLoginAsync(user, info);
            if (result.Succeeded)
            {
                _logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);
                // 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
                // using Microsoft.AspNetCore.Authentication;
                var props = new AuthenticationProperties();
                props.StoreTokens(info.AuthenticationTokens);
                props.IsPersistent = false;
                var userId = await _userManager.GetUserIdAsync(user);
                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                var callbackUrl = Url.Page(
                    "/Account/ConfirmEmail",
                    pageHandler: null,
                    values: new { area = "Identity", userId = userId, code = code },
                    protocol: Request.Scheme);
                await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                    $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
                // If account confirmation is required, we need to show the link if we don't have a real email sender
                if (_userManager.Options.SignIn.RequireConfirmedAccount)
                {
                    return RedirectToPage("./RegisterConfirmation", new { Email = Input.Email });
                }
                await _signInManager.SignInAsync(user, props, info.LoginProvider);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }
    ProviderDisplayName = info.ProviderDisplayName;
    ReturnUrl = returnUrl;
    return Page();
}
Note
Information om hur du skickar token till komponenterna i Razor en app på serversidan finns Blazor.Blazor Web App
Så här lägger du till ytterligare anpassade token
För att visa hur du lägger till en anpassad token som lagras som en del av SaveTokens lägger exempelappen till en AuthenticationToken med den aktuella DateTime för en AuthenticationToken.Name av TicketCreated:
builder.Services.AddAuthentication().AddGoogle(googleOptions =>
{
    googleOptions.ClientId = configuration["Authentication:Google:ClientId"];
    googleOptions.ClientSecret = configuration["Authentication:Google:ClientSecret"];
    googleOptions.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url");
    googleOptions.ClaimActions.MapJsonKey("urn:google:locale", "locale", "string");
    googleOptions.SaveTokens = true;
    googleOptions.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;
    };
});
Skapa och lägga till anspråk
Ramverket innehåller vanliga åtgärder och tilläggsmetoder för att skapa och lägga till anspråk i samlingen. Mer information finns i ClaimActionCollectionMapExtensions och ClaimActionCollectionUniqueExtensions.
Användare kan definiera anpassade åtgärder genom att härleda från ClaimAction och implementera den abstrakta Run metoden.
Mer information finns i Microsoft.AspNetCore.Authentication.OAuth.Claims.
Lägga till och uppdatera användaranspråk
Anspråk kopieras från externa leverantörer till användardatabasen vid första registreringen, inte vid inloggning. Om ytterligare anspråk är aktiverade i en app efter att en användare har registrerat sig för att använda appen anropar du SignInManager.RefreshSignInAsync på en användare för att tvinga fram genereringen av en ny autentisering cookie.
I utvecklingsmiljön som arbetar med testanvändarkonton tar du bort och återskapar användarkontot. För produktionssystem kan nya anspråk som läggs till i appen återfyllas till användarkonton. När du har strukturerat ExternalLogin sidan i appen på Areas/Pages/Identity/Account/Manage, lägger du till följande kod i ExternalLoginModel i filen ExternalLogin.cshtml.cs.
Lägg till en ordlista med tillagda anspråk. Använd ordlistenycklarna för att lagra anspråkstyperna och använd värdena för att lagra ett standardvärde. Lägg till följande rad överst i klassen. I följande exempel förutsätts att ett anspråk läggs till för användarens Google-bild med en allmän headshot-bild som standardvärde:
private readonly IReadOnlyDictionary<string, string> _claimsToSync =
     new Dictionary<string, string>()
     {
             { "urn:google:picture", "https://localhost:5001/headshot.png" },
     };
Ersätt standardkoden för OnGetCallbackAsync metoden med följande kod. Koden loopar genom anspråksordlistan. Anspråk läggs till (återfylls) eller uppdateras för varje användare. När anspråk läggs till eller uppdateras uppdateras användarens inloggning med hjälp av SignInManager<TUser>, vilket bevarar de befintliga autentiseringsegenskaperna (AuthenticationProperties).
private readonly IReadOnlyDictionary<string, string> _claimsToSync =
     new Dictionary<string, string>()
     {
             { "urn:google:picture", "https://localhost:5001/headshot.png" },
     };
public async Task<IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    if (remoteError != null)
    {
        ErrorMessage = $"Error from external provider: {remoteError}";
        return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
    }
    var info = await _signInManager.GetExternalLoginInfoAsync();
    if (info == null)
    {
        ErrorMessage = "Error loading external login information.";
        return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
    }
    // Sign in the user with this external login provider if the user already has a login.
    var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);
    if (result.Succeeded)
    {
        _logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider);
        if (_claimsToSync.Count > 0)
        {
            var user = await _userManager.FindByLoginAsync(info.LoginProvider,
                info.ProviderKey);
            var userClaims = await _userManager.GetClaimsAsync(user);
            bool refreshSignIn = false;
            foreach (var addedClaim in _claimsToSync)
            {
                var userClaim = userClaims
                    .FirstOrDefault(c => c.Type == addedClaim.Key);
                if (info.Principal.HasClaim(c => c.Type == addedClaim.Key))
                {
                    var externalClaim = info.Principal.FindFirst(addedClaim.Key);
                    if (userClaim == null)
                    {
                        await _userManager.AddClaimAsync(user,
                            new Claim(addedClaim.Key, externalClaim.Value));
                        refreshSignIn = true;
                    }
                    else if (userClaim.Value != externalClaim.Value)
                    {
                        await _userManager
                            .ReplaceClaimAsync(user, userClaim, externalClaim);
                        refreshSignIn = true;
                    }
                }
                else if (userClaim == null)
                {
                    // Fill with a default value
                    await _userManager.AddClaimAsync(user, new Claim(addedClaim.Key,
                        addedClaim.Value));
                    refreshSignIn = true;
                }
            }
            if (refreshSignIn)
            {
                await _signInManager.RefreshSignInAsync(user);
            }
        }
        return LocalRedirect(returnUrl);
    }
    if (result.IsLockedOut)
    {
        return RedirectToPage("./Lockout");
    }
    else
    {
        // If the user does not have an account, then ask the user to create an account.
        ReturnUrl = returnUrl;
        ProviderDisplayName = info.ProviderDisplayName;
        if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email))
        {
            Input = new InputModel
            {
                Email = info.Principal.FindFirstValue(ClaimTypes.Email)
            };
        }
        return Page();
    }
}
En liknande metod används när anspråk ändras medan en användare är inloggad men ett steg för återfyllnad inte krävs. Om du vill uppdatera en användares anspråk anropar du följande på användaren:
- UserManager.ReplaceClaimAsync på användaren för anspråk som lagras i identitetsdatabasen.
- SignInManager.RefreshSignInAsync på användaren för att tvinga genereringen av en ny autentisering cookie.
Ta bort anspråksåtgärder och anspråk
ClaimActionCollection.Remove(String) tar bort alla anspråksåtgärder för angivna ClaimType från samlingen. ClaimActionCollectionMapExtensions.DeleteClaim(ClaimActionCollection, String) tar bort ett anspråk på angivet ClaimType från identiteten. DeleteClaim används främst med OpenID Connect (OIDC) för att ta bort protokollgenererade anspråk.
Exempel på apputdata
Kör exempelappen och välj länken MyClaims :
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
Vidarebefordra begärans information genom en proxy eller lastbalanserare
Om appen distribueras bakom en proxyserver eller lastbalanserare kan en del av den ursprungliga begärandeinformationen vidarebefordras till appen i begärandehuvuden. Den här informationen omfattar vanligtvis schemat för säker begäran (https), värd och klientens IP-adress. Appar läser inte automatiskt dessa begärandehuvuden för att identifiera och använda den ursprungliga begärandeinformationen.
Schemat används i länkgenerering som påverkar autentiseringsflödet med externa leverantörer. Att förlora det säkra schemat (https) resulterar i att appen genererar felaktiga osäkra omdirigerings-URL:er.
Använd middleware för vidarebefordrade rubriker för att göra den ursprungliga begärandeinformationen tillgänglig för appen vid behandling av förfrågningar.
Mer information finns i Konfigurera ASP.NET Core att fungera med proxyservrar och lastbalanserare.
En ASP.NET Core-app kan upprätta ytterligare anspråk och token från externa autentiseringsleverantörer, till exempel Facebook, Google, Microsoft och Twitter. Varje leverantör visar olika information om användare på sin plattform, men mönstret för att ta emot och omvandla användardata till ytterligare anspråk är detsamma.
Visa eller ladda ned exempelkod (hur du laddar ned)
Prerequisites
Bestäm vilka externa autentiseringsprovidrar som ska stödjas i appen. Registrera appen för varje provider och skaffa ett klient-ID och en klienthemlighet. Mer information finns i Använda externa inloggningsprovidrar med Identity i ASP.NET Core. Exempelappen använder Google-autentiseringsprovidern.
Ange klient-ID och klienthemlighet
OAuth-autentiseringsprovidern upprättar en förtroenderelation med en app med hjälp av ett klient-ID och en klienthemlighet. Klient-ID och klienthemlighetsvärden skapas för appen av den externa autentiseringsprovidern när appen registreras hos providern. Varje extern provider som appen använder måste konfigureras separat med providerns klient-ID och klienthemlighet. Mer information finns i avsnitten om extern autentiseringsprovider som gäller för ditt scenario:
- Facebook-autentisering
- Google-autentisering
- Microsoft-autentisering
- Twitter-autentisering
- Andra autentiseringsprovidrar
- OpenIdConnect
Valfria anspråk som skickas i ID:t eller åtkomsttoken från autentiseringsprovidern konfigureras vanligtvis i leverantörens onlineportal. Med Microsoft Entra-ID kan du till exempel tilldela valfria anspråk till appens ID-token på appregistreringens tokenkonfigurationsblad . Mer information finns i Hur du: Tillhandahålla valfria krav till din app (Azure-dokumentation). För andra leverantörer kan du läsa deras externa dokumentationsuppsättningar.
Exempelappen konfigurerar Google-autentiseringsprovidern med ett klient-ID och en klienthemlighet som tillhandahålls av 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;
    };
});
Upprätta autentiseringsomfånget
Ange listan över behörigheter som ska hämtas från providern genom att ange Scope. Autentiseringsomfång för vanliga externa leverantörer visas i följande tabell.
| Provider | Scope | 
|---|---|
| https://www.facebook.com/dialog/oauth | |
| profileemailopenid | |
| Microsoft | https://login.microsoftonline.com/common/oauth2/v2.0/authorize | 
| https://api.twitter.com/oauth/authenticate | 
I exempelappen läggs Googles profile, emailoch openid omfång automatiskt till av ramverket när AddGoogle anropas på AuthenticationBuilder. Om appen kräver ytterligare omfång lägger du till dem i alternativen. I följande exempel läggs Google-omfånget https://www.googleapis.com/auth/user.birthday.read till för att hämta en användares födelsedag:
options.Scope.Add("https://www.googleapis.com/auth/user.birthday.read");
Mappa användardatanycklar och skapa anspråk
I providerns alternativ anger du en MapJsonKey eller MapJsonSubKey för varje nyckel/undernyckel i den externa providerns JSON-användardata för appidentiteten som ska läsas vid inloggning. Mer information om anspråkstyper finns i ClaimTypes.
Exempelappen skapar lokalanspråk (urn:google:locale) och bildanspråk (urn:google:picture) från nycklarna locale och picture i användardata från 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;
    };
});
I Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal.ExternalLoginModel.OnPostConfirmationAsync loggar en IdentityUser (ApplicationUser) in med SignInAsync i appen. Under inloggningsprocessen kan UserManager<TUser> lagra anspråk ApplicationUser för användardata som är tillgängliga från Principal.
I exempelappen OnPostConfirmationAsync (Account/ExternalLogin.cshtml.cs) upprättas språkvariant (urn:google:locale) och bild (urn:google:picture) anspråk för den inloggade ApplicationUser, inklusive ett anspråk för 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();
}
Som standard lagras en användares anspråk i autentiseringen cookie. Om autentiseringen cookie är för stor kan den orsaka att appen misslyckas eftersom:
- Webbläsaren identifierar att cookie rubriken är för lång.
- Den totala storleken på begäran är för stor.
Om en stor mängd användardata krävs för bearbetning av användarbegäranden:
- Begränsa antalet och storleken på användaranspråk för bearbetning av begäranden till endast det som appen kräver.
- Använd en anpassad ITicketStore för att lagra identiteter mellan begäranden i autentiseringsmellanprogrammets CookieSessionStore. Bevara stora mängder identitetsinformation på servern samtidigt som du bara skickar en liten sessionsidentifierarnyckel till klienten.
Spara åtkomsttoken
              SaveTokens definierar om åtkomst- och uppdateringstoken ska lagras i AuthenticationProperties efter en lyckad auktorisering. 
              SaveTokens är inställd false på som standard för att minska storleken på den slutliga autentiseringen cookie.
Exempelappen anger värdet av SaveTokens till true i 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;
    };
});
När OnPostConfirmationAsync körs lagrar du åtkomsttoken (ExternalLoginInfo.AuthenticationTokens) från den externa providern i ApplicationUser's AuthenticationProperties.
Exempelappen sparar åtkomsttoken i OnPostConfirmationAsync (ny användarregistrering) och OnGetCallbackAsync (tidigare registrerad användare) i 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();
}
Note
Information om hur du skickar token till komponenterna i Razor en app på serversidan finns Blazor.Blazor Web App
Så här lägger du till ytterligare anpassade token
För att visa hur du lägger till en anpassad token som lagras som en del av SaveTokens lägger exempelappen till en AuthenticationToken med den aktuella DateTime för en AuthenticationToken.Name av 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;
    };
});
Skapa och lägga till anspråk
Ramverket innehåller vanliga åtgärder och tilläggsmetoder för att skapa och lägga till anspråk i samlingen. Mer information finns i ClaimActionCollectionMapExtensions och ClaimActionCollectionUniqueExtensions.
Användare kan definiera anpassade åtgärder genom att härleda från ClaimAction och implementera den abstrakta Run metoden.
Mer information finns i Microsoft.AspNetCore.Authentication.OAuth.Claims.
Lägga till och uppdatera användaranspråk
Anspråk kopieras från externa leverantörer till användardatabasen vid första registreringen, inte vid inloggning. Om ytterligare anspråk är aktiverade i en app efter att en användare har registrerat sig för att använda appen anropar du SignInManager.RefreshSignInAsync på en användare för att tvinga fram genereringen av en ny autentisering cookie.
I utvecklingsmiljön som arbetar med testanvändarkonton kan du helt enkelt ta bort och återskapa användarkontot. För produktionssystem kan nya anspråk som läggs till i appen återfyllas till användarkonton. När du har strukturerat ExternalLogin sidan i appen på Areas/Pages/Identity/Account/Manage, lägger du till följande kod i ExternalLoginModel i filen ExternalLogin.cshtml.cs.
Lägg till en ordlista med tillagda anspråk. Använd ordlistenycklarna för att lagra anspråkstyperna och använd värdena för att lagra ett standardvärde. Lägg till följande rad överst i klassen. I följande exempel förutsätts att ett anspråk läggs till för användarens Google-bild med en allmän headshot-bild som standardvärde:
private readonly IReadOnlyDictionary<string, string> _claimsToSync = 
    new Dictionary<string, string>()
    {
        { "urn:google:picture", "https://localhost:5001/headshot.png" },
    };
Ersätt standardkoden för OnGetCallbackAsync metoden med följande kod. Koden loopar genom anspråksordlistan. Anspråk läggs till (återfylls) eller uppdateras för varje användare. När anspråk läggs till eller uppdateras uppdateras användarens inloggning med hjälp av SignInManager<TUser>, vilket bevarar de befintliga autentiseringsegenskaperna (AuthenticationProperties).
public async Task<IActionResult> OnGetCallbackAsync(
    string returnUrl = null, string remoteError = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    if (remoteError != null)
    {
        ErrorMessage = $"Error from external provider: {remoteError}";
        return RedirectToPage("./Login", new {ReturnUrl = returnUrl });
    }
    var info = await _signInManager.GetExternalLoginInfoAsync();
    if (info == null)
    {
        ErrorMessage = "Error loading external login information.";
        return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
    }
    // Sign in the user with this external login provider if the user already has a 
    // login.
    var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, 
        info.ProviderKey, isPersistent: false, bypassTwoFactor : true);
    if (result.Succeeded)
    {
        _logger.LogInformation("{Name} logged in with {LoginProvider} provider.", 
            info.Principal.Identity.Name, info.LoginProvider);
        if (_claimsToSync.Count > 0)
        {
            var user = await _userManager.FindByLoginAsync(info.LoginProvider, 
                info.ProviderKey);
            var userClaims = await _userManager.GetClaimsAsync(user);
            bool refreshSignIn = false;
            foreach (var addedClaim in _claimsToSync)
            {
                var userClaim = userClaims
                    .FirstOrDefault(c => c.Type == addedClaim.Key);
                if (info.Principal.HasClaim(c => c.Type == addedClaim.Key))
                {
                    var externalClaim = info.Principal.FindFirst(addedClaim.Key);
                    if (userClaim == null)
                    {
                        await _userManager.AddClaimAsync(user, 
                            new Claim(addedClaim.Key, externalClaim.Value));
                        refreshSignIn = true;
                    }
                    else if (userClaim.Value != externalClaim.Value)
                    {
                        await _userManager
                            .ReplaceClaimAsync(user, userClaim, externalClaim);
                        refreshSignIn = true;
                    }
                }
                else if (userClaim == null)
                {
                    // Fill with a default value
                    await _userManager.AddClaimAsync(user, new Claim(addedClaim.Key, 
                        addedClaim.Value));
                    refreshSignIn = true;
                }
            }
            if (refreshSignIn)
            {
                await _signInManager.RefreshSignInAsync(user);
            }
        }
        return LocalRedirect(returnUrl);
    }
    if (result.IsLockedOut)
    {
        return RedirectToPage("./Lockout");
    }
    else
    {
        // If the user does not have an account, then ask the user to create an 
        // account.
        ReturnUrl = returnUrl;
        ProviderDisplayName = info.ProviderDisplayName;
        if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email))
        {
            Input = new InputModel
            {
                Email = info.Principal.FindFirstValue(ClaimTypes.Email)
            };
        }
        return Page();
    }
}
En liknande metod används när anspråk ändras medan en användare är inloggad men ett steg för återfyllnad inte krävs. Om du vill uppdatera en användares anspråk anropar du följande på användaren:
- UserManager.ReplaceClaimAsync på användaren för anspråk som lagras i identitetsdatabasen.
- SignInManager.RefreshSignInAsync på användaren för att tvinga genereringen av en ny autentisering cookie.
Borttagning av anspråksåtgärder och anspråk
ClaimActionCollection.Remove(String) tar bort alla anspråksåtgärder för angivna ClaimType från samlingen. ClaimActionCollectionMapExtensions.DeleteClaim(ClaimActionCollection, String) tar bort ett anspråk på angivet ClaimType från identiteten. DeleteClaim används främst med OpenID Connect (OIDC) för att ta bort protokollgenererade anspråk.
Exempel på apputdata
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
Vidarebefordra begärans information genom en proxy eller lastbalanserare
Om appen distribueras bakom en proxyserver eller lastbalanserare kan en del av den ursprungliga begärandeinformationen vidarebefordras till appen i begärandehuvuden. Den här informationen omfattar vanligtvis schemat för säker begäran (https), värd och klientens IP-adress. Appar läser inte automatiskt dessa begärandehuvuden för att identifiera och använda den ursprungliga begärandeinformationen.
Schemat används i länkgenerering som påverkar autentiseringsflödet med externa leverantörer. Att förlora det säkra schemat (https) resulterar i att appen genererar felaktiga osäkra omdirigerings-URL:er.
Använd middleware för vidarebefordrade rubriker för att göra den ursprungliga begärandeinformationen tillgänglig för appen vid behandling av förfrågningar.
Mer information finns i Konfigurera ASP.NET Core att fungera med proxyservrar och lastbalanserare.
En ASP.NET Core-app kan upprätta ytterligare anspråk och token från externa autentiseringsleverantörer, till exempel Facebook, Google, Microsoft och Twitter. Varje leverantör visar olika information om användare på sin plattform, men mönstret för att ta emot och omvandla användardata till ytterligare anspråk är detsamma.
Visa eller ladda ned exempelkod (hur du laddar ned)
Prerequisites
Bestäm vilka externa autentiseringsprovidrar som ska stödjas i appen. Registrera appen för varje provider och skaffa ett klient-ID och en klienthemlighet. Mer information finns i Använda externa inloggningsprovidrar med Identity i ASP.NET Core. Exempelappen använder Google-autentiseringsprovidern.
Ange klient-ID och klienthemlighet
OAuth-autentiseringsprovidern upprättar en förtroenderelation med en app med hjälp av ett klient-ID och en klienthemlighet. Klient-ID och klienthemlighetsvärden skapas för appen av den externa autentiseringsprovidern när appen registreras hos providern. Varje extern provider som appen använder måste konfigureras separat med providerns klient-ID och klienthemlighet. Mer information finns i avsnitten om extern autentiseringsprovider som gäller för ditt scenario:
- Facebook-autentisering
- Google-autentisering
- Microsoft-autentisering
- Twitter-autentisering
- Andra autentiseringsprovidrar
- OpenIdConnect
Exempelappen konfigurerar Google-autentiseringsprovidern med ett klient-ID och en klienthemlighet som tillhandahålls av 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;
    };
});
Upprätta autentiseringsomfånget
Ange listan över behörigheter som ska hämtas från providern genom att ange Scope. Autentiseringsomfång för vanliga externa leverantörer visas i följande tabell.
| Provider | Scope | 
|---|---|
| https://www.facebook.com/dialog/oauth | |
| https://www.googleapis.com/auth/userinfo.profile | |
| Microsoft | https://login.microsoftonline.com/common/oauth2/v2.0/authorize | 
| https://api.twitter.com/oauth/authenticate | 
I exempelappen läggs Googles userinfo.profile omfång automatiskt till av ramverket när AddGoogle anropas på AuthenticationBuilder. Om appen kräver ytterligare omfång lägger du till dem i alternativen. I följande exempel läggs Google-omfånget https://www.googleapis.com/auth/user.birthday.read till för att hämta en användares födelsedag:
options.Scope.Add("https://www.googleapis.com/auth/user.birthday.read");
Mappa användardatanycklar och skapa anspråk
I providerns alternativ anger du en MapJsonKey eller MapJsonSubKey för varje nyckel/undernyckel i den externa providerns JSON-användardata för appidentiteten som ska läsas vid inloggning. Mer information om anspråkstyper finns i ClaimTypes.
Exempelappen skapar lokalanspråk (urn:google:locale) och bildanspråk (urn:google:picture) från nycklarna locale och picture i användardata från 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;
    };
});
I Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal.ExternalLoginModel.OnPostConfirmationAsync loggar en IdentityUser (ApplicationUser) in med SignInAsync i appen. Under inloggningsprocessen kan UserManager<TUser> lagra anspråk ApplicationUser för användardata som är tillgängliga från Principal.
I exempelappen OnPostConfirmationAsync (Account/ExternalLogin.cshtml.cs) upprättas språkvariant (urn:google:locale) och bild (urn:google:picture) anspråk för den inloggade ApplicationUser, inklusive ett anspråk för 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();
}
Som standard lagras en användares anspråk i autentiseringen cookie. Om autentiseringen cookie är för stor kan den orsaka att appen misslyckas eftersom:
- Webbläsaren identifierar att cookie rubriken är för lång.
- Den totala storleken på begäran är för stor.
Om en stor mängd användardata krävs för bearbetning av användarbegäranden:
- Begränsa antalet och storleken på användaranspråk för bearbetning av begäranden till endast det som appen kräver.
- Använd en anpassad ITicketStore för att lagra identiteter mellan begäranden i autentiseringsmellanprogrammets CookieSessionStore. Bevara stora mängder identitetsinformation på servern samtidigt som du bara skickar en liten sessionsidentifierarnyckel till klienten.
Spara åtkomsttoken
              SaveTokens definierar om åtkomst- och uppdateringstoken ska lagras i AuthenticationProperties efter en lyckad auktorisering. 
              SaveTokens är inställd false på som standard för att minska storleken på den slutliga autentiseringen cookie.
Exempelappen anger värdet av SaveTokens till true i 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;
    };
});
När OnPostConfirmationAsync körs lagrar du åtkomsttoken (ExternalLoginInfo.AuthenticationTokens) från den externa providern i ApplicationUser's AuthenticationProperties.
Exempelappen sparar åtkomsttoken i OnPostConfirmationAsync (ny användarregistrering) och OnGetCallbackAsync (tidigare registrerad användare) i 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();
}
Så här lägger du till ytterligare anpassade token
För att visa hur du lägger till en anpassad token som lagras som en del av SaveTokens lägger exempelappen till en AuthenticationToken med den aktuella DateTime för en AuthenticationToken.Name av 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;
    };
});
Skapa och lägga till anspråk
Ramverket innehåller vanliga åtgärder och tilläggsmetoder för att skapa och lägga till anspråk i samlingen. Mer information finns i ClaimActionCollectionMapExtensions och ClaimActionCollectionUniqueExtensions.
Användare kan definiera anpassade åtgärder genom att härleda från ClaimAction och implementera den abstrakta Run metoden.
Mer information finns i Microsoft.AspNetCore.Authentication.OAuth.Claims.
Borttagning av anspråksåtgärder och anspråk
ClaimActionCollection.Remove(String) tar bort alla anspråksåtgärder för angivna ClaimType från samlingen. ClaimActionCollectionMapExtensions.DeleteClaim(ClaimActionCollection, String) tar bort ett anspråk på angivet ClaimType från identiteten. DeleteClaim används främst med OpenID Connect (OIDC) för att ta bort protokollgenererade anspråk.
Exempel på apputdata
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
Vidarebefordra begärans information genom en proxy eller lastbalanserare
Om appen distribueras bakom en proxyserver eller lastbalanserare kan en del av den ursprungliga begärandeinformationen vidarebefordras till appen i begärandehuvuden. Den här informationen omfattar vanligtvis schemat för säker begäran (https), värd och klientens IP-adress. Appar läser inte automatiskt dessa begärandehuvuden för att identifiera och använda den ursprungliga begärandeinformationen.
Schemat används i länkgenerering som påverkar autentiseringsflödet med externa leverantörer. Att förlora det säkra schemat (https) resulterar i att appen genererar felaktiga osäkra omdirigerings-URL:er.
Använd middleware för vidarebefordrade rubriker för att göra den ursprungliga begärandeinformationen tillgänglig för appen vid behandling av förfrågningar.
Mer information finns i Konfigurera ASP.NET Core att fungera med proxyservrar och lastbalanserare.
Ytterligare resurser
- 
              dotnet/AspNetCore Engineering SocialSample-app: Den länkade exempelappen finns på GitHub-lagringsplatsensmainteknikgren dotnet/AspNetCore. Grenenmaininnehåller kod under aktiv utveckling för nästa version av ASP.NET Core. Om du vill se en version av exempelappen för en version av ASP.NET Core använder du listrutan Gren för att välja en versionsgren (till exempelrelease/{X.Y}).
ASP.NET Core