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:
- Facebook kimlik doğrulaması
- Google kimlik doğrulaması
- Microsoft kimlik doğrulaması
- Twitter kimlik doğrulaması
- Diğer kimlik doğrulama sağlayıcıları
- OpenIdConnect
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 |
---|---|
https://www.facebook.com/dialog/oauth |
|
profile , email , openid |
|
Microsoft | https://login.microsoftonline.com/common/oauth2/v2.0/authorize |
https://api.twitter.com/oauth/authenticate |
Örnek uygulamada, Google'ın profile
, email
ve 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 (OnPostConfirmationAsync
Account/ExternalLogin.cshtml.cs
), oturum açmış ApplicationUser
olan 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 ApplicationUser
AuthenticationProperties
depolayı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.cs
kaydeder:
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ı SaveTokens
olarak depolanan özel bir belirtecin nasıl ekleneceğini göstermek için örnek uygulama, bir AuthenticationToken.Name TicketCreated
iç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/Manage
iskelesini 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:
- Veritabanında depolanan identity talepler için kullanıcı üzerinde UserManager.ReplaceClaimAsync.
- Kullanıcı üzerinde SignInManager.RefreshSignInAsync , yeni bir kimlik doğrulaması cookieoluşturulmasını zorlamak için kullanılır.
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.
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:
- Facebook kimlik doğrulaması
- Google kimlik doğrulaması
- Microsoft kimlik doğrulaması
- Twitter kimlik doğrulaması
- Diğer kimlik doğrulama sağlayıcıları
- OpenIdConnect
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 |
---|---|
https://www.facebook.com/dialog/oauth |
|
profile , email , openid |
|
Microsoft | https://login.microsoftonline.com/common/oauth2/v2.0/authorize |
https://api.twitter.com/oauth/authenticate |
Örnek uygulamada, Google'ın profile
, email
ve 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 (OnPostConfirmationAsync
Account/ExternalLogin.cshtml.cs
), oturum açmış ApplicationUser
olan 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 ApplicationUser
AuthenticationProperties
depolayı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.cs
kaydeder:
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ı SaveTokens
olarak depolanan özel bir belirtecin nasıl ekleneceğini göstermek için örnek uygulama, bir AuthenticationToken.Name TicketCreated
iç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/Manage
iskelesini 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:
- Veritabanında depolanan identity talepler için kullanıcı üzerinde UserManager.ReplaceClaimAsync.
- Kullanıcı üzerinde SignInManager.RefreshSignInAsync , yeni bir kimlik doğrulaması cookieoluşturulmasını zorlamak için kullanılır.
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:
- Facebook kimlik doğrulaması
- Google kimlik doğrulaması
- Microsoft kimlik doğrulaması
- Twitter kimlik doğrulaması
- Diğer kimlik doğrulama sağlayıcıları
- OpenIdConnect
Ö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 |
---|---|
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 |
Ö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 (OnPostConfirmationAsync
Account/ExternalLogin.cshtml.cs
), oturum açmış ApplicationUser
olan 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 ApplicationUser
AuthenticationProperties
depolayı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.cs
kaydeder:
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ı SaveTokens
olarak depolanan özel bir belirtecin nasıl ekleneceğini göstermek için örnek uygulama, bir AuthenticationToken.Name TicketCreated
iç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}
).
ASP.NET Core