Contents

Facebook登入-手動建立登入 OAuth 2

Facebook登入-手動建立登入 OAuth 2

開放授權(OAuth)是一個開放標準,很多大公司提供的第三方驗證,不外乎都是這樣的流程,應該也是目前最廣泛被採用的第三方應用存取授權機制標準,Facebook當然也不例外有手動建立登入流程 。

這邊就不介紹OAuth,如果有興趣,我在參考的部分,放置了一篇微軟官方的介紹,另外也有不少Blog具有相當詳細的介紹,之後看有沒有機會也來寫一篇OAuth…(實現率不高,不喜歡太多文字的說明,且別人寫得非常完整了…)

Note
記得要先到https://developers.facebook.com/apps/ 建立應用程式服務

實作

  1. 取得叫用「登入」對話方塊與設定重新導向網址

    1
    2
    3
    4
    5
    6
    7
    
    public IActionResult Index()
    {
        // 叫用「登入」對話方塊與設定重新導向網址
        State = Guid.NewGuid();
        ViewData["FacebookAuth"] = $"https://www.facebook.com/v8.0/dialog/oauth?client_id={_appId}&redirect_uri={RedirectUrl}&state={State}";
        return View();
    }
    
  2. 然後前台簡單建立一個連結按鈕

    1
    
    <a href="@ViewData["FacebookAuth"]" class="btn btn-primary" title="Log in your account">Facebook</a>
    
    /static/Facebook_手動建立登入_OAuth2_73ca87d1c88c4f5aabb40f1a2745ebd1/_2020-09-10_140514.png
  3. 點了之後一樣會出現登入介面(記得Facebook要先登出,才會顯示登入畫面)

    /static/Facebook_手動建立登入_OAuth2_73ca87d1c88c4f5aabb40f1a2745ebd1/_2020-09-10_140622.png
  4. 使用者輸入完後,會導回我們剛剛設定的回傳網址

    /static/Facebook_手動建立登入_OAuth2_73ca87d1c88c4f5aabb40f1a2745ebd1/_2020-09-10_140905.png

    這邊重點在於取得code(預設) 或 token,要取得哪項可以設置response_type,須注意這兩者取得權杖的方式不同,code是用使用代碼交換存取權杖,token則是要驗證,我直接使用預設

  5. 傳送使用代碼交換存取權杖

    /static/Facebook_手動建立登入_OAuth2_73ca87d1c88c4f5aabb40f1a2745ebd1/_2020-09-10_143418.png

    重點就在於這個權杖,有了權杖後,我們就可以使用Facebook Graph API,取得用戶資料,能取得哪些用戶資料,與版本有關,所以建議API加上版本號,我這邊只取最基本的ID、Name、Email

  6. 使用Facebook Graph API取得用戶資料,資料部分參考有連結

    /static/Facebook_手動建立登入_OAuth2_73ca87d1c88c4f5aabb40f1a2745ebd1/_2020-09-10_144710.png

    接下來就是一般的登入流程,看是否寫進資料庫、判斷是否第一次登入等

完整程式碼

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
public class LoginController : Controller
{
		// 這邊要改你申請的應用程式編號及密鑰
    private readonly string _appId = "{應用程式編號}";
    private readonly string _appSecret = "{應用程式密鑰}";

    private readonly IHttpClientFactory _clientFactory;

    public LoginController(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    private string RedirectUrl => "https://" + HttpContext.Request.Host.ToString() + "/Login/SignInFacebook";

    [TempData]
    public Guid State { get; set; }

    public IActionResult Index()
    {
        // 叫用「登入」對話方塊與設定重新導向網址
        State = Guid.NewGuid();
        ViewData["FacebookAuth"] = $"https://www.facebook.com/v8.0/dialog/oauth?client_id={_appId}&redirect_uri={RedirectUrl}&state={State}";
        return View();
    }

    public async Task<IActionResult> SignInFacebook(string code, Guid state, string error, string error_description)
    {
        // 有錯誤訊息(未授權等)、State遺失、State不相同、沒有code...
        if (!string.IsNullOrEmpty(error) || state == null || State != state || string.IsNullOrEmpty(code))
            return RedirectToAction(nameof(Index));

        // 使用代碼交換存取權杖
        var url = $"https://graph.facebook.com/v8.0/oauth/access_token?" +
            $"client_id={_appId}" +
            $"&redirect_uri={RedirectUrl}" +
            $"&client_secret={_appSecret}" +
            $"&code={code}";

        var client = _clientFactory.CreateClient();
        var response = await client.GetAsync(url);

        string responseContent;
        if (response.IsSuccessStatusCode)
            responseContent = await response.Content.ReadAsStringAsync();
        else
            return RedirectToAction(nameof(Index));

        var facebookLoginResource = JsonConvert.DeserializeObject<FacebookLoginResource>(responseContent);

        // 若需要取得用戶資料,則使用Facebook Graph API,並設置想取得的欄位,欄位請參考 用戶資料
        url = $"https://graph.facebook.com/v8.0/me?fields=id,name,email" +
              $"&access_token={facebookLoginResource.AccessToken}";
        response = await client.GetAsync(url);
        if (response.IsSuccessStatusCode)
        {
            responseContent = await response.Content.ReadAsStringAsync();
            var user = JsonConvert.DeserializeObject<FacebookUser>(responseContent);
        }
				// 略..
        return View();
    }
}

public class FacebookLoginResource
{
    [JsonProperty("access_token")]
    public string AccessToken { get; set; }

    [JsonProperty("token_type")]
    public string TokenType { get; set; }

    [JsonProperty("expires_in")]
    public string ExpiresIn { get; set; }
}

public class FacebookUser
{
    [JsonProperty("id")]
    public string Id { get; set; }

    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("email")]
    public string Email { get; set; }
}

參考

Facebook 手動建立登入範例

https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/

Facebook Graph Api

https://developers.facebook.com/docs/graph-api/using-graph-api/

Facebook 可取得用戶資料

https://developers.facebook.com/docs/graph-api/reference/user

Microsoft OAuth 2.0 授權碼流程

https://docs.microsoft.com/zh-tw/azure/active-directory/develop/v2-oauth2-auth-code-flow

結論

單看OAuth流程圖可能會覺得這個很複雜,但實際上流程多數都是相同的,程式碼也不複雜。

使用手動建立登入的程式碼比較多,但可控部分也相對增加了,OAuth程式碼是通用的,可以將這套流程封裝成介面,如果還有其他登入方式,是採用OAuth驗證標準,只要實作介面,這樣即使沒有官方沒有提供插件,也沒關係,可以優雅的統一作法。