我正在創建一個訪問我的服務的移動應用程序(Xamarin + MvvmCross)Web API 2 + Owin並試圖支持外部登錄。對於以下內容,假設我已經註冊了外部身份驗證用戶,並且只想從移動應用登錄用戶。使用ASP.NET WebApi 2移動應用程序的Microsoft Graph外部身份驗證
目前我的Facebook登錄工作主要是在這個ASP.NET Web API 2 external logins with Facebook and Google in AngularJS app之後工作的,但是在幾個小時後,我無法弄清楚如何實現微軟登錄的相同策略,因爲我找不到驗證Microsoft訪問令牌的方法。
我的理解是,爲了叫我自己的API(如讓我的用戶數據和對象),我需要先執行以下操作,從我自己的服務交換外部訪問令牌本地訪問令牌:
- 身份驗證與外部供應商(移動應用)
- 發送提供商訪問令牌到我的服務器(網頁API 2)
- 驗證與供應商的訪問令牌來確定用戶是否是合法的 - 在這個過程中接收ProviderKey(Facebook中的userId和g oogle)
- 登錄本地使用提供程序名稱和用戶ID從第3步
- 用戶生成在當前登錄用戶的本地訪問令牌。
Facebook目前通過上面列出的網絡和移動應用程序的整個過程。但是,通過Microsoft,我可以在移動應用程序上進行身份驗證,然後獲得Microsoft訪問令牌,但我堅持在步驟3中驗證服務器上的訪問令牌,以接收我可用於識別我的應用中已註冊的用戶的任何有用信息。
爲了識別正確的用戶,我需要AspNetUserLogins表中的登錄提供程序和ProviderKey。
收到Microsoft Access令牌後,如何驗證令牌並檢查匹配的提供者名稱和提供者密鑰?
這是外部認證的通用/標準方法嗎?如果是這樣的話,我認爲會有更多的信息。所有和任何幫助非常感謝。
額外的資訊
我也在努力,以支持正常的Microsoft帳戶以及使用新的Microsoft Graph API(統一office365)工作/學校賬戶。因此,根據微軟/組織帳戶的不同,看起來有不同的提供者密鑰。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);
}
謝謝
UPDATE
我試着實現了@dstrockis選項,但成功有限。在這兩種選擇,我仍然無法找出該用什麼來匹配到ProviderKey
使得圖形API調用當得到用戶的,不匹配的值存儲在ProviderKey,比如在Facebook中使用的帳戶及谷歌。 Microsoft ProviderKey值看起來像:AAAAAAAAAAAAAAAAAAAAADWmHuzvvAQpO ******* 9PM。
我還設法按照建議使用JWT驗證庫驗證訪問令牌。查看索賠,我現在可以確定正確的LoginProvider(好的開始),但是我仍然無法找到任何與ProviderKey中存儲的值相匹配的值。
此值必須與用戶有關,但不直接與任何內容匹配。有誰知道ProviderKey來自OpenIdAuthentication for Microsoft的何處。它是否從另一個值加密?
驗證令牌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????
}
驗證提供者 - 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);