1

サービスWeb API 2 + Owin にアクセスし、外部ログインをサポートしようとしているモバイル アプリ (Xamarin + MvvmCross) を作成しています。以下では、外部認証を使用してユーザーを既に登録しており、モバイル アプリからユーザーをログインさせたいだけであると仮定します。

現在、主にこのASP.NET Web API 2 外部ログイン Facebook および Google in AngularJS appに従って Facebook ログインが機能していますが、何時間も経っても、Microsoft ログインに同じ戦略を実装する方法がわかりません。 Microsoft アクセス トークンを確認する方法を見つけます。

私の理解では、独自の API を呼び出す (たとえば、ユーザー データとオブジェクトを取得する) には、まず次の手順を実行して、外部アクセス トークンを独自のサービスからローカル アクセス トークンに交換する必要があります。

  1. 外部プロバイダーによる認証 (モバイル アプリ内)
  2. プロバイダー アクセス トークンをサーバーに送信する (Web Api 2)
  3. プロバイダでアクセス トークンを検証して、ユーザーが正当かどうかを判断します - ProviderKey (Facebook と Google の userId)を受け取るプロセスで
  4. 手順 3のプロバイダー名ユーザーID を使用して、ユーザーをローカルでログインします。
  5. 現在ログインしているユーザーのローカル アクセス トークンを生成します。

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);
4

1 に答える 1