前篇文章介紹過ASP.NET MVC 5內建Google、FB、Twitter、Microsoft Account外部帳號登入支援,只需設定API Key及API Secret即可啟用,十分方便。上回整完Google帳號,繼續處理Facebook登入。

一回生二回熟,第一步當然是到Facebook開發者網站申請API Key。在網頁按下「新増應用程式」:

類型選「網站」:

為應用程式取個名字:

填寫「顯示名稱」、「聯絡電子郵件」及「類別」:

下一步要填寫網站URL:

基本上這樣就算設定好了,「應用程式編號」跟「應用程式密碼」即程式所需的AppId及AppSecret。補充一點,Facebook API不像Google可以設多組URL,開發機、測試台、正式機URL不同需要多組AppId,可善用左側選單「建立測試應用程式」快速產生另一組AppId及AppSecret方便測試。

取得AppId及AppSecret後,在Startup.Auth.cs加入設定:(提醒:實務上appId及appSecret應由appSettings讀取,不宜寫死。)

            app.UseFacebookAuthentication(
               appId: "7360…1957",
               appSecret: "258d…8509");

實際測試,在MVC登入網頁按下Facebook按鈕,瀏覽器會導向Facebook網站,進行登入及要求授權:

待使用者同意授權後,會再導向MVC網站,此時ASP.NET已取得使用者的Facebook身分,拿到使用者的Email… 咦?並沒有!Email欄位是空的,跟Google帳號登入的情況不同。

依照ASP.NET會員管理需求,只要Facebook針對特定使用者提供唯一且固定的識別碼與會員資料連結就已夠用,Email交由使用者自由填寫不一定要與FB相同。但我有不同計劃,打算以Google、Facebook傳回Email作為權限管控依據,故還是得設法拿到Email。

只因為比一般應用情境多了一點要求,我離開寫兩行程式就搞定的高速公路,再次走上「抓Code看Code改Code」這條崎嶇蜿蜒的產業道路…(好討厭的感覺~)

使用Visual Studio偵錯發現,外部帳號登入成功後會使用AuthenticationManager.GetExternalLoginInfoAsync()方法取得使用者基本資料,DefaultUserName為使用者姓名、Email為電子郵件,另外有個Login屬性為UserLoginInfo型別,其中LoginProvider為"Facebook",另外有個ProviderKey,使用Facebook登入時,Email是空的。

而Google登入則可以抓到Email:

深入了解,得知Facebook從Graph API 2.0起改採App-Scoped User Id(中文翻成應用程式範圍編號)以強化個資保護,應用程式無法取得使用者真實User Id,改為首次登入應用程式時動態產生識別序號,該序號只對該應用程式有效,而應用程式只能取得有限的個人資訊,想取得更多資訊需經過審查。ProviderKey即是所謂的App-Scoped User Id。

依據官方文件,不需要審查可取得的資料範圍包含public_profile(姓名、連結、照片等)、email、user_friends。從CodePlex Katana專案拿到Microsoft.Owin.Security.Facebook的原始碼,發現Startup.Auth.cs UseFacebookAuthentication可以額外指定抓取email Scope:

            var opt = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()
            {
                AppId = System.Configuration.ConfigurationManager.AppSettings["FBApiKey"],
                AppSecret = System.Configuration.ConfigurationManager.AppSettings["FBApiSecret"]
            };
            opt.Scope.Add("email");
 
            app.UseFacebookAuthentication(opt);

很不幸,加入email Scope後還是抓不到Email資料。逼得我得架設深入Microsoft.Owin.Security.Facebook內部偵錯並重現問題的測試環境(過程很繁瑣磨人,細節就不說了),費了一番手腳找出問題點。在Microsoft.Owin.Security.Facebook.FacebookAuthenticationHandler.AuthenticationCoreAsync()有一段程式呼叫 httqs://graph.facebook.com/me?access_token=… 取回App-Scoped User Id、姓名,舊版Graph API傳回結果預設即包含Email資料,新版API則需加上fields參數才會回傳Email,故程式需修改如下:(感謝小朱補充:fields參數需求是2.4版加入的規格)

歷經N個小時努力,Facebook登入終於也能抓到Email了,萬歲~

2017-02-03 更新:網友Ted Chen 補充一個不用修改原始碼的做法,特此感謝


Comments

# by Ted Chen

另一個解法供你參考。(使用 FacebookBackChannelHandler) http://stackoverflow.com/questions/32059384/why-new-fb-api-2-4-returns-null-email-on-mvc-5-with-identity-and-oauth-2

# by Jeffrey

to Ted, 感謝分享,已加入本文。

Post a comment