上一篇結尾提到,LDAP 與 Kerberos 撐起了企業內部「身份資料存哪裡、怎麼查」跟「怎麼證明我是這個人」兩個問題,但這兩套協定都是企業內部網路的產物,沒辦法很好地解決「第三方應用程式要怎麼在使用者不透露密碼的前提下,存取另一個服務上的資料」這個 Web 時代才冒出來的新問題——這正是 Day04 提過的 OAuth 想解決的事。這篇要先回到 OAuth 最早的版本 OAuth 1.0:它是怎麼被設計出來的、實際運作起來是什麼樣子、又面臨了哪些限制與挑戰,最後才讓整個社群決定重新設計出 OAuth 2.0。
今天內容涵蓋:
OAuth 的故事始於 2006 年 11 月,當時 Twitter 跟 Ma.gnolia(一個社群書籤服務)都想讓使用者安全地把第三方應用程式串接到自己的帳號,但當時還沒有一套開放標準能解決這個問題——這個缺口促成了一群開發者開始討論解法。
2007 年 4 月,一個小型的 OAuth 討論社群成立,開始草擬這套開放協定;同年 7 月完成初版規格。2007 年 10 月 3 日,OAuth Core 1.0 正式定稿發布。
2008 年 11 月,在 IETF 第 73 次會議上舉辦了一場 OAuth 的 Birds-of-a-Feather 討論,會後決定在 IETF 內成立正式的 OAuth 工作小組,把這個協定帶入標準化流程。不過在那之前,2009 年 4 月,OAuth 1.0 被發現有一個 Session Fixation(工作階段固定) 的安全性漏洞,影響的正是接下來會介紹的三腳授權流程,社群因此發布了修正過的 OAuth 1.0a(Revision A)。這個修正後的版本,最終在 2010 年 4 月以 RFC 5849 的形式發布——但要注意,RFC 5849 只是「Informational(參考性)」文件,並不是標準路徑(Standards Track)規格,單純是把社群已經在用的協定整理成正式文件備查。
💡 文中提到的 OpenID,是約 2005 年出現的早期身分驗證協定,和前面提到的 OpenID Connect(OIDC) 並不是同一套協定。
OIDC 於 2014 年正式標準化,建立在 OAuth 2.0 之上,主要用來處理使用者身分驗證與身分資訊交換。
從用途上來看,OIDC 可以視為接替了舊版 OpenID 所扮演的角色,但兩者的協定設計、流程與技術實作其實完全不同。
OAuth 1.0 的原始規格(OAuth Core 1.0)使用一套名詞,後來 RFC 5849 把它們重新命名,兩邊常常混用,這裡先對照一次:
| 原始社群名詞 | RFC 5849 名詞 | 說明 |
|---|---|---|
| Consumer | client | 想存取使用者資料的第三方應用程式 |
| Service Provider | server | 提供資源、同時處理授權的服務(OAuth 1.0 裡兩者通常是同一台伺服器) |
| User | resource owner | 擁有資料的使用者本人 |
| Consumer Key + Secret | client credentials | 用來識別、驗證第三方應用程式身份的一組金鑰 |
| Request Token + Secret | temporary credentials | 授權流程中代表「這次存取請求」的臨時憑證 |
| Access Token + Secret | token credentials | 授權完成後,用來實際存取受保護資源的憑證 |
除了上面這些身份與憑證相關的名詞,簽名請求裡還會帶上幾個關鍵參數,包括 oauth_signature_method(簽章演算法)、oauth_timestamp、oauth_nonce(合稱用來防止重放攻擊)、oauth_verifier(證明使用者完成授權的驗證碼),這些會在後面分別看到它們實際的用途。
OAuth 1.0 因為牽涉到三個角色(Client、Resource Owner、Server),協定裡把整個授權過程稱為三腳流程(Three-legged Flow),可以概括成三個大階段、共八個步驟:

oauth_token 與 oauth_token_secret。
oauth_token + oauth_token_secret)。oauth_verifier,證明使用者確實完成了授權這個步驟。
oauth_verifier。oauth_verifier,再對 Server 的 Access Token 端點發一次已簽名的請求,交換成正式的 oauth_token(Access Token)與 oauth_token_secret;之後 Client 就能拿這組正式憑證,對受保護的資源發出已簽名的請求,取得資料。
oauth_verifier,交換 Access Token。oauth_token + oauth_token_secret。可以看到,OAuth 1.0 的每一次關鍵請求幾乎都要帶著簽章。下一節就來看這個簽章機制實際上怎麼運作。
OAuth 1.0 選擇不直接把 Token 當密碼一樣裸傳,而是要求 Client 對「每一個」請求都算出一個簽章(Signature),附在請求裡讓 Server 驗證。這麼做的好處是:即使請求在傳輸過程中被截獲,沒有對應的金鑰資訊,也很難偽造出合法的簽章。
一個已簽名的請求,通常會帶上以下幾個關鍵參數:
oauth_consumer_key:識別是哪個應用程式(Client)在發請求。oauth_token:目前使用的臨時憑證或存取憑證。oauth_signature_method:簽章演算法,最常見的是 HMAC-SHA1,規格上也支援 RSA-SHA1、PLAINTEXT 等方法。oauth_timestamp / oauth_nonce:時間戳記與隨機字串,用來防止重放攻擊(Replay Attack)。oauth_signature:實際的簽章值。簽章的計算方式,是把 HTTP 方法、請求網址、所有請求參數,依照規格排序、編碼後串成一段「簽章基礎字串(Signature Base String)」,再用 Consumer Secret 跟 Token Secret 組成的金鑰,對這段字串做雜湊運算(例如 HMAC-SHA1)。Server 收到請求後,會用同樣的方式自己算一次簽章,只要跟請求裡帶的簽章一致,就代表請求沒有被竄改,而且真的是持有正確金鑰的 Client 發出的。
OAuth 1.0 的安全性有很大一部分是靠這套簽章機制撐起來的。這和 OAuth 2.0 把大部分傳輸安全責任交給 HTTPS 的思路不同,也和時代背景有關:2007 年前後 HTTPS 還沒普及,OAuth 1.0 不能假設傳輸層一定加密,因此改由應用層自行以簽章機制確保安全性。
💡 OAuth 1.0a 修正版加入了
oauth_verifier參數,用來補強原本 OAuth 1.0 授權流程中的安全性問題。
IETF 從更廣的社群蒐集使用情境與擴充需求後,決定重新設計一套協定,這就是 OAuth 2.0。它雖然承接 OAuth 1.0 的實務經驗,但並不向下相容,等於是一次全新設計。OAuth 2.0 最終在 2012 年 10 月以 RFC 6749(核心框架)與 RFC 6750(Bearer Token 用法)發布,而且都是標準路徑(Standards Track)規格。
整體而言,OAuth 2.0 主要在幾個方向上做了取捨:
💡 其實不管是 OAuth 1.0 還是 OAuth 2.0,它們的本質都是 授權(Authorization) 協定,而不是 驗證(Authentication) 協定。
OAuth 2.0 雖然經常被拿來實作「登入」,但它本身並不是為了驗證使用者身分而設計的。
至於 OAuth 2.0 實際上有哪些授權流程(Grant Types)、每種流程適合什麼情境,以及要怎麼正確使用,後面會再完整拆解。
OAuth 1.0 想解決的問題,是讓第三方應用程式在不取得使用者密碼的前提下,取得使用者授權並存取受保護資源。它透過三腳授權流程,讓 Client 先取得臨時憑證,等使用者同意後,再交換成可以實際呼叫 API 的 Access Token。
它最大的特色是「每個請求都要簽章」:Client 不只是帶著 Token 呼叫 API,還要用 Consumer Secret 和 Token Secret 算出 Signature,讓 Server 確認請求沒有被竄改,也確實來自持有正確金鑰的 Client。
以下整理出三個重點:
下一篇會進入 OAuth 2.0。OAuth 2.0 不是 OAuth 1.0 的小幅升級,而是一次重新設計:它拿掉強制簽章,改用 Bearer Token 搭配 HTTPS,並用不同 Grant Type 支援更多應用場景。