サービスWeb API 2 + Owin にアクセスし、外部ログインをサポートしようとしているモバイル アプリ (Xamarin + MvvmCross) を作成しています。以下では、外部認証を使用してユーザーを既に登録しており、モバイル アプリからユーザーをログインさせたいだけであると仮定します。
現在、主にこのASP.NET Web API 2 外部ログイン Facebook および Google in AngularJS appに従って Facebook ログインが機能していますが、何時間も経っても、Microsoft ログインに同じ戦略を実装する方法がわかりません。 Microsoft アクセス トークンを確認する方法を見つけます。
私の理解では、独自の API を呼び出す (たとえば、ユーザー データとオブジェクトを取得する) には、まず次の手順を実行して、外部アクセス トークンを独自のサービスからローカル アクセス トークンに交換する必要があります。
- 外部プロバイダーによる認証 (モバイル アプリ内)
- プロバイダー アクセス トークンをサーバーに送信する (Web Api 2)
- プロバイダでアクセス トークンを検証して、ユーザーが正当かどうかを判断します - ProviderKey (Facebook と Google の userId)を受け取るプロセスで
- 手順 3のプロバイダー名とユーザーID を使用して、ユーザーをローカルでログインします。
- 現在ログインしているユーザーのローカル アクセス トークンを生成します。
Facebook は現在、上記のプロセス全体を Web およびモバイル アプリで実行しています。Microsoft を使用すると、モバイル アプリで認証して Microsoft アクセス トークンを取得できますが、ステップ 3 で行き詰まり、サーバーでアクセス トークンを検証して、アプリで既に登録されているユーザーを識別するために使用できる有用な情報を受け取ります。
正しいユーザーを識別するために、AspNetUserLogins テーブルからログイン プロバイダーと ProviderKeyが必要です。
Microsoft アクセス トークンを受け取った後、トークンを検証し、一致するプロバイダー名とプロバイダー キーを確認するにはどうすればよいですか?
これは外部認証の一般的/標準的なアプローチですか? もしそうなら、もっと多くの情報があっただろうと思いました。すべての助けをいただければ幸いです。
追加情報
また、新しい Microsoft Graph Api (統合された office365) を使用して、通常の Microsoft アカウントと職場/学校アカウントの両方をサポートしようとしています。そのため、Microsoft/Organisation アカウントに応じて異なるプロバイダー キーがあるようです。
コード例
ステップ 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に一致させるために何を使用すればよいかまだわかりません
Graph Api 呼び出しで GET User を実行すると、Facebook や Google で使用される userId など、格納されている ProviderKey と一致する値がありません。Microsoft ProviderKey の値は、AAAAAAAAAAAAAAAAAAAAADWmHuzvvAQpO********9PM のようになります。
また、提案されているように、JWT Validation ライブラリを使用してアクセス トークンを検証することもできました。クレームを調べると、正しいLoginProviderを特定できます(良いスタートです) が、ProviderKey に格納されている値と一致する値はまだ見つかりません。
この値はユーザーと関係があるはずですが、直接一致するものはありません。Microsoft の OpenIdAuthentication で ProviderKey がどこから来たのか知っている人はいますか? 別の値から暗号化されていますか?
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);