在昨天,我們已經手動在 Cognito Console 綁定了 Google IdP(因為需要 Client ID/Secret)。
今天,我們要把 Cognito 的基本架構(User Pool、App Client、Domain) 自動化,用 CDK 來管理,讓專案更有雲原生味道。
以下是一個最小範例,創建 User Pool + User Pool Client + Domain:
python
from aws_cdk import (
Stack,
aws_cognito as cognito
)
from constructs import Construct
class CognitoStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# 建立 User Pool
user_pool = cognito.UserPool(
self, "MyUserPool",
user_pool_name="MyCognitoUserPool",
self_sign_up_enabled=True,
sign_in_aliases=cognito.SignInAliases(email=True)
)
# 建立 User Pool Client
user_pool_client = cognito.UserPoolClient(
self, "MyUserPoolClient",
user_pool=user_pool,
generate_secret=False,
auth_flows=cognito.AuthFlow(
user_password=True,
user_srp=True
),
o_auth=cognito.OAuthSettings(
flows=cognito.OAuthFlows(authorization_code_grant=True),
scopes=[
cognito.OAuthScope.OPENID,
cognito.OAuthScope.EMAIL,
cognito.OAuthScope.PROFILE
],
callback_urls=["http://localhost:3000/"],
logout_urls=["http://localhost:3000/"]
)
)
# 建立 Cognito Domain (用來產生 Hosted UI 登入頁)
cognito.UserPoolDomain(
self, "CognitoDomain",
user_pool=user_pool,
cognito_domain=cognito.CognitoDomainOptions(
domain_prefix="my-demo-app"
)
)
這樣我們就用 CDK 把 Cognito 的骨架 建立起來了。
--
「既然用 CDK 建立 User Pool,為什麼不把 Google 的 IdP 也寫進 CDK?」
嗯... 關於這個問題,其實主要是關於 Client ID 和 Client Secret 的管理方法不同所導致的選擇
所以這裡我們的策略是:
雖然這次我們沒用 Secrets Manager,但還是可以認識一下他的功能:
只不過Secret Manager並非持久免費的服務,AWS提供30天試用,30天過後以每個secret每月0.4美金收費(截至撰稿日)
以下是一個簡單的範例
import boto3
def getGoogleOAuthSecret(event, context):
client = boto3.client("secretsmanager")
response = client.get_secret_value(SecretId="GoogleOAuthSecret")
print(response["SecretString"])
有了 Cognito User Pool + Hosted UI,我們在前端可以用 Amplify 很輕鬆地串接。
首先需要安裝
npm install aws-amplify
在使用Amplify串接Cognito時,我們基本上需要做的事情就是兩件
1.初始化Amplify實例,Configure Cognito相關的參數
程式碼如下:
import { Amplify } from 'aws-amplify';
/// 使用 Amplify.configure 來設定 AWS Cognito 的驗證 (Auth) 模組
Amplify.configure({
Auth: {
/// Cognito User Pool 所在的區域 (Region)
region: 'ap-east-1',
/// User Pool 的唯一識別碼 (在 AWS Cognito 介面可找到)
userPoolId: 'ap-east-1_xxxxxxxx',
/// User Pool App Client ID (前端應用程式對應的 Client ID)
userPoolWebClientId: 'xxxxxxxxxxxxxxxxxxxx',
/// OAuth 相關設定(主要用於 Hosted UI)
oauth: {
/// 你的 Cognito User Pool Domain (必須在 AWS Cognito 或CDK裡先設定)
domain: 'your-user-pool-domain.auth.ap-east-1.amazoncognito.com',
/// 指定要求的使用者資料範圍 (scope)
/// - 'email': 允許存取使用者 email
/// - 'openid': 基本 OIDC 權杖
/// - 'profile': 使用者基本資訊 (名字等)
scope: ['email', 'openid', 'profile'],
/// 使用者登入成功後,會被導向到的 URL
/// 一般在本地開發會用 http://localhost:3000/callback
redirectSignIn: 'http://localhost:3000/callback',
/// 使用者登出後,會被導向的 URL
redirectSignOut: 'http://localhost:3000/signout',
/// 使用的授權流程
/// - 'code': Authorization Code Grant (安全性較佳,建議使用)
/// - 'token': Implicit Grant (舊式方法,不建議)
responseType: 'code',
},
},
});
這些值都是在之前的 CDK 或 Console 建置 Cognito 時產生的資源 ID。
接著我們需要實作的是登入流程
如果有跟著昨天的內容,我們大概可以感受到使用者點擊「Google 登入」時,背後的流程是長怎樣
瀏覽器導向 Cognito Hosted UI -> Cognito 與 Google OAuth 交換 code
-> 前端使用 Amplify 交換 code 取得 JWT Token
在 Amplify 中,可以這樣觸發:
import { Auth } from 'aws-amplify';
// 導向 Hosted UI
Auth.federatedSignIn({ provider: 'Google' });
登入成功後,可取得使用者資訊或 Session:
// 取得目前使用者資訊
const user = await Auth.currentAuthenticatedUser();
// 取得 JWT Token
const session = await Auth.currentSession();
const idToken = session.getIdToken().getJwtToken();
這就是前端與 Cognito Hosted UI 的串接核心流程。
原本想要直接在今天藉由 Amplify 直接帶入專案的實作的但結果發現我們系統中需要注意的細節太多可能會一路串到資料庫去,如此一來知識點會太混雜,於是乎就打算將注意力先集中在 AWS 產品的介紹與基礎實作,再來切入實戰級的專案產品的實作內容,這樣的內容應該會比較順暢,請期待專案實作的各位再稍等一下啦 QQ