ios 12 台设备上的 Facebook 身份验证问题
Authentification problem with facebook on ios 12 devices
iphone 版本 12 的设备开始面临问题,facebook 身份验证停止工作,我找不到发生这种情况的任何原因,当您单击 facebook auth 按钮时出现问题,safari 只是重定向到facebook 并立即返回应用程序。
从代码来看,它触发了 HttpPost 方法 ExternalLogin,然后使用 HttpGet Login 返回页面。它适用于其他设备,但仅不适用于 iphones 版本 12。有人遇到过这个问题吗?另外,要注意 facebook api 版本是 2.7
这是我的代码:
<div class="form-group">
@{
if (ViewBag.Shemas != null)
{
<form asp-controller="Account" asp-action="ExternalLogin" method="post" class="form-horizontal">
<input id="ExtReturnUrl" data-val="true" name="ExtReturnUrl" asp-for="ExtReturnUrl" type="hidden" value="@Context.Request.Query["ReturnUrl"].ToString()">
<div>
<p>
@foreach (var provider in ViewBag.Shemas)
{
if (provider.DisplayName.Equals("Google"))
{
<button type="submit" class="btn btn-block text-uppercase btn-login-social btn-login-google" id="Google" name="provider"
value="Google" title="Log in using your Google account">
<i class="fa fa-google-plus-square"></i>Sign in via Google
</button>
}
else
{
<button type="submit" class="btn btn-block text-uppercase btn-login-social btn-login-facebook" id="Facebook" name="provider"
value="Facebook" title="Log in using your Facebook account">
<i class="fa fa-facebook-square"></i>Sign in via Facebook
</button>
}
}
</p>
</div>
</form>
}
}
@if (ViewData["Error"] != null)
{
<span class="field-validation-error">@ViewData["Error"]</span>
}
并且来自控制器:
[HttpGet("/account/login")]
[AllowAnonymous]
public async Task<IActionResult> Login(string error = "", string returnUrl = "", string errorStateKey = null, string errorStateMessage = null)
{
if (!string.IsNullOrEmpty(returnUrl))
{
Request.QueryString.Add("ReturnUrl", returnUrl);
}
if (HttpContext.User.Identity.IsAuthenticated)
{
if (string.IsNullOrEmpty(returnUrl)) return RedirectToAction("Index", "Home");
// Check if ReturnUrl is Wp Support Site Url>>
var isAbsoluteReturnUrl = IsAbsoluteValidUrl(returnUrl);
if (!isAbsoluteReturnUrl) return RedirectToAction("Index", "Home");
var uriWpReturn = new Uri(returnUrl);
var hostWpReturn = uriWpReturn.Host;
var uriWp = new Uri(_wpOptions.Value.Host);
var hostWp = uriWp.Host;
if (hostWpReturn == hostWp)
{
returnUrl = string.Format(_wpOptions.Value.AutologinUrl, CurrentSalesPeople.Var1, returnUrl);
return Redirect(returnUrl);
}
// << check if ReturnUrl is Wp Support Site
return RedirectToAction("Index", "Home");
}
if (!string.IsNullOrEmpty(error))
{
ViewData["Error"] = error;
}
if (errorStateKey != null && errorStateMessage != null)
{
ModelState.AddModelError(errorStateKey, errorStateMessage);
}
return await ShowLoginViewAsync(null);
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login(LoginModel model)
{
if (!ModelState.IsValid) return await ShowLoginViewAsync(model);
var validatePeople = await _authService.ValidateSalespeople(model.Login, model.Password);
if (!validatePeople.Success)
{
var errorStateKey = "Password";
var errorStateMessage = validatePeople.Message;
if (!string.IsNullOrEmpty(model.ReturnUrl))
{
return RedirectToAction("Login", new { returnUrl = model.ReturnUrl, errorStateKey = errorStateKey, errorStateMessage = errorStateMessage });
}
ModelState.AddModelError(errorStateKey, errorStateMessage);
return await ShowLoginViewAsync(model);
}
var salespeopleRole = await _roleService.GetRoleNameBySalespeopleId(validatePeople.Model.SId);
if (!salespeopleRole.Success)
{
var errorStateKey = "";
var errorStateMessage = validatePeople.Message;
if (!string.IsNullOrEmpty(model.ReturnUrl))
{
return RedirectToAction("Login", new { returnUrl = model.ReturnUrl, errorStateKey = errorStateKey, errorStateMessage = errorStateMessage });
}
ModelState.AddModelError(errorStateKey, errorStateMessage);
return await ShowLoginViewAsync(model);
}
var requestForDealerToolkit = await _dealerService.GetToolkitTypeBySalespeopleIddAsync(validatePeople.Model.SId);
if (!requestForDealerToolkit.Success)
{
var errorStateKey = "";
var errorStateMessage = "Problem with your toolkit subscription. Please tell a support about this error";
if (!string.IsNullOrEmpty(model.ReturnUrl))
{
return RedirectToAction("Login", new { returnUrl = model.ReturnUrl, errorStateKey = errorStateKey, errorStateMessage = errorStateMessage });
}
ModelState.AddModelError(errorStateKey, errorStateMessage);
return await ShowLoginViewAsync(model);
}
var getToolkit = GetToolkitTypeForSalespeople(validatePeople.Model.SubscriptionTypeId, requestForDealerToolkit.Model);
var roleName = new RoleHelper(getToolkit, salespeopleRole.Model == "Manager").RoleName;
if (roleName == "None")
{
var errorStateKey = "Password";
var errorStateMessage = "You don't have permissions";
if (!string.IsNullOrEmpty(model.ReturnUrl))
{
return RedirectToAction("Login", new { returnUrl = model.ReturnUrl, errorStateKey = errorStateKey, errorStateMessage = errorStateMessage });
}
ModelState.AddModelError(errorStateKey, errorStateMessage);
return await ShowLoginViewAsync(model);
}
//We clear session before each login to prevent caching changes in new app vesrions
//HttpContext.Session.Clear();
await Authenticate(validatePeople.Model, roleName, getToolkit);
return Redirect(model.ReturnUrl);
}
private async Task<IActionResult> ShowLoginViewAsync(object model)
{
var schemeProvider = HttpContext.RequestServices.GetRequiredService<IAuthenticationSchemeProvider>();
var shemas = await schemeProvider.GetAllSchemesAsync().FromAsyncResult(z => z.Where(x => x.Name.Equals("Facebook") || x.Name.Equals("Google")).ToList());
ViewBag.Shemas = shemas;
return View(model);
}
[HttpPost]
[AllowAnonymous]
public IActionResult ExternalLogin(string provider, string extReturnUrl = null)
{
if (User.Identity.IsAuthenticated)
{
return RedirectToAction("Index", "Home");
}
// Request a redirect to the external login provider.
var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = extReturnUrl });
var properties = new AuthenticationProperties { RedirectUri = redirectUrl };
return Challenge(properties, provider);
}
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl)
{
await HttpContext.SignOutAsync("Cookies");
var externalModel = ExternalLoginModel.FromIdentity(HttpContext);
if (externalModel != null)
{
var user = default(Salespeople);
if (externalModel.Provider.Equals("Google"))
{
var request = await _salespeopleGetService.GetSalespeopleByPredicateAsNoTrackingAsync(p => p.GoogleKey.Equals(externalModel.Id));
if (!request.Success)
return RedirectToAction("Login", new { error = "You do not have a connected google account", returnUrl = returnUrl });
user = request.Model;
}
else if (externalModel.Provider.Equals("Facebook"))
{
var request = await _salespeopleGetService.GetSalespeopleByPredicateAsNoTrackingAsync(p => p.FacebookKey.Equals(externalModel.Id));
if (!request.Success)
return RedirectToAction("Login", new { error = "You do not have a connected facebook account", returnUrl = returnUrl });
user = request.Model;
}
return await Login(new LoginModel() { Login = user?.Email, Password = user?.Pass, ReturnUrl = returnUrl });
}
return RedirectToAction(nameof(Login));
}
private async Task Authenticate(Salespeople user, string role, int toolkitType)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.SId.ToString(), ClaimValueTypes.String),
new Claim(ClaimsIdentity.DefaultNameClaimType, user.Email),
new Claim(ClaimsIdentity.DefaultRoleClaimType, role),
new Claim(ClaimTypes.UserData,toolkitType.ToString()),
new Claim(ClaimTypes.Expiration, "")
};
ClaimsIdentity id = new ClaimsIdentity(claims, "ApplicationCookie", ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType);
_cpl = new ClaimsPrincipal(id);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, _cpl, (await _authService.GetAuthenticationPropertiesWithPermissions(user)).Model);
}
在 iOS12 中,Apple 以其无限的智慧选择让 safari 的行为与其他浏览器不同。因此,如果您遵循 ASP.NET Core 中的最佳安全实践,它会破坏 safari 中的社交身份验证。
有一个关于它的公告here,解决方法如下所示:
services.ConfigureExternalCookie(options =>
{
// Other options
options.Cookie.SameSite = SameSiteMode.None;
});
services.ConfigureApplicationCookie(options =>
{
// Other options
options.Cookie.SameSite = SameSiteMode.None;
});
iphone 版本 12 的设备开始面临问题,facebook 身份验证停止工作,我找不到发生这种情况的任何原因,当您单击 facebook auth 按钮时出现问题,safari 只是重定向到facebook 并立即返回应用程序。
从代码来看,它触发了 HttpPost 方法 ExternalLogin,然后使用 HttpGet Login 返回页面。它适用于其他设备,但仅不适用于 iphones 版本 12。有人遇到过这个问题吗?另外,要注意 facebook api 版本是 2.7
这是我的代码:
<div class="form-group">
@{
if (ViewBag.Shemas != null)
{
<form asp-controller="Account" asp-action="ExternalLogin" method="post" class="form-horizontal">
<input id="ExtReturnUrl" data-val="true" name="ExtReturnUrl" asp-for="ExtReturnUrl" type="hidden" value="@Context.Request.Query["ReturnUrl"].ToString()">
<div>
<p>
@foreach (var provider in ViewBag.Shemas)
{
if (provider.DisplayName.Equals("Google"))
{
<button type="submit" class="btn btn-block text-uppercase btn-login-social btn-login-google" id="Google" name="provider"
value="Google" title="Log in using your Google account">
<i class="fa fa-google-plus-square"></i>Sign in via Google
</button>
}
else
{
<button type="submit" class="btn btn-block text-uppercase btn-login-social btn-login-facebook" id="Facebook" name="provider"
value="Facebook" title="Log in using your Facebook account">
<i class="fa fa-facebook-square"></i>Sign in via Facebook
</button>
}
}
</p>
</div>
</form>
}
}
@if (ViewData["Error"] != null)
{
<span class="field-validation-error">@ViewData["Error"]</span>
}
并且来自控制器:
[HttpGet("/account/login")]
[AllowAnonymous]
public async Task<IActionResult> Login(string error = "", string returnUrl = "", string errorStateKey = null, string errorStateMessage = null)
{
if (!string.IsNullOrEmpty(returnUrl))
{
Request.QueryString.Add("ReturnUrl", returnUrl);
}
if (HttpContext.User.Identity.IsAuthenticated)
{
if (string.IsNullOrEmpty(returnUrl)) return RedirectToAction("Index", "Home");
// Check if ReturnUrl is Wp Support Site Url>>
var isAbsoluteReturnUrl = IsAbsoluteValidUrl(returnUrl);
if (!isAbsoluteReturnUrl) return RedirectToAction("Index", "Home");
var uriWpReturn = new Uri(returnUrl);
var hostWpReturn = uriWpReturn.Host;
var uriWp = new Uri(_wpOptions.Value.Host);
var hostWp = uriWp.Host;
if (hostWpReturn == hostWp)
{
returnUrl = string.Format(_wpOptions.Value.AutologinUrl, CurrentSalesPeople.Var1, returnUrl);
return Redirect(returnUrl);
}
// << check if ReturnUrl is Wp Support Site
return RedirectToAction("Index", "Home");
}
if (!string.IsNullOrEmpty(error))
{
ViewData["Error"] = error;
}
if (errorStateKey != null && errorStateMessage != null)
{
ModelState.AddModelError(errorStateKey, errorStateMessage);
}
return await ShowLoginViewAsync(null);
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login(LoginModel model)
{
if (!ModelState.IsValid) return await ShowLoginViewAsync(model);
var validatePeople = await _authService.ValidateSalespeople(model.Login, model.Password);
if (!validatePeople.Success)
{
var errorStateKey = "Password";
var errorStateMessage = validatePeople.Message;
if (!string.IsNullOrEmpty(model.ReturnUrl))
{
return RedirectToAction("Login", new { returnUrl = model.ReturnUrl, errorStateKey = errorStateKey, errorStateMessage = errorStateMessage });
}
ModelState.AddModelError(errorStateKey, errorStateMessage);
return await ShowLoginViewAsync(model);
}
var salespeopleRole = await _roleService.GetRoleNameBySalespeopleId(validatePeople.Model.SId);
if (!salespeopleRole.Success)
{
var errorStateKey = "";
var errorStateMessage = validatePeople.Message;
if (!string.IsNullOrEmpty(model.ReturnUrl))
{
return RedirectToAction("Login", new { returnUrl = model.ReturnUrl, errorStateKey = errorStateKey, errorStateMessage = errorStateMessage });
}
ModelState.AddModelError(errorStateKey, errorStateMessage);
return await ShowLoginViewAsync(model);
}
var requestForDealerToolkit = await _dealerService.GetToolkitTypeBySalespeopleIddAsync(validatePeople.Model.SId);
if (!requestForDealerToolkit.Success)
{
var errorStateKey = "";
var errorStateMessage = "Problem with your toolkit subscription. Please tell a support about this error";
if (!string.IsNullOrEmpty(model.ReturnUrl))
{
return RedirectToAction("Login", new { returnUrl = model.ReturnUrl, errorStateKey = errorStateKey, errorStateMessage = errorStateMessage });
}
ModelState.AddModelError(errorStateKey, errorStateMessage);
return await ShowLoginViewAsync(model);
}
var getToolkit = GetToolkitTypeForSalespeople(validatePeople.Model.SubscriptionTypeId, requestForDealerToolkit.Model);
var roleName = new RoleHelper(getToolkit, salespeopleRole.Model == "Manager").RoleName;
if (roleName == "None")
{
var errorStateKey = "Password";
var errorStateMessage = "You don't have permissions";
if (!string.IsNullOrEmpty(model.ReturnUrl))
{
return RedirectToAction("Login", new { returnUrl = model.ReturnUrl, errorStateKey = errorStateKey, errorStateMessage = errorStateMessage });
}
ModelState.AddModelError(errorStateKey, errorStateMessage);
return await ShowLoginViewAsync(model);
}
//We clear session before each login to prevent caching changes in new app vesrions
//HttpContext.Session.Clear();
await Authenticate(validatePeople.Model, roleName, getToolkit);
return Redirect(model.ReturnUrl);
}
private async Task<IActionResult> ShowLoginViewAsync(object model)
{
var schemeProvider = HttpContext.RequestServices.GetRequiredService<IAuthenticationSchemeProvider>();
var shemas = await schemeProvider.GetAllSchemesAsync().FromAsyncResult(z => z.Where(x => x.Name.Equals("Facebook") || x.Name.Equals("Google")).ToList());
ViewBag.Shemas = shemas;
return View(model);
}
[HttpPost]
[AllowAnonymous]
public IActionResult ExternalLogin(string provider, string extReturnUrl = null)
{
if (User.Identity.IsAuthenticated)
{
return RedirectToAction("Index", "Home");
}
// Request a redirect to the external login provider.
var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = extReturnUrl });
var properties = new AuthenticationProperties { RedirectUri = redirectUrl };
return Challenge(properties, provider);
}
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl)
{
await HttpContext.SignOutAsync("Cookies");
var externalModel = ExternalLoginModel.FromIdentity(HttpContext);
if (externalModel != null)
{
var user = default(Salespeople);
if (externalModel.Provider.Equals("Google"))
{
var request = await _salespeopleGetService.GetSalespeopleByPredicateAsNoTrackingAsync(p => p.GoogleKey.Equals(externalModel.Id));
if (!request.Success)
return RedirectToAction("Login", new { error = "You do not have a connected google account", returnUrl = returnUrl });
user = request.Model;
}
else if (externalModel.Provider.Equals("Facebook"))
{
var request = await _salespeopleGetService.GetSalespeopleByPredicateAsNoTrackingAsync(p => p.FacebookKey.Equals(externalModel.Id));
if (!request.Success)
return RedirectToAction("Login", new { error = "You do not have a connected facebook account", returnUrl = returnUrl });
user = request.Model;
}
return await Login(new LoginModel() { Login = user?.Email, Password = user?.Pass, ReturnUrl = returnUrl });
}
return RedirectToAction(nameof(Login));
}
private async Task Authenticate(Salespeople user, string role, int toolkitType)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.SId.ToString(), ClaimValueTypes.String),
new Claim(ClaimsIdentity.DefaultNameClaimType, user.Email),
new Claim(ClaimsIdentity.DefaultRoleClaimType, role),
new Claim(ClaimTypes.UserData,toolkitType.ToString()),
new Claim(ClaimTypes.Expiration, "")
};
ClaimsIdentity id = new ClaimsIdentity(claims, "ApplicationCookie", ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType);
_cpl = new ClaimsPrincipal(id);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, _cpl, (await _authService.GetAuthenticationPropertiesWithPermissions(user)).Model);
}
在 iOS12 中,Apple 以其无限的智慧选择让 safari 的行为与其他浏览器不同。因此,如果您遵循 ASP.NET Core 中的最佳安全实践,它会破坏 safari 中的社交身份验证。
有一个关于它的公告here,解决方法如下所示:
services.ConfigureExternalCookie(options =>
{
// Other options
options.Cookie.SameSite = SameSiteMode.None;
});
services.ConfigureApplicationCookie(options =>
{
// Other options
options.Cookie.SameSite = SameSiteMode.None;
});