C# 在ASP.NET WebApi 2中实现移动应用程序的外部身份验证
我正在尝试构建一个API(使用ASP.NET WebApi),该API将被学校项目的本地移动应用程序使用。(我不关心/开发移动应用程序,这个责任落在其他成员身上) 我现在需要实现基于令牌的Facebook登录。关于如何在基于浏览器的应用程序中实现这一功能,有很多教程可供参考(这非常简单,而且大部分都是内置的),但我不认为这会在本机应用程序中起作用。我不明白的是重定向是如何工作的 根据这一点,我的服务器不需要专门处理任何事情。我不明白这是怎么回事?Facebook的代币将如何处理 另外,我应该实现令牌处理的哪一部分,我真的找不到WebApi外部登录身份验证的好文档 无论如何,如果有人能告诉我发生的令牌交换的确切流程以及ASP.NET默认实现的内容,那将非常有用 另外,我最大的困惑是我不明白Facebook返回的代币将如何处理C# 在ASP.NET WebApi 2中实现移动应用程序的外部身份验证,c#,asp.net,facebook,asp.net-web-api,login,C#,Asp.net,Facebook,Asp.net Web Api,Login,我正在尝试构建一个API(使用ASP.NET WebApi),该API将被学校项目的本地移动应用程序使用。(我不关心/开发移动应用程序,这个责任落在其他成员身上) 我现在需要实现基于令牌的Facebook登录。关于如何在基于浏览器的应用程序中实现这一功能,有很多教程可供参考(这非常简单,而且大部分都是内置的),但我不认为这会在本机应用程序中起作用。我不明白的是重定向是如何工作的 根据这一点,我的服务器不需要专门处理任何事情。我不明白这是怎么回事?Facebook的代币将如何处理 另外,我应该实现
对于我正在开发的应用程序,我必须做几乎相同的事情。我也很难找到关于它的信息。似乎我发现的一切都接近我所需要的,但并不是真正的解决方案。最后,我从一堆不同的博客帖子、文章等中收集了一些零碎的东西,然后把它们放在一起,让它发挥作用 我记得你发布的两个链接“声明和基于令牌的身份验证”和“ASP.NET Web API 2在AngularJS应用程序中与Facebook和Google进行外部登录”都包含有用的信息 我不能给你一个全面的答案,因为我不记得我必须做的每件事,甚至不知道我当时正在做的每件事,但我可以给你一个大概的想法。你在正确的轨道上 基本上,我最终使用Facebook授予的令牌来确认他们登录了Facebook帐户,根据他们的Facebook用户ID创建了一个用户,并授予他们我自己的承载令牌,他们可以使用该令牌访问我的API 流程如下所示:
- Facebook给他们一个代币
- 该令牌使用Facebook的Graph API进行验证,该API返回用户信息
- 用户通过ASP.NET身份在数据库中创建,其Facebook用户ID作为密钥
- 该令牌使用Facebook的Graph API进行验证,该API返回用户信息
- 用户信息用于在数据库中查找用户,确认他们以前已注册
- ASP.NET标识用于为该用户生成新令牌
- 该令牌将返回给客户端
- 如果WebApi端点具有[Authorize]属性,则ASP.NET Identity将自动验证承载令牌,如果该令牌无效,则拒绝访问
- 服务器拥有facebook密钥,就像web登录一样
- 该应用程序要求可用的社交登录并显示按钮(我想你可以硬编码)
- 当按下按钮时,应用程序将打开浏览器并将URL设置为与指定社交登录相关的URL。然后,ASP.NET将浏览器重定向到facebook/google/任何带有相应挑战的位置
- 用户可能已登录或未登录,也可能已授予您的应用权限。在他授予权限后,facebook将重定向回提供的回调URL
- 此时,您可以从SignInManager获取外部登录信息,并检查该用户是否已存在以及是否应创建新帐户
- 最后生成一个令牌,并将浏览器重定向到放置令牌的URL。应用程序从URL获取令牌并关闭浏览器。使用令牌继续处理API请求
public async Task<IEnumerable<ExternalLoginDto>> GetExternalLogins(string returnUrl, bool generateState = false)
{
IEnumerable<AuthenticationScheme> loginProviders = await SignInManager.GetExternalAuthenticationSchemesAsync();
var logins = new List<ExternalLoginDto>();
string state;
if (generateState)
{
const int strengthInBits = 256;
state = RandomOAuthStateGenerator.Generate(strengthInBits);
}
else
{
state = null;
}
foreach (AuthenticationScheme authenticationScheme in loginProviders)
{
var routeValues = new
{
provider = authenticationScheme.Name,
response_type = "token",
client_id = Configuration["Jwt:Issuer"],
redirect_uri = $"{Request.Scheme}//{Request.Host}{returnUrl}",
state = state
};
var login = new ExternalLoginDto
{
Name = authenticationScheme.DisplayName,
Url = Url.RouteUrl("ExternalLogin", routeValues),
State = state
};
logins.Add(login);
}
return logins;
}
公共异步任务GetExternalLogins(字符串returnUrl,bool generateState=false)
{
IEnumerable loginProviders=Wait SignInManager.GetExternalAuthenticationSchemesAsync();
var logins=新列表();
字符串状态;
如果(不动产)
{
常数int-strengthInBits=256;
state=RandomOAuthStateGenerator.Generate(strengthInBits);
}
其他的
{
state=null;
}
foreach(loginProviders中的AuthenticationScheme AuthenticationScheme)
{
var routeValues=新
{
provider=authenticationScheme.Name,
[Authorize(AuthenticationSchemes = "Identity.External")]
[Route("ExternalLogin", Name = "ExternalLogin")]
public async Task<IActionResult> GetExternalLogin(string provider, string state = null, string client_id = null, string error = null)
{
if (error != null)
{
ThrowBadRequest(error);
}
if (!User.Identity.IsAuthenticated)
{
return new ChallengeResult(provider);
}
string providerKey = User.FindFirstValue(ClaimTypes.NameIdentifier);
var externalLoginInfo = new ExternalLoginInfo(User, User.Identity.AuthenticationType, providerKey, User.Identity.AuthenticationType);
if (externalLoginInfo.LoginProvider != provider)
{
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
return new ChallengeResult(provider);
}
var userLoginInfo = new UserLoginInfo(externalLoginInfo.LoginProvider, externalLoginInfo.ProviderKey, externalLoginInfo.ProviderDisplayName);
User user = await UserManager.FindByLoginAsync(externalLoginInfo.LoginProvider, externalLoginInfo.ProviderKey);
if (client_id != Configuration["Jwt:Issuer"])
{
return Redirect($"/#error=invalid_client_id_{client_id}");
}
if (user != null)
{
return await LoginWithLocalUser(user, state);
}
else
{
string email = null;
string firstName = null;
string lastName = null;
IEnumerable<Claim> claims = externalLoginInfo.Principal.Claims;
if (externalLoginInfo.LoginProvider == "Google")
{
email = claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
firstName = claims.FirstOrDefault(c => c.Type == ClaimTypes.GivenName)?.Value;
lastName = claims.FirstOrDefault(c => c.Type == ClaimTypes.Surname)?.Value;
}
else if (externalLoginInfo.LoginProvider == "Facebook")
{
email = claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
string[] nameParts = claims.First(c => c.Type == ClaimTypes.Name)?.Value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
firstName = nameParts?.First();
lastName = nameParts?.Last();
}
//some fallback just in case
firstName ??= externalLoginInfo.Principal.Identity.Name;
lastName ??= externalLoginInfo.Principal.Identity.Name;
user = new User
{
UserName = email,
Email = email,
FirstName = firstName,
LastName = lastName,
EmailConfirmed = true //if the user logs in with Facebook consider the e-mail confirmed
};
IdentityResult userCreationResult = await UserManager.CreateAsync(user);
if (userCreationResult.Succeeded)
{
userCreationResult = await UserManager.AddLoginAsync(user, userLoginInfo);
if (userCreationResult.Succeeded)
{
return await LoginWithLocalUser(user, state);
}
}
string identityErrrors = String.Join(" ", userCreationResult.Errors.Select(ie => ie.Description));
Logger.LogWarning($"Error registering user with external login. Email:{email}, Errors:" + Environment.NewLine + identityErrrors);
return Redirect($"/#error={identityErrrors}");
}
}
private async Task<RedirectResult> LoginWithLocalUser(User user, string state)
{
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
DateTime expirationDate = DateTime.UtcNow.AddDays(365);
string token = user.GenerateJwtToken(Configuration["Jwt:Key"], Configuration["Jwt:Issuer"], expirationDate);
return Redirect($"/#access_token={token}&token_type=bearer&expires_in={(int)(expirationDate - DateTime.UtcNow).TotalSeconds}&state={state}");
}