ASP.NET Core'da dış sağlayıcılardan gelen ek talepleri ve belirteçleri kalıcı hale

ASP.NET Core uygulaması Facebook, Google, Microsoft ve Twitter gibi dış kimlik doğrulama sağlayıcılarından ek talepler ve belirteçler oluşturabilir. Her sağlayıcı, platformundaki kullanıcılar hakkında farklı bilgiler gösterir, ancak kullanıcı verilerini alma ve ek taleplere dönüştürme deseni aynıdır.

Önkoşullar

Uygulamada hangi dış kimlik doğrulama sağlayıcılarının desteklendiğine karar verin. Her sağlayıcı için uygulamayı kaydedin ve bir istemci kimliği ile istemci gizli dizisi alın. Daha fazla bilgi için bkz . ASP.NET Core'da Facebook ve Google kimlik doğrulaması. Örnek uygulama, Google kimlik doğrulama sağlayıcısını kullanır.

İstemci kimliğini ve istemci gizli dizisini ayarlama

OAuth kimlik doğrulama sağlayıcısı, istemci kimliği ve istemci gizli dizisi kullanarak bir uygulamayla güven ilişkisi kurar. İstemci kimliği ve istemci gizli anahtarı değerleri, uygulama sağlayıcıya kaydedildiğinde dış kimlik doğrulama sağlayıcısı tarafından uygulama için oluşturulur. Uygulamanın kullandığı her dış sağlayıcı, sağlayıcının istemci kimliği ve istemci gizli dizisiyle bağımsız olarak yapılandırılmalıdır. Daha fazla bilgi için, geçerli olan dış kimlik doğrulama sağlayıcısı konularına bakın:

Kimlik doğrulaması sağlayıcısından kimlik veya erişim belirtecinde gönderilen isteğe bağlı talepler genellikle sağlayıcının çevrimiçi portalında yapılandırılır. Örneğin, Microsoft Entra Id, uygulama kaydının Belirteç yapılandırması dikey penceresinde uygulamanın kimlik belirtecine isteğe bağlı talepler atamaya izin verir. Daha fazla bilgi için bkz. Nasıl yapılır: Uygulamanıza isteğe bağlı talepler sağlama (Azure belgeleri). Diğer sağlayıcılar için dış belge kümelerine başvurun.

Örnek uygulama, Google kimlik doğrulama sağlayıcısını, Google tarafından sağlanan bir istemci kimliği ve gizli dizi ile yapılandırıyor:

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.

Kimlik doğrulama kapsamını belirleme

öğesini belirterek sağlayıcıdan alınacak izinlerin listesini belirtin Scope. Yaygın dış sağlayıcıların kimlik doğrulama kapsamları aşağıdaki tabloda gösterilir.

Provider Kapsam
Facebook https://www.facebook.com/dialog/oauth
Google profile, email, openid
Microsoft https://login.microsoftonline.com/common/oauth2/v2.0/authorize
Twitter https://api.twitter.com/oauth/authenticate

Örnek uygulamada, Google'ın profile, emailve openid kapsamları üzerinde çağrıldığında AuthenticationBuilderAddGoogle çerçeve tarafından otomatik olarak eklenir. Uygulama ek kapsamlar gerektiriyorsa, bunları seçeneklere ekleyin. Aşağıdaki örnekte, kullanıcının doğum gününü almak için Google https://www.googleapis.com/auth/user.birthday.read kapsamı eklenir:

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

Kullanıcı veri anahtarlarını eşleme ve talep oluşturma

Sağlayıcı seçeneklerinde, uygulamanın identity oturum açmada okuması için dış sağlayıcının JSON kullanıcı verilerindeki her anahtar veya alt anahtar için bir MapJsonKey veya MapJsonSubKey belirtin. Talep türleri hakkında daha fazla bilgi için bkz ClaimTypes. .

Örnek uygulama, Google kullanıcı verilerindeki ve picture anahtarlarından locale yerel ayarurn:google:locale () ve resim (urn:google:picture) talepleri oluşturur:

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çinde Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal.ExternalLoginModel.OnPostConfirmationAsync, ile uygulamada SignInAsyncbir IdentityUser (ApplicationUser) oturum açtı. Oturum açma işlemi sırasında, UserManager<TUser> içinden Principalsağlanan kullanıcı verileri için bir ApplicationUser talep depolayabilir.

Örnek uygulamada (OnPostConfirmationAsyncAccount/ExternalLogin.cshtml.cs), oturum açmış ApplicationUserolan için yerel ayar (urn:google:locale) ve resim (urn:google:picture) taleplerini oluşturur; buna için GivenNamebir talep de dahildir:

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();
}

Varsayılan olarak, kullanıcının talepleri kimlik doğrulamasında cookiedepolanır. Kimlik doğrulaması cookie çok büyükse uygulamanın başarısız olmasına neden olabilir çünkü:

  • Tarayıcı üst bilginin çok uzun olduğunu cookie algılar.
  • İsteğin genel boyutu çok büyük.

Kullanıcı isteklerini işlemek için büyük miktarda kullanıcı verisi gerekiyorsa:

  • İstek işleme için kullanıcı taleplerinin sayısını ve boyutunu yalnızca uygulamanın gerektirdiğiyle sınırlayın.
  • Kimlik Doğrulama Ara Yazılımı'nın SessionStore Cookie istekler arasında depolaması identity için özel ITicketStore bir özellik kullanın. İstemciye yalnızca küçük bir oturum tanımlayıcı anahtarı gönderirken sunucudaki büyük miktarda identity bilgiyi koruyun.

Erişim belirtecini kaydetme

SaveTokens , başarılı bir yetkilendirmeden sonra içinde erişim ve yenileme belirteçlerinin depolanıp depolanmayacağını AuthenticationProperties tanımlar. SaveTokens, son kimlik doğrulamasının cookieboyutunu küçültmek için varsayılan olarak olarak ayarlanırfalse.

Örnek uygulama içindeki değerini SaveTokens olarak true GoogleOptionsayarlar:

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;
    };
});

YürütürkenOnPostConfirmationAsync, dış sağlayıcıdan erişim belirtecini (ExternalLoginInfo.AuthenticationTokens) içinde ApplicationUserAuthenticationPropertiesdepolayın.

Örnek uygulama erişim belirtecini içinde OnPostConfirmationAsync (yeni kullanıcı kaydı) ve OnGetCallbackAsync (daha önce kaydedilmiş kullanıcı) içinde Account/ExternalLogin.cshtml.cskaydeder:

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();
}

Not

Belirteçleri sunucu tarafı uygulamasının Razor bileşenlerine geçirme hakkında bilgi için bkz. Sunucu tarafı Blazor ASP.NET Core Blazor ek güvenlik senaryoları.

Ek özel belirteçler ekleme

bir parçası SaveTokensolarak depolanan özel bir belirtecin nasıl ekleneceğini göstermek için örnek uygulama, bir AuthenticationToken.Name TicketCreatediçin geçerli DateTime ile bir eklerAuthenticationToken:

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;
    };
});

Talep oluşturma ve ekleme

Çerçeve, koleksiyona talep oluşturmak ve eklemek için yaygın eylemler ve uzantı yöntemleri sağlar. Daha fazla bilgi için ve ClaimActionCollectionUniqueExtensionsbölümüne ClaimActionCollectionMapExtensions bakın.

Kullanıcılar, soyut Run yönteminden ClaimAction türeterek ve uygulayarak özel eylemler tanımlayabilir.

Daha fazla bilgi için bkz. Microsoft.AspNetCore.Authentication.OAuth.Claims.

Kullanıcı taleplerini ekleme ve güncelleştirme

Talepler, oturum açma sırasında değil, ilk kayıtta dış sağlayıcılardan kullanıcı veritabanına kopyalanır. Kullanıcı uygulamayı kullanmak üzere kaydoldıktan sonra uygulamada ek talepler etkinleştirilirse, yeni bir kimlik doğrulaması cookieoluşturmayı zorlamak için kullanıcı üzerinde SignInManager.RefreshSignInAsync öğesini çağırın.

Test kullanıcı hesaplarıyla çalışan Geliştirme ortamında kullanıcı hesabını silin ve yeniden oluşturun. Üretim sistemleri için uygulamaya eklenen yeni talepler kullanıcı hesaplarına doldurulabilir. sayfasını uygulamasındaki uygulamaya Areas/Pages/Identity/Account/Manageiskelesini yaptıktan ExternalLogin sonra dosyasına aşağıdaki kodu ExternalLoginModel ExternalLogin.cshtml.cs ekleyin.

Eklenen talepler sözlüğü ekleyin. Talep türlerini tutmak için sözlük anahtarlarını ve varsayılan değeri tutmak için değerleri kullanın. Sınıfın en üstüne aşağıdaki satırı ekleyin. Aşağıdaki örnekte, kullanıcının Google resmi için varsayılan değer olarak genel bir headshot görüntüsüyle bir talep eklendiği varsayılır:

private readonly IReadOnlyDictionary<string, string> _claimsToSync =
     new Dictionary<string, string>()
     {
             { "urn:google:picture", "https://localhost:5001/headshot.png" },
     };

yönteminin varsayılan kodunu OnGetCallbackAsync aşağıdaki kodla değiştirin. Kod, talep sözlüğünde döngü oluşturur. Talepler her kullanıcı için eklenir (yedeklenir) veya güncelleştirilir. Talepler eklendiğinde veya güncelleştirildiğinde, kullanıcı oturum açma işlemi, mevcut kimlik doğrulama özellikleri (AuthenticationProperties) korunarak kullanılarak SignInManager<TUser>yenilenir.

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();
    }
}

Kullanıcı oturum açtığında talepler değiştiğinde de benzer bir yaklaşım benimser ancak bir geri doldurma adımı gerekli değildir. Kullanıcının taleplerini güncelleştirmek için kullanıcı üzerinde aşağıdakileri çağırın:

Talep eylemlerini ve beyanlarını kaldırma

ClaimActionCollection.Remove(Dize), verilen ClaimType için tüm talep eylemlerini koleksiyondan kaldırır. ClaimActionCollectionMapExtensions.DeleteClaim(ClaimActionCollection, String), verilen ClaimType talebinden identitysiler. DeleteClaim, protokol tarafından oluşturulan talepleri kaldırmak için öncelikli olarak OpenID Connect (OIDC) ile birlikte kullanılır.

Örnek uygulama çıkışı

Örnek uygulamayı çalıştırın ve MyClaims bağlantısını seçin:

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

İstek bilgilerini ara sunucu veya yük dengeleyici ile iletme

Uygulama bir ara sunucunun veya yük dengeleyicinin arkasına dağıtılırsa, özgün istek bilgilerinden bazıları istek üst bilgilerinde uygulamaya iletilebilir. Bu bilgiler genellikle güvenli istek düzenini (https), ana bilgisayarı ve istemci IP adresini içerir. Uygulamalar, özgün istek bilgilerini bulmak ve kullanmak için bu istek üst bilgilerini otomatik olarak okumaz.

Düzen, dış sağlayıcılarla kimlik doğrulaması akışını etkileyen bağlantı oluşturmada kullanılır. Güvenli düzenin (https) kaybedilmesi, uygulamanın yanlış güvenli olmayan yeniden yönlendirme URL'leri oluşturmasını sağlar.

Özgün istek bilgilerini uygulamaya, istek işleme için kullanılabilir hale getirmek için İletilen Üstbilgiler Ara Yazılımını kullanın.

Daha fazla bilgi için bkz. ASP.NET Core'u ara sunucular ve yük dengeleyicilerle çalışacak şekilde yapılandırma.

Örnek kodu görüntüleme veya indirme (indirme)

ASP.NET Core uygulaması Facebook, Google, Microsoft ve Twitter gibi dış kimlik doğrulama sağlayıcılarından ek talepler ve belirteçler oluşturabilir. Her sağlayıcı, platformundaki kullanıcılar hakkında farklı bilgiler gösterir, ancak kullanıcı verilerini alma ve ek taleplere dönüştürme deseni aynıdır.

Örnek kodu görüntüleme veya indirme (indirme)

Önkoşullar

Uygulamada hangi dış kimlik doğrulama sağlayıcılarının desteklendiğine karar verin. Her sağlayıcı için uygulamayı kaydedin ve bir istemci kimliği ile istemci gizli dizisi alın. Daha fazla bilgi için bkz . ASP.NET Core'da Facebook ve Google kimlik doğrulaması. Örnek uygulama, Google kimlik doğrulama sağlayıcısını kullanır.

İstemci kimliğini ve istemci gizli dizisini ayarlama

OAuth kimlik doğrulama sağlayıcısı, istemci kimliği ve istemci gizli dizisi kullanarak bir uygulamayla güven ilişkisi kurar. İstemci kimliği ve istemci gizli anahtarı değerleri, uygulama sağlayıcıya kaydedildiğinde dış kimlik doğrulama sağlayıcısı tarafından uygulama için oluşturulur. Uygulamanın kullandığı her dış sağlayıcı, sağlayıcının istemci kimliği ve istemci gizli dizisiyle bağımsız olarak yapılandırılmalıdır. Daha fazla bilgi için senaryonuz için geçerli olan dış kimlik doğrulama sağlayıcısı konularına bakın:

Kimlik doğrulaması sağlayıcısından kimlik veya erişim belirtecinde gönderilen isteğe bağlı talepler genellikle sağlayıcının çevrimiçi portalında yapılandırılır. Örneğin, Microsoft Entra Id, uygulama kaydının Belirteç yapılandırma dikey penceresinde uygulamanın kimlik belirtecine isteğe bağlı beyanlar atamanıza izin verir. Daha fazla bilgi için bkz. Nasıl yapılır: Uygulamanıza isteğe bağlı talepler sağlama (Azure belgeleri). Diğer sağlayıcılar için dış belge kümelerine başvurun.

Örnek uygulama, Google kimlik doğrulama sağlayıcısını, Google tarafından sağlanan bir istemci kimliği ve gizli dizi ile yapılandırıyor:

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;
    };
});

Kimlik doğrulama kapsamını belirleme

öğesini belirterek sağlayıcıdan alınacak izinlerin listesini belirtin Scope. Yaygın dış sağlayıcıların kimlik doğrulama kapsamları aşağıdaki tabloda gösterilir.

Provider Kapsam
Facebook https://www.facebook.com/dialog/oauth
Google profile, email, openid
Microsoft https://login.microsoftonline.com/common/oauth2/v2.0/authorize
Twitter https://api.twitter.com/oauth/authenticate

Örnek uygulamada, Google'ın profile, emailve openid kapsamları üzerinde çağrıldığında AuthenticationBuilderAddGoogle çerçeve tarafından otomatik olarak eklenir. Uygulama ek kapsamlar gerektiriyorsa, bunları seçeneklere ekleyin. Aşağıdaki örnekte, kullanıcının doğum gününü almak için Google https://www.googleapis.com/auth/user.birthday.read kapsamı eklenir:

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

Kullanıcı veri anahtarlarını eşleme ve talep oluşturma

Sağlayıcının seçeneklerinde, uygulamanın identity oturum açmada okuması için dış sağlayıcının JSON kullanıcı verilerindeki her anahtar/alt anahtar için bir MapJsonKey veya MapJsonSubKey belirtin. Talep türleri hakkında daha fazla bilgi için bkz ClaimTypes. .

Örnek uygulama, Google kullanıcı verilerindeki ve picture anahtarlarından locale yerel ayarurn:google:locale () ve resim (urn:google:picture) talepleri oluşturur:

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çinde Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal.ExternalLoginModel.OnPostConfirmationAsync, ile uygulamada SignInAsyncbir IdentityUser (ApplicationUser) oturum açtı. Oturum açma işlemi sırasında, UserManager<TUser> içinden Principalsağlanan kullanıcı verileri için bir ApplicationUser talep depolayabilir.

Örnek uygulamada (OnPostConfirmationAsyncAccount/ExternalLogin.cshtml.cs), oturum açmış ApplicationUserolan için yerel ayar (urn:google:locale) ve resim (urn:google:picture) taleplerini oluşturur; buna için GivenNamebir talep de dahildir:

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();
}

Varsayılan olarak, kullanıcının talepleri kimlik doğrulamasında cookiedepolanır. Kimlik doğrulaması cookie çok büyükse uygulamanın başarısız olmasına neden olabilir çünkü:

  • Tarayıcı üst bilginin çok uzun olduğunu cookie algılar.
  • İsteğin genel boyutu çok büyük.

Kullanıcı isteklerini işlemek için büyük miktarda kullanıcı verisi gerekiyorsa:

  • İstek işleme için kullanıcı taleplerinin sayısını ve boyutunu yalnızca uygulamanın gerektirdiğiyle sınırlayın.
  • Kimlik Doğrulama Ara Yazılımı'nın SessionStore Cookie istekler arasında depolaması identity için özel ITicketStore bir özellik kullanın. İstemciye yalnızca küçük bir oturum tanımlayıcı anahtarı gönderirken sunucudaki büyük miktarda identity bilgiyi koruyun.

Erişim belirtecini kaydetme

SaveTokens , başarılı bir yetkilendirmeden sonra içinde erişim ve yenileme belirteçlerinin depolanıp depolanmayacağını AuthenticationProperties tanımlar. SaveTokens, son kimlik doğrulamasının cookieboyutunu küçültmek için varsayılan olarak olarak ayarlanırfalse.

Örnek uygulama içindeki değerini SaveTokens olarak true GoogleOptionsayarlar:

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;
    };
});

YürütürkenOnPostConfirmationAsync, dış sağlayıcıdan erişim belirtecini (ExternalLoginInfo.AuthenticationTokens) içinde ApplicationUserAuthenticationPropertiesdepolayın.

Örnek uygulama erişim belirtecini içinde OnPostConfirmationAsync (yeni kullanıcı kaydı) ve OnGetCallbackAsync (daha önce kaydedilmiş kullanıcı) içinde Account/ExternalLogin.cshtml.cskaydeder:

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();
}

Not

Belirteçleri sunucu tarafı uygulamasının Razor bileşenlerine geçirme hakkında bilgi için bkz. Sunucu tarafı Blazor ASP.NET Core Blazor ek güvenlik senaryoları.

Ek özel belirteçler ekleme

bir parçası SaveTokensolarak depolanan özel bir belirtecin nasıl ekleneceğini göstermek için örnek uygulama, bir AuthenticationToken.Name TicketCreatediçin geçerli DateTime ile bir eklerAuthenticationToken:

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;
    };
});

Talep oluşturma ve ekleme

Çerçeve, koleksiyona talep oluşturmak ve eklemek için yaygın eylemler ve uzantı yöntemleri sağlar. Daha fazla bilgi için ve ClaimActionCollectionUniqueExtensionsbölümüne ClaimActionCollectionMapExtensions bakın.

Kullanıcılar, soyut Run yönteminden ClaimAction türeterek ve uygulayarak özel eylemler tanımlayabilir.

Daha fazla bilgi için bkz. Microsoft.AspNetCore.Authentication.OAuth.Claims.

Kullanıcı taleplerini ekleme ve güncelleştirme

Talepler, oturum açma sırasında değil, ilk kayıtta dış sağlayıcılardan kullanıcı veritabanına kopyalanır. Kullanıcı uygulamayı kullanmak üzere kaydoldıktan sonra uygulamada ek talepler etkinleştirilirse, yeni bir kimlik doğrulaması cookieoluşturmayı zorlamak için kullanıcı üzerinde SignInManager.RefreshSignInAsync öğesini çağırın.

Test kullanıcı hesaplarıyla çalışan Geliştirme ortamında, kullanıcı hesabını silip yeniden oluşturabilirsiniz. Üretim sistemleri için uygulamaya eklenen yeni talepler kullanıcı hesaplarına doldurulabilir. sayfasını uygulamasındaki uygulamaya Areas/Pages/Identity/Account/Manageiskelesini yaptıktan ExternalLogin sonra dosyasına aşağıdaki kodu ExternalLoginModel ExternalLogin.cshtml.cs ekleyin.

Eklenen talepler sözlüğü ekleyin. Talep türlerini tutmak için sözlük anahtarlarını ve varsayılan değeri tutmak için değerleri kullanın. Sınıfın en üstüne aşağıdaki satırı ekleyin. Aşağıdaki örnekte, kullanıcının Google resmi için varsayılan değer olarak genel bir headshot görüntüsüyle bir talep eklendiği varsayılır:

private readonly IReadOnlyDictionary<string, string> _claimsToSync = 
    new Dictionary<string, string>()
    {
        { "urn:google:picture", "https://localhost:5001/headshot.png" },
    };

yönteminin varsayılan kodunu OnGetCallbackAsync aşağıdaki kodla değiştirin. Kod, talep sözlüğünde döngü oluşturur. Talepler her kullanıcı için eklenir (yedeklenir) veya güncelleştirilir. Talepler eklendiğinde veya güncelleştirildiğinde, kullanıcı oturum açma işlemi, mevcut kimlik doğrulama özellikleri (AuthenticationProperties) korunarak kullanılarak SignInManager<TUser>yenilenir.

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();
    }
}

Kullanıcı oturum açtığında talepler değiştiğinde de benzer bir yaklaşım benimser ancak bir geri doldurma adımı gerekli değildir. Kullanıcının taleplerini güncelleştirmek için kullanıcı üzerinde aşağıdakileri çağırın:

Talep eylemlerinin ve taleplerin kaldırılması

ClaimActionCollection.Remove(Dize), verilen ClaimType için tüm talep eylemlerini koleksiyondan kaldırır. ClaimActionCollectionMapExtensions.DeleteClaim(ClaimActionCollection, String), verilen ClaimType talebinden identitysiler. DeleteClaim, protokol tarafından oluşturulan talepleri kaldırmak için öncelikli olarak OpenID Connect (OIDC) ile birlikte kullanılır.

Örnek uygulama çıkışı

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

İstek bilgilerini ara sunucu veya yük dengeleyici ile iletme

Uygulama bir ara sunucunun veya yük dengeleyicinin arkasına dağıtılırsa, özgün istek bilgilerinden bazıları istek üst bilgilerinde uygulamaya iletilebilir. Bu bilgiler genellikle güvenli istek düzenini (https), ana bilgisayarı ve istemci IP adresini içerir. Uygulamalar, özgün istek bilgilerini bulmak ve kullanmak için bu istek üst bilgilerini otomatik olarak okumaz.

Düzen, dış sağlayıcılarla kimlik doğrulaması akışını etkileyen bağlantı oluşturmada kullanılır. Güvenli düzenin (https) kaybedilmesi, uygulamanın yanlış güvenli olmayan yeniden yönlendirme URL'leri oluşturmasını sağlar.

Özgün istek bilgilerini uygulamaya, istek işleme için kullanılabilir hale getirmek için İletilen Üstbilgiler Ara Yazılımını kullanın.

Daha fazla bilgi için bkz. ASP.NET Core'u ara sunucular ve yük dengeleyicilerle çalışacak şekilde yapılandırma.

ASP.NET Core uygulaması Facebook, Google, Microsoft ve Twitter gibi dış kimlik doğrulama sağlayıcılarından ek talepler ve belirteçler oluşturabilir. Her sağlayıcı, platformundaki kullanıcılar hakkında farklı bilgiler gösterir, ancak kullanıcı verilerini alma ve ek taleplere dönüştürme deseni aynıdır.

Örnek kodu görüntüleme veya indirme (indirme)

Önkoşullar

Uygulamada hangi dış kimlik doğrulama sağlayıcılarının desteklendiğine karar verin. Her sağlayıcı için uygulamayı kaydedin ve bir istemci kimliği ile istemci gizli dizisi alın. Daha fazla bilgi için bkz . ASP.NET Core'da Facebook ve Google kimlik doğrulaması. Örnek uygulama, Google kimlik doğrulama sağlayıcısını kullanır.

İstemci kimliğini ve istemci gizli dizisini ayarlama

OAuth kimlik doğrulama sağlayıcısı, istemci kimliği ve istemci gizli dizisi kullanarak bir uygulamayla güven ilişkisi kurar. İstemci kimliği ve istemci gizli anahtarı değerleri, uygulama sağlayıcıya kaydedildiğinde dış kimlik doğrulama sağlayıcısı tarafından uygulama için oluşturulur. Uygulamanın kullandığı her dış sağlayıcı, sağlayıcının istemci kimliği ve istemci gizli dizisiyle bağımsız olarak yapılandırılmalıdır. Daha fazla bilgi için senaryonuz için geçerli olan dış kimlik doğrulama sağlayıcısı konularına bakın:

Örnek uygulama, Google kimlik doğrulama sağlayıcısını, Google tarafından sağlanan bir istemci kimliği ve gizli dizi ile yapılandırıyor:

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;
    };
});

Kimlik doğrulama kapsamını belirleme

öğesini belirterek sağlayıcıdan alınacak izinlerin listesini belirtin Scope. Yaygın dış sağlayıcıların kimlik doğrulama kapsamları aşağıdaki tabloda gösterilir.

Provider Kapsam
Facebook https://www.facebook.com/dialog/oauth
Google https://www.googleapis.com/auth/userinfo.profile
Microsoft https://login.microsoftonline.com/common/oauth2/v2.0/authorize
Twitter https://api.twitter.com/oauth/authenticate

Örnek uygulamada, Google'ın userinfo.profile kapsamı üzerinde çağrıldığında AddGoogle AuthenticationBuilderçerçeve tarafından otomatik olarak eklenir. Uygulama ek kapsamlar gerektiriyorsa, bunları seçeneklere ekleyin. Aşağıdaki örnekte, kullanıcının doğum gününü almak için Google https://www.googleapis.com/auth/user.birthday.read kapsamı eklenir:

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

Kullanıcı veri anahtarlarını eşleme ve talep oluşturma

Sağlayıcının seçeneklerinde, uygulamanın identity oturum açmada okuması için dış sağlayıcının JSON kullanıcı verilerindeki her anahtar/alt anahtar için bir MapJsonKey veya MapJsonSubKey belirtin. Talep türleri hakkında daha fazla bilgi için bkz ClaimTypes. .

Örnek uygulama, Google kullanıcı verilerindeki ve picture anahtarlarından locale yerel ayarurn:google:locale () ve resim (urn:google:picture) talepleri oluşturur:

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çinde Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal.ExternalLoginModel.OnPostConfirmationAsync, ile uygulamada SignInAsyncbir IdentityUser (ApplicationUser) oturum açtı. Oturum açma işlemi sırasında, UserManager<TUser> içinden Principalsağlanan kullanıcı verileri için bir ApplicationUser talep depolayabilir.

Örnek uygulamada (OnPostConfirmationAsyncAccount/ExternalLogin.cshtml.cs), oturum açmış ApplicationUserolan için yerel ayar (urn:google:locale) ve resim (urn:google:picture) taleplerini oluşturur; buna için GivenNamebir talep de dahildir:

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();
}

Varsayılan olarak, kullanıcının talepleri kimlik doğrulamasında cookiedepolanır. Kimlik doğrulaması cookie çok büyükse uygulamanın başarısız olmasına neden olabilir çünkü:

  • Tarayıcı üst bilginin çok uzun olduğunu cookie algılar.
  • İsteğin genel boyutu çok büyük.

Kullanıcı isteklerini işlemek için büyük miktarda kullanıcı verisi gerekiyorsa:

  • İstek işleme için kullanıcı taleplerinin sayısını ve boyutunu yalnızca uygulamanın gerektirdiğiyle sınırlayın.
  • Kimlik Doğrulama Ara Yazılımı'nın SessionStore Cookie istekler arasında depolaması identity için özel ITicketStore bir özellik kullanın. İstemciye yalnızca küçük bir oturum tanımlayıcı anahtarı gönderirken sunucudaki büyük miktarda identity bilgiyi koruyun.

Erişim belirtecini kaydetme

SaveTokens , başarılı bir yetkilendirmeden sonra içinde erişim ve yenileme belirteçlerinin depolanıp depolanmayacağını AuthenticationProperties tanımlar. SaveTokens, son kimlik doğrulamasının cookieboyutunu küçültmek için varsayılan olarak olarak ayarlanırfalse.

Örnek uygulama içindeki değerini SaveTokens olarak true GoogleOptionsayarlar:

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;
    };
});

YürütürkenOnPostConfirmationAsync, dış sağlayıcıdan erişim belirtecini (ExternalLoginInfo.AuthenticationTokens) içinde ApplicationUserAuthenticationPropertiesdepolayın.

Örnek uygulama erişim belirtecini içinde OnPostConfirmationAsync (yeni kullanıcı kaydı) ve OnGetCallbackAsync (daha önce kaydedilmiş kullanıcı) içinde Account/ExternalLogin.cshtml.cskaydeder:

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();
}

Ek özel belirteçler ekleme

bir parçası SaveTokensolarak depolanan özel bir belirtecin nasıl ekleneceğini göstermek için örnek uygulama, bir AuthenticationToken.Name TicketCreatediçin geçerli DateTime ile bir eklerAuthenticationToken:

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;
    };
});

Talep oluşturma ve ekleme

Çerçeve, koleksiyona talep oluşturmak ve eklemek için yaygın eylemler ve uzantı yöntemleri sağlar. Daha fazla bilgi için ve ClaimActionCollectionUniqueExtensionsbölümüne ClaimActionCollectionMapExtensions bakın.

Kullanıcılar, soyut Run yönteminden ClaimAction türeterek ve uygulayarak özel eylemler tanımlayabilir.

Daha fazla bilgi için bkz. Microsoft.AspNetCore.Authentication.OAuth.Claims.

Talep eylemlerinin ve taleplerin kaldırılması

ClaimActionCollection.Remove(Dize), verilen ClaimType için tüm talep eylemlerini koleksiyondan kaldırır. ClaimActionCollectionMapExtensions.DeleteClaim(ClaimActionCollection, String), verilen ClaimType talebinden identitysiler. DeleteClaim, protokol tarafından oluşturulan talepleri kaldırmak için öncelikli olarak OpenID Connect (OIDC) ile birlikte kullanılır.

Örnek uygulama çıkışı

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

İstek bilgilerini ara sunucu veya yük dengeleyici ile iletme

Uygulama bir ara sunucunun veya yük dengeleyicinin arkasına dağıtılırsa, özgün istek bilgilerinden bazıları istek üst bilgilerinde uygulamaya iletilebilir. Bu bilgiler genellikle güvenli istek düzenini (https), ana bilgisayarı ve istemci IP adresini içerir. Uygulamalar, özgün istek bilgilerini bulmak ve kullanmak için bu istek üst bilgilerini otomatik olarak okumaz.

Düzen, dış sağlayıcılarla kimlik doğrulaması akışını etkileyen bağlantı oluşturmada kullanılır. Güvenli düzenin (https) kaybedilmesi, uygulamanın yanlış güvenli olmayan yeniden yönlendirme URL'leri oluşturmasını sağlar.

Özgün istek bilgilerini uygulamaya, istek işleme için kullanılabilir hale getirmek için İletilen Üstbilgiler Ara Yazılımını kullanın.

Daha fazla bilgi için bkz. ASP.NET Core'u ara sunucular ve yük dengeleyicilerle çalışacak şekilde yapılandırma.

Ek kaynaklar

  • dotnet/AspNetCore mühendislik SocialSample uygulaması: Bağlantılı örnek uygulama dotnet/AspNetCore GitHub deposunun main mühendislik dalındadır. Dal, main ASP.NET Core'un sonraki sürümü için etkin geliştirme kapsamındaki kodu içerir. ASP.NET Core'un yayımlanmış bir sürümü için örnek uygulamanın bir sürümünü görmek için Dal açılan listesini kullanarak bir yayın dalı seçin (örneğinrelease/{X.Y}).