development

ASP.NET MVC5 OWIN Facebook 인증이 갑자기 작동하지 않음

big-blog 2020. 11. 13. 23:47
반응형

ASP.NET MVC5 OWIN Facebook 인증이 갑자기 작동하지 않음


2017 업데이트!

원래 질문을 게시했을 때 겪었던 문제는 Facebook이 모든 사람에게 API 2.3 버전을 강제로 적용했을 때 변경 한 최근 변경 사항과 관련이 없습니다. 특정 문제에 대한 해결책은 아래 sammy34의 답변을 참조하십시오 . / oauth / access_token 엔드 포인트의 버전 2.3은 이제 양식 인코딩 값 대신 JSON을 반환합니다.

역사적인 이유로 내 원래 질문 / 문제는 다음과 같습니다.

Facebook 및 Google을 통한 인증에 대한 기본 제공 지원을 사용하는 MVC5 웹 응용 프로그램이 있습니다. 몇 달 전에이 앱을 빌드 할 때 다음 자습서를 따랐습니다. 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으로 리디렉션됩니다. "확인"을 클릭하면 사이트로 다시 리디렉션되지만 로그인하는 대신 로그인 화면이 표시됩니다.

디버그 모드에서이 프로세스를 진행했으며 위에서 언급 한 자습서에 따라 계정 컨트롤러에이 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 권한 대화 상자에서 "확인"을 클릭하면 Facebook이 다음 URL을 사용하여 응용 프로그램으로 다시 리디렉션된다는 것입니다.

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

이 URL은 실제 파일이 아니며 아마도이 OWIN 프레임 워크에 내장 된 컨트롤러 / 핸들러에 의해 처리 될 것입니다. 대부분의 경우 로그인을 시도하는 사용자에 대한 정보를 쿼리하기 위해 주어진 코드를 사용하여 Facebook에 다시 연결합니다. 이제 문제는 그렇게하는 대신 다음으로 리디렉션된다는 것입니다.

/Account/ExternalLoginCallback?error=access_denied

Facebook이하고있는 일이라고 확신합니다. 즉, 사용자 데이터를 제공하는 대신이 오류 메시지로 다시 리디렉션합니다.

이로 인해는 AuthenticationManager.GetExternalLoginInfoAsync();실패하고 항상 NULL을 반환합니다.

나는 완전히 아이디어가 없습니다. 우리가 아는 한, 우리는 우리 쪽에서 아무것도 변경하지 않았습니다.

새로운 페이스 북 앱을 만들려고했고 튜토리얼을 다시 시도했지만 항상 같은 문제가 있습니다.

어떤 아이디어라도 환영합니다!

최신 정보!

좋아, 이것은 나를 미치게 만든다! 이제 인증을 수행하는 데 필요한 단계를 수동으로 수행했으며 그렇게하면 모든 것이 잘 작동합니다. 왜 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 구현에 문제가 있습니다.


2017 년 4 월 22 일 업데이트 : 이제 Microsoft.Owin. * 패키지 버전 3.1.0을 사용할 수 있습니다. 2017 년 3 월 27 일부터 Facebook의 API가 변경된 후 문제가 발생하면 업데이트 된 NuGet 패키지를 먼저 사용해보세요. 제 경우에는 문제를 해결했습니다 (프로덕션 시스템에서 잘 작동 함).

원래 답변 :

제 경우에는 2017 년 3 월 28 일에 깨어나서 앱의 Facebook 인증이 갑자기 작동을 멈춘 것을 발견했습니다. 우리는 앱 코드에서 아무것도 변경하지 않았습니다.

Facebook은 2017 년 3 월 27 일에 그래프 API를 버전 2.2에서 2.3으로 "강제 업그레이드"한 것으로 나타났습니다. 이러한 API 버전의 차이점 중 하나는 Facebook 엔드 포인트 /oauth/access_token가 더 이상 양식 인코딩 된 형식으로 응답하지 않는다는 것입니다. 콘텐츠 본문이지만 대신 JSON을 사용합니다.

이제 Owin 미들웨어 protected override FacebookAuthenticationHandler.AuthenticateCoreAsync()에서 응답의 본문을 양식으로 access_token구문 분석 하고 이후 에 구문 분석 된 양식에서를 사용하는 메서드를 찾습니다 . 말할 필요도없이, 구문 분석 된 양식이 비어 있으므로이 access_token역시 비어 access_denied있어 체인 아래에서 오류가 발생합니다 .

이 문제를 빠르게 해결하기 위해 Facebook Oauth 응답에 대한 래퍼 클래스를 만들었습니다.

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에 배포하지 않았지만 :).

비슷한 문제로 오늘 깨어 난 누군가를 구할 수 있기를 바랍니다!

또한 누구든지 이것에 대한 더 깨끗한 해결책을 가지고 있다면, 알고 싶습니다!


어제이 문제를 발견했습니다. 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


좋아, 문제에 대한 해결책이 있습니다.

이것은 이전에 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 변수가 있는지 확인합니다.

이제 모든 것이 그랬던 것처럼 작동합니다!

나는 이것이 이전에 왜 그렇게 작동했는지 이해할 수 없습니다. 제가 생각해 낼 수있는 유일한 설명은 페이스 북이 그들의 입장에서 무언가를 바꿨다는 것입니다.


Google 인증에서도 이와 동일한 문제가 발생했습니다. 다음은 나를 위해 일했습니다 .Google OAuth 2.0 변경 및 3.0.0 RC 릴리스 용 Google 미들웨어 업데이트


마지막 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의 경우) 다른 솔루션을 찾아야 할 것입니다.


위의 솔루션은 저에게 효과적이지 않았습니다. 결국 세션과 관련된 것 같았다. 이전 호출에서 세션을 "깨우면"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를 사용하도록 세션을 설정할 때 코드가 변경 되었기 때문이라고 생각합니다.


이 문제도 있었지만 범위 설정으로 인한 것이 아닙니다. 그것을 알아내는 데 오랜 시간이 걸렸지 만 마침내 나를 단서로 만든 것은에서 다음을 설정하여 사용자 정의 로거를 설정하는 것이 었습니다 OwinStartup.Configuration(IAppBuilder app).

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

이것은 다음을 출력했습니다.

2014-05-31 21:14:48,508 [8] ERROR
Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware
[(null)] - 0x00000000 - Authentication failed
System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The remote name could not
be resolved: 'graph.facebook.com' at
System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar) --- End of inner exception stack trace --- at
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task) at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at
Microsoft.Owin.Security.Facebook.FacebookAuthenticationHandler.d__0.MoveNext()

Based on the above call stack I found that my Azure VM was unable to resolve graph.facebook.com. All I had to do to fix that was to run "ipconfig /registerdns" and I was all fixed...


I have been working on solution for three days. And I've just found it on 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);

And to get values

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;
}

Check you get an outside internet connection from your application. If not, fix your outside internet connection. My problem was I was using an EC2 AWS instance that suddenly stopped connecting to the internet. It took me a while to realize that was the problem.


This drove me insane. All was working until I deployed to my staging environment. I was using Microsoft.Owin.Security.Facebook version 3.0.1 from Nuget. Updated it to the prelease version 3.1.0 from Nuget and I no longer got the access denied error...


Even though i did everything what sammy34 said, it did not work for me. I was at the same point with HaukurHaf: When i make apirequest manually on browser it works perfect, but if i use my mvc app, GetExternalLoginInfoAsync() always returns null.

So i changed some rows on sammy34's codes like on this comment: https://stackoverflow.com/a/43148543/7776015

Replaced:

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;
}

Instead of:

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

And added this row into my FacebookAuthenticationOptions:

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

and now it works.(fields and that parameters optional)

Note: I did not update Microsoft.Owin.Security.Facebook

참고URL : https://stackoverflow.com/questions/22364442/asp-net-mvc5-owin-facebook-authentication-suddenly-not-working

반응형