準備並整合登入和授權頁面 - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天

iT邦幫忙

2022 iThome 鐵人賽

DAY 10
2
Software Development

30 天與九頭蛇先生做好朋友系列 第 10

準備並整合登入和授權頁面

到目前為止,都還在介紹「既有登入系統」和 Hydra 的功能,接著要來跨出很難的第一步了--整合。

Hydra 的設計把整個登入流程拆分成四個服務:

  1. Hydra
  2. 應用程式
  3. Login Provider
  4. Consent Provider

Hydra 是個具有完整功能的微服務,所以後面不會修改 Hydra 的任何程式碼,但會調整相關的設定。主要是要修改既有系統的程式碼,其中應用程式和 Login Provider 能夠從既有系統中拆出來,而 Consent Provider 則是原本沒有的,因此需要從頭開始寫。因此從這些角度分析,原有系統跟轉換後的系統的樣子應該如下表:

轉換前的行為 轉換後的行為
GET /login 進登入頁 GET /login 轉導至 Hydra
GET /oauth2/login Login Provider,使用原有登入頁
POST /login 驗證帳密 POST /oauth2/login 改路徑
GET /oauth2/consent Consent Provider
POST /oauth2/consent 執行同意授權
GET /callback 接受身分驗證回應的端點

主要就是把驗證帳密的部分拆出來,其他與 IdP 相關的功能,如註冊、忘記密碼等,就先不動。

開始修改程式碼

在測試既有系統的登入過程中,觸發登入(和顯示登入頁)的 URL 主要是 /login,這個連結將會改轉導到 Hydra,然後建立新的路徑來實作 Login Provider 與 Consent Provider,這裡使用 /oauth2/login/oauth2/consent

先找到 /login 路由,是在 routes/auth.php 裡面定義,我們先改成一個轉導的寫法,另外同時新增 /callback 路由,是 OAuth 2.0 流程的最後一步會用到的:

// 原本
// Route::get('login', [AuthenticatedSessionController::class, 'create'])
//             ->name('login');

// 調整移到最下面
Route::get('login', function() {
	return response('先假裝我是轉導');
})->name('login');

Route::get('callback', function() {
    dump(request()->all());
    return response('拿到身分驗證回應了');
});

再來把它原本對應的 AuthenticatedSessionController::create() 方法,設定給新的 /oauth2/login 路由,可以寫在 routes/auth.php 的最下面,對應的 POST 方法路由,也重新設定路由名稱,並把回應調整過:

Route::get('/oauth2/login', function() {
    return view('auth.login');
})->name('oauth2.login');

Route::post('/oauth2/login', function(\App\Http\Requests\Auth\LoginRequest $request) {
    $request->authenticate();

    $request->session()->regenerate();

    return 'OAuth 2.0 身分驗證完成';
});

然後測試看看 http://127.0.0.1:8000/oauth2/login 能不能看到原有的登入頁,可以的話就算成功了。

接著先暫時複製 login 頁面(resources/views/auth/login.blade.php)來做一個 consent 頁面(resources/views/auth/consent.blade.php),並設定到 /oauth2/consent 路由,同時也建立一個讓 consent 發送 POST 請求的路由:

Route::get('/oauth2/consent', function() {
    return view('auth.consent');
})->name('oauth2.consent');

Route::post('/oauth2/consent', function() {
    dump(request()->all());
    return 'OAuth 2.0 授權完成';
});

頁面隨便做,能用比較重要:

9-1.png

重新註冊 Hydra 應用程式與設定 Providers

因為前面的過程有把 DB 重新清理過,這裡再重新註冊一次應用程式。跟之前說明快速開始一樣,但指令和最後的轉導路徑調整一下:

hydra --endpoint http://127.0.0.1:4445/ clients --skip-tls-verify \
        create \
              --id my-rp \
              --secret my-secret \
              --grant-types authorization_code,implicit,client_credentials,refresh_token \
              --response-types "code,token,id_token,token code,code id_token,id_token token,id_token token code" \
              --scope openid \
              --token-endpoint-auth-method client_secret_basic \
              --callbacks http://127.0.0.1:8000/callback

hydra.yml 的設定需要新增 Login Provider 與 Consent Provider 的位置,記得改完之後要重啟 Hydra。

見證奇蹟的時候

我們再回頭把 /login 的程式調整一下。還記得快速開始的 Debugger 嗎?依對應必要的欄位,把程式實作出來如下。其中有個欄位是 Authorize URI,這個指的是 Hydra 啟動授權流程的端點:

Route::get('login', function () {
    $authorizeUri = 'http://127.0.0.1:4444/oauth2/auth';

    $query = \Illuminate\Support\Arr::query([
        'client_id' => 'my-rp',
        'redirect_uri' => 'http://127.0.0.1:8000/callback',
        'scope' => 'openid',
        'response_type' => 'code',
        'state' => '1a2b3c4d',
    ]);

    return redirect($authorizeUri . '?' . $query);
})->name('login');

接著回首頁再按一次登入,如果前面的步驟有照著做的話,畫面內容會完全一致,但注意看網址,會多一個 login_challenge 的參數,這個是 Hydra 建立並轉導至 Login Provider 的:

9-2.png

看到這個畫面,就先恭喜大家成功完成第一步串接了,執行登入應該就會看到 OAuth 2.0 身分驗證完成。到這邊為止,我們已經完成了循序圖的第 1 步和第 2 步了,明天來看我們要如何實作第 3 步。

今天的程式碼調整不難,這是因為「既有系統」還算小,所以還很好控制,即便資料庫有共用,但都可以透過 API 包裝來解決。實務上真正會遇到的問題,通常是身分驗證流程與應用程式綁定太多業務邏輯,比方說:有些產品擁有者對於登入定義可能包含了身分驗證與存取控制,而存取控制的邏輯,可能跟產品有直接相關,像需要通過實名認證與管理員授權。因此這些產品擁有者並不認為帳密正確就算登入完成,而是必須包含實名認證與管理員授權,在這個情境下,要把登入獨立拆出來就會非常困難--因為產品跟 Hydra 對於登入這個行為的描述不是共同語言(Ubiquitous Language)。

遇到這個問題,就必須跟產品擁有者溝通關於登入行為的分離。慶幸的是,當有需求做第三方登入驗證的提供者,通常就必須重新定義登入行為,因為原本只有第一方登入的行為,後續要改成可以接受第三方登入,業務邏輯是需要重新思考的。當遇到了,記得好好跟產品擁有者討論即可。

相關的程式碼可以參考 GitHub


上一篇
Hydra 的身分認證與授權流程詳解
下一篇
實作 Login Provider
系列文
30 天與九頭蛇先生做好朋友23
圖片
  熱門推薦
圖片
{{ item.channelVendor }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
雷N
iT邦研究生 1 級 ‧ 2022-09-25 10:32:54

萬事起頭難
元元不說難

我要留言

立即登入留言