web-dev-qa-db-ja.com

ASP.NET MVC5 OWIN Facebook認証が突然機能しない

2017を更新!

元の質問を投稿したときに抱えていた問題は、FacebookがAPIのバージョン2.3を強制したときにFacebookが行った最近の変更とは関係ありません。その特定の問題の解決策については、以下のsammy34の回答を参照してください。/oauth/access_tokenエンドポイントのバージョン2.3は、フォームでエンコードされた値ではなくJSONを返すようになりました

歴史的な理由から、ここに私の元の質問/問題があります:

FacebookとGoogleを介した認証の組み込みサポートを使用しているMVC5 Webアプリケーションがあります。数か月前にこのアプリを構築したとき、このチュートリアルに従いました: http://www.asp.net/mvc/tutorials/mvc-5/create-an-aspnet-mvc-5-app-with -facebook-and-google-oauth2-and-openid-sign-on そしてすべてがうまくいきました。

突然、Facebookの認証が機能しなくなりました。 Google認証は引き続き機能します。

問題の説明:Facebookを使用して接続するためにリンクをクリックすると、Facebookアプリにプロファイルへのアクセスを許可しない場合にプロンプ​​トが表示されるFacebookにリダイレクトされます。 [OK]をクリックすると、サイトにリダイレクトされますが、ログインする代わりに、ログイン画面が表示されます。

デバッグモードでこのプロセスを実行し、上記のチュートリアルに従ってアカウントコントローラーにこのActionResultを取得しました。

_// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
    var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
    if (loginInfo == null)
    {
        return RedirectToAction("Login");
    }
    ............
_

コードをステップ実行し、Facebookから戻るとき、loginInfoオブジェクトは常にNULLであり、これによりユーザーはログインにリダイレクトされます。

舞台裏で実際に何が起こっているのかを理解するために、Fiddlerをインストールし、HTTPトラフィックを監視しました。 Facebookの許可ダイアログで[OK]をクリックすると、Facebookは次のURLでアプリケーションにリダイレクトします。

_https://localhost/signin-facebook?code=<access-token>
_

このURLは実際のファイルではなく、おそらく私が推測しているこのOWINフレームワークに組み込まれているコントローラー/ハンドラーによって処理されます。ほとんどの場合、指定されたコードを使用してFacebookに接続し直し、ログインしようとしているユーザーに関する情報を照会しています。問題は、それを行う代わりに、次のようにリダイレクトされることです。

_/Account/ExternalLoginCallback?error=access_denied
_

これはFacebookがやっていることだと思います。つまり、ユーザーデータを提供する代わりに、このエラーメッセージで私たちをリダイレクトしています。

これにより、AuthenticationManager.GetExternalLoginInfoAsync();が失敗し、常にNULLが返されます。

私は完全にアイデアを失っています。私たちが知る限り、私たちは何も変えませんでした。

新しいFacebookアプリを作成しようとしました。チュートリアルをもう一度試してみましたが、いつも同じ問題があります。

どんなアイデアも歓迎します!

更新!

OK、これは私を狂気にさせています!認証を実行するために必要な手順を手動で実行しましたが、それを行うとすべてがうまく機能します。 MVC5 Owinのものを使用しているときに、なぜこれが機能しないのですか?

これは私がやったことです:

_    // Step 1 - Pasted this into a browser, this returns a code
    https://www.facebook.com/dialog/oauth?response_type=code&client_id=619359858118523&redirect_uri=https%3A%2F%2Flocalhost%2Fsignin-facebook&scope=&state=u9R1m4iRI6Td4yACEgO99ETQw9NAos06bZWilJxJrXRn1rh4KEQhfuEVAq52UPnUif-lEHgayyWrsrdlW6t3ghLD8iFGX5S2iUBHotyTqCCQ9lx2Nl091pHPIw1N0JV23sc4wYfOs2YU5smyw9MGhcEuinvTAEql2QhBowR62FfU6PY4lA6m8pD3odI5MwBYOMor3eMLu2qnpEk0GekbtTVWgQnKnH6t1UcC6KcNXYY

I was redirected back to localhost (which I had shut down at this point to avoid being redirected immediately away).  The URL I was redirected to is this:

https://localhost/signin-facebook?code=<code-received-removed-for-obvious-reasons>

Now, I grabbed the code I got and used it in the URL below:

// Step 2 - opened this URL in a browser, and successfully retrieved an access token
https://graph.facebook.com/oauth/access_token?client_id=619359858118523&redirect_uri=https://localhost/signin-facebook&client_secret=<client-secret>&code=<code-from-step-1>

// Step 3 - Now I'm able to query the facebook graph using the access token from step 2!

https://graph.facebook.com/me?access_token=<access-token-from-step-2>
_

エラーなし、すべてがうまくいきます!それでは、MVC5 Owinを使用しているときに、なぜこれが機能しないのでしょうか? OWinの実装には明らかに問題があります。

66
HaukurHaf

OK、問題の解決策があります。

これは、以前Startup.Auth.csファイルにあったコードです。

var x = new FacebookAuthenticationOptions();
            //x.Scope.Add("email");
            x.AppId = "1442725269277224";
            x.AppSecret = "<secret>";
            x.Provider = new FacebookAuthenticationProvider()
            {
                OnAuthenticated = async context =>
                {
                        //Get the access token from FB and store it in the database and
                    //use FacebookC# SDK to get more information about the user
                    context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken",context.AccessToken));
                    context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:name", context.Name));
                    context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:email", context.Email));
                }
            };
            x.SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie;
            app.UseFacebookAuthentication(x);

どのように

x.Scope.Add("email")

行はコメントアウトされていますが、それでも後でOnAuthenticatedハンドラーで電子メールを照会していますか?はい、そうです。何らかの理由で、これは数週間完璧に機能しました。

私の解決策は、x.Scope.Add( "email");のコメントを外すことです。 Facebookへの最初のリクエストでscope = email変数が存在することを確認する行。

これですべてが正常に機能するようになりました!

これが以前のように機能した理由がわかりません。私が思いつくことができる唯一の説明は、Facebookが彼らの終わりに何かを変えたということです。

9
HaukurHaf

2017年4月22日更新: Microsoft.Owin。*パッケージのバージョン3.1.0が利用可能になりました。 FacebookのAPIが2017年3月27日から変更された後に問題が発生した場合は、まず更新されたNuGetパッケージを試してください。私の場合、彼らは問題を解決しました(私たちの本番システムで問題なく動作します)。

元の回答:

私の場合、2017年3月28日に起きて、アプリのFacebook認証が突然機能しなくなったことを発見しました。アプリのコードは何も変更していません。

Facebookは、2017年3月27日にグラフAPIをバージョン2.2から2.3に「強制アップグレード」したことがわかりました。これらのバージョンのAPIの違いの1つは、Facebookエンドポイント_/oauth/access_token_が応答しなくなったことですフォームでエンコードされたコンテンツ本文を使用しますが、代わりにJSONを使用します。

さて、Owinミドルウェアには、応答の本文をフォームとして解析し、解析されたフォームから_access_token_を使用するメソッドprotected override FacebookAuthenticationHandler.AuthenticateCoreAsync()があります。言うまでもなく、解析されたフォームは空であるため、_access_token_も空であるため、チェーンのさらに下で_access_denied_エラーが発生します。

これをすばやく修正するために、Facebook Oauth responseのラッパークラスを作成しました

_public class FacebookOauthResponse
{
    public string access_token { get; set; }
    public string token_type { get; set; }
    public int expires_in { get; set; }
}
_

次に、OwinStartで、カスタムバックチャネルハンドラーを追加しました...

_        app.UseFacebookAuthentication(new FacebookAuthenticationOptions
        {
            AppId = "hidden",
            AppSecret = "hidden",
            BackchannelHttpHandler = new FacebookBackChannelHandler()
        });
_

...ここで、ハンドラーは次のように定義されます:

_public class FacebookBackChannelHandler : HttpClientHandler
{
    protected override async System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        var result = await base.SendAsync(request, cancellationToken);
        if (!request.RequestUri.AbsolutePath.Contains("access_token"))
            return result;

        // For the access token we need to now deal with the fact that the response is now in JSON format, not form values. Owin looks for form values.
        var content = await result.Content.ReadAsStringAsync();
        var facebookOauthResponse = JsonConvert.DeserializeObject<FacebookOauthResponse>(content);

        var outgoingQueryString = HttpUtility.ParseQueryString(string.Empty);
        outgoingQueryString.Add(nameof(facebookOauthResponse.access_token), facebookOauthResponse.access_token);
        outgoingQueryString.Add(nameof(facebookOauthResponse.expires_in), facebookOauthResponse.expires_in + string.Empty);
        outgoingQueryString.Add(nameof(facebookOauthResponse.token_type), facebookOauthResponse.token_type);
        var postdata = outgoingQueryString.ToString();

        var modifiedResult = new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new StringContent(postdata)
        };

        return modifiedResult;
    }
}
_

基本的に、ハンドラーは、Facebook JSON応答からの同等のフォームエンコードされた情報を含む新しいHttpResponseMessageを作成するだけです。このコードは一般的なJson.Netパッケージを使用していることに注意してください。

このカスタムハンドラーを使用すると、問題は解決されたように見えます(ただし、prodにはまだデプロイしていません:))。

同様の問題で今日目を覚ます誰かを救うことを願っています!

また、誰かがこれに対するよりクリーンなソリューションを持っているなら、私は知りたいです!

109
sammy34

昨日この問題に気づきました。 FacebookはMicrosoft.Owin.Security.Facebookバージョン3.0.1をサポートしなくなりました。私にとっては、バージョン3.1.0のインストールに成功しました。 3.1.0に更新するには、パッケージマネージャーコンソールでInstall-Package Microsoft.Owin.Security.Facebookコマンドを実行します。 https://www.nuget.org/packages/Microsoft.Owin.Security.Facebook

27
JayPi

Google認証でも同じ問題が発生しました。以下は私のために働いた: Googleへの変更OAuth 2.0および3.0.0 RCリリースのGoogleミドルウェアの更新

4
Maarten Docter

Facebookの最後のアップグレードは2015-02-09でした( https://www.nuget.org/packages/Microsoft.AspNet.WebPages.OAuth/

その時点でのAPIの最新バージョンはバージョン2.2でした。バージョン2.2は2017年3月25日に失効しましたが、これは偶然にも問題が始まったときです。 ( https://developers.facebook.com/docs/apps/changelog

FacebookはおそらくAPIを自動的にアップグレードし、MS OAUTHライブラリは新しい応答を解析できません。

tldr:Microsoft WebPages OAuthライブラリは古くなっています(少なくともFBの場合)、おそらく別のソリューションを見つける必要があります。

3
RedTornado

上記の解決策は私にとってはうまくいきませんでした。結局、それはセッションに関連しているように見えました。前の呼び出しでセッションを「ウェイクアップ」することにより、GetExternalLoginInfoAsync()からnullを返さなくなりました。

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public ActionResult ExternalLogin(string provider, string returnUrl)
    {
        Session["WAKEUP"] = "NOW!";
        // Request a redirect to the external login provider
        return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
    }

OPのように、サードパーティの認証が長時間正常に機能していましたが、突然停止しました。 AzureでRedis Cacheを使用するようにセッションをセットアップしたときにコードに加えられた変更が原因であると考えています。

2
Joel Gallagher

私もこの問題を抱えていましたが、スコープの設定が原因ではありませんでした。それを理解するのに長い時間がかかりましたが、最終的に私を決めたのは、OwinStartup.Configuration(IAppBuilder app)で以下を設定することによってカスタムロガーを設定することでした。

app.SetLoggerFactory(new LoggerFactory()); 
// Note: LoggerFactory is my own custom ILoggerFactory

これにより、以下が出力されました。

2014-05-31 21:14:48,508 [8]エラー
Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware
[(null)]-0x00000000-認証に失敗しました
System.Net.Http.HttpRequestException:リクエストの送信中にエラーが発生しました。 ---> System.Net.WebException:リモート名はできませんでした
解決される: 'graph.facebook.com'
System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar)---内部例外スタックトレースの終了--- at
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task)
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()at
Microsoft.Owin.Security.Facebook.FacebookAuthenticationHandler.d__0.MoveNext()

上記のコールスタックに基づいて、Azure VMはgraph.facebook.comを解決できませんでした。すべて修正...

2
jt000

私は3日間ソリューションに取り組んでいます。そして、私はちょうどgithubでそれを見つけました( https://github.com/aspnet/AspNetKatana/issues/38#issuecomment-290400987

var facebookOptions = new FacebookAuthenticationOptions()
{
    AppId = "xxxxx",
    AppSecret = "xxxxx",
};

// Set requested scope
facebookOptions.Scope.Add("email");
facebookOptions.Scope.Add("public_profile");

// Set requested fields
facebookOptions.Fields.Add("email");
facebookOptions.Fields.Add("first_name");
facebookOptions.Fields.Add("last_name");

facebookOptions.Provider = new FacebookAuthenticationProvider()
{
    OnAuthenticated = (context) =>
        {
            // Attach the access token if you need it later on for calls on behalf of the user
            context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));

            foreach (var claim in context.User)
            {
                //var claimType = string.Format("urn:facebook:{0}", claim.Key);
                var claimType = string.Format("{0}", claim.Key);
                string claimValue = claim.Value.ToString();

                    if (!context.Identity.HasClaim(claimType, claimValue))
                        context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaString", "Facebook"));
            }

            return Task.FromResult(0);
       }
};

app.UseFacebookAuthentication(facebookOptions);

そして値を取得する

var info = await AuthenticationManager.GetExternalLoginInfoAsync();

if (info != null)
{
    var firstName = info.ExternalIdentity.Claims.First(c => c.Type == "first_name").Value;
    var lastName = info.ExternalIdentity.Claims.First(c => c.Type == "last_name").Value;
}
1
affair

アプリケーションから外部インターネット接続を取得していることを確認してください。そうでない場合は、外部のインターネット接続を修正します。私の問題は、インターネットへの接続を突然停止したEC2 AWSインスタンスを使用していたことでした。それが問題だと気づくまでに時間がかかりました。

0
Ronen Festinger

これは私を狂気に駆り立てました。ステージング環境にデプロイするまで、すべてが機能していました。 NugetのMicrosoft.Owin.Security.Facebookバージョン3.0.1を使用していました。 Nugetからpreleaseバージョン3.1.0に更新しましたが、アクセス拒否エラーが表示されなくなりました...

0
Ewert

sammy34が言ったことをすべてやりましたが、私にはうまくいきませんでした。私はHaukurHafと同じポイントにいました:ブラウザで手動でapirequestを作成すると完璧に動作しますが、mvcアプリを使用すると、GetExternalLoginInfoAsync()は常にnullを返します。

だから私はこのコメントのようなsammy34のコードのいくつかの行を変更しました: https://stackoverflow.com/a/43148543/7776015

置換:

if (!request.RequestUri.AbsolutePath.Contains("/oauth"))
{
request.RequestUri = new Uri(request.RequestUri.AbsoluteUri.Replace("?access_token", "&access_token"));
}
var result = await base.SendAsync(request, cancellationToken);
if (!request.RequestUri.AbsolutePath.Contains("/oauth"))
{
return result;
}

の代わりに:

var result = await base.SendAsync(request, cancellationToken);
if (!request.RequestUri.AbsolutePath.Contains("access_token"))
return result;

そして、この行を私のFacebookAuthenticationOptionsに追加しました:

UserInformationEndpoint = "https://graph.facebook.com/v2.8/me?fields=id,name,email,first_name,last_name,picture"

動作するようになりました(フィールドとそのパラメーターはオプションです)

注:Microsoft.Owin.Security.Facebookは更新しませんでした

0
gld-R