[料理佳餚] 在 ASP.NET Core 整合 Facebook 做為網站的第三方登入

第三方登入系列文章的第二篇,在 ASP.NET Core 整合 Facebook 做第三方登入,Facebook 的使用者相當多,用 Facebook 來做第三方登入的普及率不是第一也有第二,算是相當廣泛,有做第三方登入的網站,大都有支援 Facebook,這篇文章記錄了整合的過程。

其他的第三方登入解決方案可以參考下面的連結:

建立應用程式

所有走 OAuth 2.0 授權機制整合第三方登入的起手式,一定是讓授權伺服器認識我們的應用程式,Facebook 也不例外,我們到 https://developers.facebook.com/apps 這個網頁「建立應用程式」。

選擇應用程式的「類型」,這裡就根據我們的需要來做選擇,因為是針對開發用的,所以我選擇「」。

提供應用程式的「基本資訊」,應用程式名稱是必填的,其他欄位就按照我們的需要來填寫。

如果我們的應用程式還在開發中,而且 Redirect URI 的 Host 是 localhost,我們本身也是應用程式的擁有者的話,基本上到這邊應用程式算是建立完成了,將來要開放上線的時候至少還得接續做這兩件事:

  1. 生出一個對外的 HTTPS Redirect URI
  2. 取得 public_profile 的進階存取權限,以獲得使用者的 id、name、picture。

準備 Redirect URI

接下來進入程式撰寫的環節,我們不用 Facebook 提供的 JavaScript SDK,而是手動建立登入流程,這樣可以避免讓 Access Token 曝露在瀏覽器端。

使用者透過授權網址授權之後,我們就能拿到 code,拿 code 就能去換 Access Token 回來,而換 Access Token 的網址及參數如下:

  1. Access Token URL:https://graph.facebook.com/oauth/access_token
  2. grant_type:填入 "authorization_code"
  3. code:即我們拿到的 code
  4. redirect_uri:即 Redirect URI
  5. client_id:即應用程式編號
  6. client_secret:即應用程式密鑰

應用程式編號及應用程式密鑰,我們可以在應用程式「設定」的「基本資料」裡面找到。

Facebook 傳給我們 Redirect URI 的資料就只有 Access Token,所以我們還需要拿著 Access Token 再去跟 Facebook 要使用者授權的資料,我這邊只有要 idnamepicture,而要資料的網址是 https://graph.facebook.com/me?fields=id,name,picture{url},下面是整個 Redirect URI 的程式碼:

[HttpGet("callback")]
public async Task<IActionResult> Callback()
{
    if (!this.Request.Query.TryGetValue("code", out var code))
    {
        return this.StatusCode(400);
    }

    var accessToken = await this.ExchangeAccessToken(code);

    if (accessToken == null)
    {
        return this.StatusCode(400);
    }

    var userProfile = await this.GetUserProfile(accessToken);

    // TODO: Save AccessToken and UserProfile

    // TODO: User Login

    return this.Redirect("/");
}

private async Task<string> ExchangeAccessToken(string code)
{
    var client = this.httpClientFactory.CreateClient();

    var request = new HttpRequestMessage(HttpMethod.Post, "AccessTokenUrl");

    request.Content = new FormUrlEncodedContent(
        new Dictionary<string, string>
        {
            ["grant_type"] = "authorization_code",
            ["code"] = code,
            ["redirect_uri"] = "Redirect URI",
            ["client_id"] = "ClientId",
            ["client_secret"] = "ClientSecret"
        });

    var response = await client.SendAsync(request);

    if (response.StatusCode != HttpStatusCode.OK) return null;

    var content = await response.Content.ReadAsStringAsync();

    var result = JsonNode.Parse(content);

    return result["access_token"].GetValue<string>();
}

private async Task<string> GetUserProfile(string accessToken)
{
    var client = this.httpClientFactory.CreateClient();

    var request = new HttpRequestMessage(HttpMethod.Get, "UserInfoUrl");

    request.Headers.Authorization = AuthenticationHeaderValue.Parse($"Bearer {accessToken}");

    var response = await client.SendAsync(request);

    var result = await response.Content.ReadAsStringAsync();

    return result;
}

授權網址

最後我們要組合出授權網址,使用者點擊這個授權網址即開始進入登入流程,Facebook 的授權網址是 https://www.facebook.com/dialog/oauth,所需的參數為:

  1. response_type:填入 "code"
  2. client_id:即應用程式編號
  3. redirect_uri:即 Redirect URI
  4. state:隨機產生的一段唯一的字串,主要是可以用來避免 CSRF(Cross Site Request Forgery)
  5. scope:想請使用者授權給我們的資料範圍

我就依據我的需要,組合出下面這個網址:

https://www.facebook.com/dialog/oauth?client_id=xxx&redirect_uri=http%3A%2F%2Flocalhost%3A5101%2Ffacebook%2Flogin%2Fcallback&state=12345abcde&response_type=code&scope=public_profile

整個登入的流程如下圖:

我們點擊授權網址,登入 Facebook 之後,就會看到授權的頁面,授權成功後,沒意外的話就能登入成功了,而授權畫面會有一個紅色驚嘆號,是因為我的應用程式還沒通過審查,我是使用應用程式擁有者的身份才有辦法進到這個授權畫面。

以上,以手動建立登入流程的方式,整合 Facebook 第三方登入,分享給各位朋友,希望對大家有起到一點幫助。

相關資源

C# 指南
ASP.NET 教學
ASP.NET MVC 指引
Azure SQL Database 教學
SQL Server 教學
Xamarin.Forms 教學