首先要了解甚麼是 OAuth
Client-Server架構中,Client向Server出示使用者(Resource Owner)帳號密碼取得受保護資源(Protected Resource)。 若第三方也要取得Resources,則Resource Owner亦需將帳號密碼給予第三方,造成以下問題:
- 第三方取得帳號密碼,明碼儲存
- Server需支援密碼認證
- 第三方幾乎全能
- Resource Owner無法撤回第三方使用權,除非更改密碼
- 第三方被破解
- 引入認證層(authorization layer)
- 分離client(第三方)與resource owner
- 流程
- Resource存放於Resource Server上
- Client索取存取權限,存取Resource owner資源
- Client取得不同於Resource Owner所持有的驗證碼(credential)
- Client取得一個Access Token存取Protected Resources(非Resource Owner帳號密碼)
- Access Token 為字串,記載特定的存取範圍,時效等
- Access Token 來自 Authorization Server,取得前會得到Resource Owner許可
- Resource Owner - 可以授權別人存取Protected Resource,若是人類,即為用戶
- Resource Server - 存放Protected Resource 的伺服器,根據Access Token接受對Resource的請求
- Client 代表Resource Owner 存取Protected Resource(etg., 第三方)
- Authorization Server - 驗證Resource Owner並Resource Owner許可後,派發Access Token的伺服器
抽象流程:
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
Authorization Grant 代表 Resource Owner 授權 Client 可以去取得 Access Token 來存取 Protected Resource。
用以存取Protected Resource,為字串,代表特定scope,時效。
- Client 向 Resource Sercer 出示 Access Token
- 通常為 Ahtorization header 搭配 Access Token定義的auth schema(etg., Bearer)
- Resource Server 驗證 Access Token 並確認未過期、scope、及要存取的resource
在現今第三方網頁(etg,. Udemy/CodePen...)普遍應用OAuth獲得使用者授權,以取得使用者儲存在Google的資源(帳號信箱/雲端硬碟...),起始畫面一般如下:
往上溯源,我們需要在網頁中產生按鈕/超連結,以開啟上面的授權畫面,關鍵點在於我們需要一串url,告訴Google是誰(Client)以及要取得甚麼授權(Scope),我們可以在OAuth 2.0 Playground中產生取得特定授權的url(用以測試)。
例如我們(Client)需要使用者授權我們(ADHero)取得他的Adwords資料(Scope),url會長這樣:
https://accounts.google.com/signin/oauth/oauthchooseaccount?redirect_uri=https%3A%2F%2Fdevelopers.google.com%2Foauthplayground&prompt=consent&response_type=code&client_id=407408718192.apps.googleusercontent.com&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fadwords&access_type=offline&o2v=2&as=-RhcXCAnlB4ZP0sSuWxA2w&flowName=GeneralOAuthFlow
觀察這個url,我們初步了解是透過Query String來告訴Google是誰以及要取得甚麼授權,一旦格式正確,Google便會產生上面的網頁請使用者確定要不要授權給我們,一旦完成授權,我們便可以取得Access Token,透過API的方式取得用戶授權我們取用的資料。
- 在Console中開啟API總表
- 選擇要使用的Google專案
- 選擇要啟用的API(例:ADWords)
- 點選啟用
你需要先讓Google的OAuth2.0伺服器認識你的專案,步驟如下:
- 開啟憑證頁面
- 點擊建立憑證 => OAuth用戶端ID
- 選取網路應用程式
- 在已授權的重新導向 URI中輸入重導向url,這很重要,Google會在用戶完成授權時將Authorization Code傳遞到這個url,這意味你需要建立一個後端Service監聽Google的呼叫
- 建立完成後你可以下載組態檔(json),請不要讓他曝露在任何地方,格式大約如下
{
"web": {
"client_id": "專案下取得使用者授權的第三方ID",
"project_id": "專案ID",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_secret": "專案下取得使用者授權的第三方Secret",
"redirect_uris": [
"完成授權後Google會呼叫的端點(你的後端Service)"
]
}
}
步驟大致為下:
- 你的應用程式標示出他所需要的授權
- 你的應用程式將使用者引導到Google授權頁面
- 使用者決定是否授權給你的應用程式
- 你的應用程式知道使用者的決定
- 如果使用者授權,你的應用程式會獲得Access Token
透過url的Query String標示出你的應用程式需要使用者授權的項目。 下列為url所使用的參數(Query String)-僅列出較重要的
- client_id(必須)-你的Google專案下用以獲得使用者授權的第三方ID
- redirect_uri(必須)-使用者完成授權後將使用者導入的頁面(會包含Google給你的Authorization Code),必須是建立授權憑證時建立的重導向url之一
- response_type(必須)-Google是否會傳遞Authorization Code
- scope(必須)-你的應用程式將索取的授權,這組參數會決定使用者看到的授權畫面,可以用"+"號連結多個
- access_type(建議)-指出你的應用程式是否在使用者離線時能重新取得Access Token
- 若為online(預設),禁止重新取得Access Token
- 若為offline,Google會回傳refresh token用以重新取得Access Token
- 記得這些參數的值若為url,需要先進行Url Encode
依照上面的參數,可以組成url如下
https://accounts.google.com/o/oauth2/v2/auth?
scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly&
access_type=offline&
include_granted_scopes=true&
response_type=code&
state=state_parameter_passthrough_value&
redirect_uri=https%3A//oauth2.example.com/code&
client_id=client_id
使用者開啟上面的url,便會開啟授權畫面,一旦使用者同意,Google便會將Authorization Code傳遞到redirect_uri
Google OAuth2.0伺服器會調用你在url裡的redirect_uri回應授權結果。 若使用者同意授權,會應會將authorization code作為Query String傳遞,相反則包含error message,回應如下。
- 使用者同意
https://oauth2.example.com/auth?code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7
- 使用者不同意
https://oauth2.example.com/auth?error=access_denied
當你的Service收到Authorization Code後,加入以下參數以交換Access Token。
- client_id(必須)-你的Google專案下用以獲得使用者授權的第三方ID
- client_secret(必須)-你的Google專案下用以獲得使用者授權的第三方Secret
- code(必須)-收到的Authorization Code
- grant_type(必須)-指名授權類型,值為authorization_code
- redirect_uri(必須)-相同於步驟1的redirect_uri(用途不明)
Request如下
POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded
code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your_client_id&
client_secret=your_client_secret&
redirect_uri=https%3A//oauth2.example.com/code&
grant_type=authorization_code
Google最後會回應json,若步驟1的access_type為offline,則會包含refresh token。 json參數如下:
- access_token-千辛萬苦,終於得到
- expires_in-過期剩餘秒數
- refresh_token-用以重新獲得access_token
- scope-你所得到的授權
- token_type-token類型
json如下
{
"access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in": 3920,
"token_type": "Bearer",
"scope": "https://www.googleapis.com/auth/drive.metadata.readonly",
"refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}
由上步驟最終可以獲得Access Token,至於怎麼透過他呼叫API,則是下一步的研究。