我正在创建一个移动应用程序(Xamarin + MvvmCross),它可以访问我的服务 Web API 2 + Owin ,并且我正在尝试支持外部登录。对于以下内容,假设我已经使用外部身份验证注册了用户,并且只想从移动应用程序登录用户。
目前我的Facebook登录工作主要遵循ASP.NET Web API 2 external logins with Facebook and Google in AngularJS app,但是经过几个小时后,我无法弄清楚如何为Microsoft登录实施相同的策略,因为我无法找到方法验证Microsoft访问令牌。
我的理解是,为了调用我自己的API(例如获取我的用户数据和对象),我需要首先执行以下操作,从我自己的服务中为本地访问令牌交换外部访问令牌:
Facebook目前正在通过网络和移动应用程序完成上述整个流程。使用Microsoft,我可以在移动应用程序上进行身份验证并获取Microsoft访问令牌,但是我坚持在第3步,验证我服务器上的访问令牌以接收我可用于识别我的应用程序中已注册用户的任何有用信息。
为了识别正确的用户,我需要AspNetUserLogins表中的 Login Provider和ProviderKey 。
收到Microsoft Access令牌后,如何验证令牌并检查匹配的提供商名称和提供商密钥?
这是外部身份验证的常用/标准方法吗?如果是这样的话,我认为会有更多关于它的信息。非常感谢所有和任何帮助。
额外信息
我还尝试使用新的Microsoft Graph Api(统一office365)支持普通的Microsoft帐户以及工作/学校帐户。因此,根据Microsoft / Organization帐户,似乎存在不同的提供者密钥,例如https://login.microsoftonline.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/v2.0
代码示例
步骤3 - 验证外部访问令牌以获取用户登录信息
Private async Task<ParsedExternalAccessToken> VerifyExternalAccessToken(string provider, string accessToken) {
ParsedExternalAccessToken parsedToken = null;
var verifyTokenEndPoint = "";
if (provider == Resources.Constants.FacebookProvider) {
//You can get it from here: https://developers.facebook.com/tools/accesstoken/
//More about debug_tokn here: https://stackoverflow.com/questions/16641083/how-does-one-get-the-app-access-token-for-debug-token-inspection-on-facebook
var appToken = WebConfigurationManager.AppSettings["fb_app_token"];
verifyTokenEndPoint = string.Format("https://graph.facebook.com/debug_token?input_token={0}&access_token={1}", accessToken, appToken);
} else if (provider == Resources.Constants.GoogleProvider) {
verifyTokenEndPoint = string.Format("https://www.googleapis.com/oauth2/v1/tokeninfo?access_token={0}", accessToken);
} else if (provider == "Microsoft" || provider == Resources.Constants.MicrosoftAccountProvider || provider == Resources.Constants.MicrooftSchoolOrWorkAccountProvider) {
//made up end point -> what is the real answer/solution?
verifyTokenEndPoint = string.Format("https://login.microsoftonline.com/common/v2.0/tokeninfo?access_token={0}", accessToken);
} else {
return null;
}
var client = new HttpClient();
var uri = new Uri(verifyTokenEndPoint);
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode) {
var content = await response.Content.ReadAsStringAsync();
dynamic jObj = (JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(content);
parsedToken = new ParsedExternalAccessToken();
if (provider == Resources.Constants.FacebookProvider) {
parsedToken.user_id = jObj["data"]["user_id"];
parsedToken.app_id = jObj["data"]["app_id"];
if (!string.Equals(Startup.facebookAuthOptions.AppId, parsedToken.app_id, StringComparison.OrdinalIgnoreCase)) {
return null;
}
} else if (provider == Resources.Constants.GoogleProvider) {
parsedToken.user_id = jObj["user_id"];
parsedToken.app_id = jObj["audience"];
if (!string.Equals(Startup.googleAuthOptions.ClientId, parsedToken.app_id, StringComparison.OrdinalIgnoreCase)) {
return null;
}
} else if (provider == Resources.Constants.MicrosoftAccountProvider || provider == Resources.Constants.MicrooftSchoolOrWorkAccountProvider) {
throw new NotImplementedException("Microsoft Access Token Validation not implemented");
}
}
return parsedToken;
}
第4步 - 获取本地访问令牌
public async Task<IHttpActionResult> ObtainLocalAccessToken(string provider, string externalAccessToken) {
if (string.IsNullOrWhiteSpace(provider) || string.IsNullOrWhiteSpace(externalAccessToken)) {
return ApiErrorResult(ServiceResults.ExternalAuth.Codes.ProviderOrExternalAccessTokenIsNotSent, ServiceResults.ExternalAuth.Messages.ProviderOrExternalAccessTokenIsNotSent);
}
var verifiedAccessToken = await VerifyExternalAccessToken(provider, externalAccessToken);
if (verifiedAccessToken == null) {
return ApiErrorResult(ServiceResults.ExternalAuth.Codes.InvalidProviderOrExternalAccessToken, ServiceResults.ExternalAuth.Messages.InvalidProviderOrExternalAccessToken);
}
var user = await _userManager.FindAsync(new UserLoginInfo(provider, verifiedAccessToken.user_id));
bool hasRegistered = user != null;
if (!hasRegistered) {
return ApiErrorResult(ServiceResults.ExternalAuth.Codes.ExternalUserIsNotRegistered, ServiceResults.ExternalAuth.Messages.ExternalUserIsNotRegistered);
}
//generate access token response
var accessTokenResponse = await GenerateLocalOauthToken(user);
return Ok(accessTokenResponse);
}
谢谢
更新
我已尝试同时实施两种@dstrockis选项。在这两个选项中,我仍然无法弄清楚要使用什么来匹配 ProviderKey
在对GET用户进行图表Api调用时,没有值与存储的ProviderKey匹配,例如Facebook和Google中使用的userId。 Microsoft ProviderKey值如下所示:AAAAAAAAAAAAAAAAAAAAADWmHuzvvAQpO ******* 9PM。
我还设法按照建议使用JWT验证库来验证访问令牌。仔细查看声明,我现在可以识别出正确的 LoginProvider (良好的开端),但我仍然无法找到与ProviderKey中存储的值匹配的任何值。
此值必须与用户有关,但不能直接匹配任何内容。有没有人知道ProviderKey来自Microsoft的OpenIdAuthentication。它是从另一个值加密的吗?
验证JWT令牌
private async Task ValidateMicrosoftToken(string token) {
string stsDiscoveryEndpoint = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration";
ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint);
OpenIdConnectConfiguration config = await configManager.GetConfigurationAsync();
TokenValidationParameters validationParameters = new TokenValidationParameters {
ValidateAudience = false,
ValidateIssuer = false,
IssuerSigningTokens = config.SigningTokens,
ValidateLifetime = false
};
JwtSecurityTokenHandler tokendHandler = new JwtSecurityTokenHandler();
SecurityToken jwt;
var result = tokendHandler.ValidateToken(token, validationParameters, out jwt);
//result contains claims with lots of values
//get provider key from claims????
}
Auth Provider - Startup.Auth
microsoftAccountAuthOptions = new OpenIdConnectAuthenticationOptions() {
Description = new AuthenticationDescription() { AuthenticationType = "OpenIdConnect", Caption = "Microsoft"},
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive,
ClientId = appId,
Authority = authority,
Scope = "openid user.read email " + string.Join(" ", scopes),
RedirectUri = redirectUri,
//PostLogoutRedirectUri = "/",
TokenValidationParameters = new TokenValidationParameters {
//.....
},
Notifications = new OpenIdConnectAuthenticationNotifications {
//.....
}
};
app.UseOpenIdConnectAuthentication(microsoftAccountAuthOptions);
答案 0 :(得分:0)
这种方法非常好。对于第3步,您确实有两个选择:
使用令牌验证库验证应用中的令牌。 Microsoft发布的Id_tokens是Json Web Tokens或JWT。有几个JWT验证库可供使用,如下所示:https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet。这些库通常要求您获取OpenID Connect元数据(包括令牌签名密钥)并将其传递给库以执行验证。这里有更多信息:https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-tokens/#idtokens。我通常推荐这种方法,因为它可以节省额外的API调用。
另一种方法是遵循Google&amp; amp; Facebook使用并调用一些Web API来为您验证令牌并返回一些用户信息,例如用户的ID。使用Microsoft Graph,我建议使用此API:https://graph.microsoft.io/en-us/docs/api-reference/v1.0/api/user_get。请务必在您的身份验证请求中使用正确的范围。
祝你好运!