# [實作] .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 架構

[圖片來源: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** 使用權限(如下圖要啟用,完成後有綠色勾勾)

#### (4) 於 Google Console 中建立憑證與重新導向的路由
- 建立 新的憑證

- 選擇 網頁應用程式

- 定義 相關路由
- 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/`,並不相同

---
### 4. 實作 API

#### (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 套件後,運行程式,可能會出現以下錯誤訊息:

- 解決方法:切換到上方圖片右下角的"錯誤清單"欄位,並雙點擊內部驚嘆號的錯誤訊息,就會跳出以下視窗:

點選"是",再重新運行,應該就可以解決套件版本相依性問題了
---