---
# System prepended metadata

title: '[實作] .Net Framework WebAPI 實作 第三方登入(Google 為例)'
tags: [登入註冊, OAuth2.0, .Net Framework, 第三方, WebAPI]

---

# [實作] .Net Framework WebAPI 實作 第三方登入(Google 為例)

### :arrow_forward: **綱要**
1. **第三方登入 是什麼**
2. **OAuth2.0 架構**
3. **本文環境與套件設定**
4. **使用 第三方登入 - 程式碼實作**
5. **(補充) Google API 參考文件**
6. **(補充) 版本相依性錯誤**
---
### 1. 第三方登入 是什麼
    是一種帳戶登入作法，使用者透過登入第三方伺服器(如Google)，授權予以目標網站(第二方)相關使用者訊息
- **架構**：採用 OAuth2.0 架構，達成間接授權
- **作法**：網站分別取得使用者和第三方的認可，讓使用者可以用第三方帳戶進行目標網站的登入
- **優點**：使用者無須於目標網站建立專屬帳戶，亦即不用記住密碼
- **隱憂**：需要網站本身支援多元的第三方平台 (如 Google、Line 等)
---
### 2. OAuth2.0 架構
![image 來源](https://upload.wikimedia.org/wikipedia/commons/7/72/Abstract-flow.png)
[圖片來源：Wiki 開放授權(OAuth)](https://zh.wikipedia.org/zh-tw/%E5%BC%80%E6%94%BE%E6%8E%88%E6%9D%83)
#### (1) 原則：分別取得使用者和第三方的同意以及 Auth Token，就可以透過 Auth Token 取得使用者的特定資源
#### (2) 應用：當網站獲得 Google Token 後，已經間接認證使用者具備 Google 帳戶，如此就可以達到目標網站的登入功能
---
### 3. 本文環境與套件設定
#### (1) 環境
- IDE：VS-2022
- 框架：.Net Framework 4.7.2
- 專案：ASP NET Web API
#### (2) 套件
- Nuget 套件：**Google.Apis.Auth**(1.68.0版) 及 **Google.Apis.PeopleService.v1**(1.68.0.3359版)
    - Note：初次安裝上述套件，並運行程式，可能出現問題，請參考[下方區域](#(補充)6.-套件版本相依性錯誤)
#### (3) Google Console App 服務申請
- 請參考：https://www.tsg.com.tw/blog-detail2-162-0-google-login.htm
- 完成上述文章操作後，就會取得「**用戶端ID**」與「**用戶端密碼**」(憑證建立可以參考[下方區域](#(4)-於-Google-Console-中建立憑證與重新導向的路由)
- 記得要於 Google Console Project 中啟用 **Google People API** 使用權限(如下圖要啟用，完成後有綠色勾勾)
![image](https://hackmd.io/_uploads/ryP0YrvGC.png)
#### (4) 於 Google Console 中建立憑證與重新導向的路由
- 建立 新的憑證
![image](https://hackmd.io/_uploads/Hyk5YSPG0.png)
- 選擇 網頁應用程式
![image](https://hackmd.io/_uploads/ryVMqSDzC.png)
- 定義 相關路由
    - Note："已授權的JavaScript來源"，此為前端網站路由，如：`https://sun-live.vercel.app`
    - Note："已授權的重新導向 URI"，此為重新導向後的路由網址，如：`https://sun-live.vercel.app/auth/verify`
        - Note：重新導向後的路由網址，路由最後的 "/" 會有差別，需要特別注意
        - 如：`https://sun-live.vercel.app/auth/verify` 和 
        `https://sun-live.vercel.app/auth/verify/`，並不相同
![image](https://hackmd.io/_uploads/BJ1O9SvGC.png)

---
### 4. 實作 API 
![image](https://hackmd.io/_uploads/HJRw1XIGA.png)

#### (1) API 1：取得與建立 Google 帳號授權網址
```csharp
public IHttpActionResult API_1()
{
    // 一、建立 ClientSecrets 物件，其中包含 用戶端ID 與 用戶端密碼 物件
    var clientSecrets = new ClientSecrets
    {
        ClientId = WebConfigurationManager.AppSettings["GoogleId"].ToString(),
        ClientSecret = WebConfigurationManager.AppSettings["GoogleKey"].ToString()
    };

    // 二、建立 授權資料流 物件，其中夾帶 ClientSecrets 物件
    var flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
    {
        ClientSecrets = clientSecrets
    });
    
    // 三、建立 重新導向 路由網址 String(為架構圖中的第7步驟使用)，
    // Note：請再 Google Console 中加入以下網址，否則會失敗(請參考3. 本文環境與套件設定(4)步驟)
    string redirecturi = @"https://sun-live.vercel.app/auth/verify"; // 請自行修訂 
    
    // 四、創立 authorizationUrl 物件，其中夾帶 redirecturi String
    var authorizationUrl = flow.CreateAuthorizationCodeRequest(redirecturi);

    // 五、設定 使用者需要允許的授權範圍，
    // Note：有多個Scope就需要用 空白Space 進行間隔
    authorizationUrl.Scope = @"https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email";

    // 六、將 authorizationUrl 物件轉變成為 URI物件
    // Note：將URI物件中的 Scope 空白Space 替代成為 "%20"
    // Note：此 authUrlSpace 路由網址，其實是一串 Google 網址，QueryString 夾帶上面所設定的相關參數
    Uri authUrl = authorizationUrl.Build();
    string authUrlSpace = authUrl.ToString().Replace(" ", "%20");
    
    // 七、將 authUrlSpace 路由 String 送給前端
    // Note：請前端讓使用者重新導向到 authUrlSpace 路由(以利於完成架構圖的第7步驟)
    var result = new
    {
        statusCode = 200,
        status = "success",
        message = "取得成功",
        url = authUrlSpace,
    };
    return Content(HttpStatusCode.OK, result);
}

```
#### (2) API 2：驗證 Auth Code 並回傳結果
- 前端將會回傳一個物件，內部為一個 String 資料 **code** ，創立一個類別，接收該物件 (如下)
    - Note：使用者完成指定路由 (authUrlSpace) 的帳號選擇後，將會重新導向到 
       3. 本文環境與套件設定的[步驟(4)](#(4)-於-Google-Console-中建立憑證與重新導向的路由)路由內(即為 redirecturi String)
    - Note：Google 會重新導向後，QueryString 會夾帶名為 Code 的 Key 和對應的 Value
    - Note：請前端 擷取 Code Key 所對應的 Value，並透過 API-2 回傳給予後端
      
```csharp
public class AuthCode
{
    public string code { get; set; }
}
```



```csharp
public async Task<IHttpActionResult> API_2(AuthCode ac)
{
    // 一、建立 ClientSecrets 物件，其中包含 用戶端ID 與 用戶端密碼 物件
    var clientSecrets = new ClientSecrets
    {
        ClientId = WebConfigurationManager.AppSettings["GoogleId"].ToString(),
        ClientSecret = WebConfigurationManager.AppSettings["GoogleKey"].ToString()
    };

    // 二、建立 授權資料流 物件，其中夾帶 ClientSecrets 物件
    var flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
    {
        ClientSecrets = clientSecrets
    });

    // 三、建立 重新導向 路由網址 String
    // Note：請再 Google Console 中加入以下網址，否則會失敗(請參考3. 本文環境與套件設定(4)步驟)
    string redirecturi = @"https://sun-live.vercel.app/auth/verify"; // 請自行修訂 
    
    // 四、將前端傳入的 ac 物件 進行 URL Decode，取得 Decoding 的 code String
    string decodedCode = HttpUtility.UrlDecode(ac.code);
    
    // 五、使用 ExchangeCodeForToke 方法，取得 Google Auth TokenResponse 物件
    // Note：該方法內部的 "user" 值，可為任意值
    var tokenResponse = await flow.ExchangeCodeForTokenAsync("user", decodedCode, redirecturi, CancellationToken.None);

    // 六、使用 UserCredential()，取得 credential 物件
    var credential = new UserCredential(flow, "user", tokenResponse);

    // 七、建立 PeopleService 物件，並夾帶 credential 物件，以利取的使用者的 People API 特定資料
    var PeopleService = new PeopleServiceService(new BaseClientService.Initializer()
    {
     HttpClientInitializer = credential,
     ApplicationName = "FarmerAPI"
    });

    // 八、建立與呼叫 Google People API，取得 Google 回傳的 me 物件 
    // Note：Google API 使用方法可以參考下方 (補充)5.API 文件 
    GetRequest peopleRequest = PeopleService.People.Get("people/me");
    peopleRequest.PersonFields = "names,emailAddresses";
    Person me = peopleRequest.Execute();

    // 九、將 me 物件打開，儲存到 UserName 和 UserMail 中
    string UserName = me.Names[0].DisplayName;
    string UserMail = me.EmailAddresses[0].Value;
    
    // 上述已經取得 Google 所回傳的帳號名稱和 Mail Address，以下就可以開始進行登入或註冊的邏輯
    var UserInDB = db.User.Where(x=>x.Acount==UserMail).FirstorDefault();
    if(UserInDB!=null)
    {
        // 使用者帳號不存在於 DB 內，可進行註冊邏輯
        ...
        return Content(HttpStatusCode.OK, RegisterResult);
    }
    else
    {
        // 使用者帳號存在於 DB 內，可進行登入邏輯
        return Content(HttpStatusCode.OK, LoginResult)
    }
}
```
---
### (補充)5. Google People API 文件
透過 Google People API，除了可以取得使用者 names 和 emailAddresses 資料，還可以取得其他使用者資訊，可參考以下連結
#### (1) People API 總覽
- 官方文件：https://developers.google.com/people/api/rest/v1/people?hl=zh-tw
#### (2) people.get 方法參數 
- 官方文件：https://developers.google.com/people/api/rest/v1/people/get?hl=zh-tw
---
### (補充)6. 套件版本相依性錯誤
- 安裝完 Nuget 套件後，運行程式，可能會出現以下錯誤訊息：
![Error](https://hackmd.io/_uploads/SkLl13m7R.png)
- 解決方法：切換到上方圖片右下角的"錯誤清單"欄位，並雙點擊內部驚嘆號的錯誤訊息，就會跳出以下視窗：
![image](https://hackmd.io/_uploads/ByJcTiQQ0.png)
點選"是"，再重新運行，應該就可以解決套件版本相依性問題了
---


