個人用戶管理對於許多網站 或 App 都是不可缺少的,大家有可能會經常煩惱要怎麼樣讓自己的服務,可以透過 Google 、Facebook 等第三方認證,或者擁有自己的使用者群。這時可以選擇考慮 AWS Cognito,它可以讓你快速的為你的服務建立使用者群,並且可以整合讓使用者擁有權限操作其他 AWS 服務 。由於使用 UI 建立 Cognito User Pool 的文章已經不少,所以這邊文章會介紹使用 AWS CLI cognito-idp 建立一個用戶可以註冊,並且自己透過 Mail 認證通過的 User Pool。

前置作業


  1. AWS CLI
  2. AWS SES 驗證過的 Email

透過 AWS Console 設定 SES


Step1 註冊 SES Mail

這邊要注意,Cognito 目前允許的 SES Region 只有三個,分別是 us-east-1、us-east-2、eu-west-1。所以在建立 Email 時,請確認要別確認 Region 喔,不然之後有可能會無法設定喔

請到 SES 下面,點選 Email Address,然後點下 Verify a New Email Address,並填寫你的 Mail

Ses_register_1

當你填完以後,這時可以你的 Email 在 list 中,而 Verification Status 還會在 Pending 喔~ AWS 會寄 Mail 到你的郵箱,請去填寫的 Email 認證。

Ses_register_2

Step2 驗證 Mail

這時候當入剛剛輸入的 Mail 中,應該會有一封 Email Address Verification Request 的 Mail ,這時候請點選裡面的 Link 完成驗證

Ses_register_3

Step3 確認信箱已驗證

這時候重新進入 SES 頁面,會發現 Verification Status 是 verified

Ses_register_4

如果你也在嘗試練習,請點選 UI 的 Email ,這時候可以看到 Identity ARN。請將 Identity ARN 記錄下來,後面建立 User Pool 會使用到。

Ses_register_5

使用 AWS CLI cognito-idp 建立 User Pool & Client

aws cognito-idp command 主要是讓我們來管理 User Pool 的,裡面有分兩種不同的 level 的 command ,分別是 ADMIN 和 一般 User 的, 所有 admin 的 command 都會是 admin- 開頭的。以下是附上 AWS 官方的圖片,一開始 User 在 Cognito 註冊是在 已註冊狀態(Registered) 是不能 Login 的,必須要是認證狀態(Confirmed) ,才能 Login。而目前只有兩種方式才能讓 User 變成認證的,第一種就是由 ADMIN 這邊認證,而第二種就是 User 可以自己認證透過電話或 Mail。這邊要示範透過 Mail 認證

amazon-cognito-sign-in-confirm-user

Step1 使用 AWS cognito-idp 列出目前所擁有的 User Pool

使用 list-user-pools command 列出目前擁有的全部 User Pool,目前艾倫的 User Pool 都已經清空,所以沒有。

1$ aws cognito-idp list-user-pools --max-results 10
2
3#Result
4{
5    "UserPools": []
6}

Step2 使用 cognito-idp 建立 User Pool

如果正在使用這邊文章做範例請紀錄 ID,後面建立 Client 需要使用

這邊使用 create-user-pool command

  • –pool-name User Pool 的名字
  • –auto-verified-attributes 想要驗證的欄位,這邊是 Mail
  • –username-attributes 這邊特別指定 User Name 可以透過 Email 替代 ,這邊可以不強制 User Name 使用 Email,也可以讓 User 自定義自己的 Uesr Id
  • –email-configuration 這邊要定義 Cognito 寄出的 Mail Server,艾倫這邊使用 SES 寄出,SourceArn 記得改成你的 SES ARN

這邊的 Result 由於太長,所以艾倫有做一些刪減。Result 中的 VerificationMessageTemplate ,裡面寫 CONFIRM_WITH_CODE, 當 User Sign up 時, Cognito 會產生一組驗證碼寄送到 Mail 中,這樣 User 就可以使用驗證碼來自己驗證。另外可以看到下面 PasswordPolicy,至少要一個大小寫英文字母,並且要有符號和數字,最後長度必須要八以上,這些條件都是可以客製化的,不過可以看到,艾倫在建立 User Pool 時,沒有帶入特定參數,所以這些目前是使用預設的。

 1$ aws cognito-idp create-user-pool \
 2 --pool-name helloWorld \
 3 --auto-verified-attributes email \
 4 --username-attributes "email" \
 5 --email-configuration=SourceArn="arn:aws:ses:us-east-1:129824596365:identity/allen.hsieh.aws@gmail.com",ReplyToEmailAddress="allen.hsieh.aws@gmail.com"
 6
 7# Result
 8{
 9     "UserPool": {
10         "Name": "helloWorld",
11         "VerificationMessageTemplate": {
12             "DefaultEmailOption": "CONFIRM_WITH_CODE"
13         },
14         "EmailConfiguration": {
15             "EmailSendingAccount": "COGNITO_DEFAULT",
16             "ReplyToEmailAddress": "allen.hsieh.aws@gmail.com",
17             "SourceArn": "arn:aws:ses:us-east-1:129824596365:identity/allen.hsieh.aws@gmail.com"
18         },
19         "Policies": {
20             "PasswordPolicy": {
21                 "RequireNumbers": true,
22                 "RequireLowercase": true,
23                 "RequireSymbols": true,
24                 "RequireUppercase": true,
25                 "TemporaryPasswordValidityDays": 7,
26                 "MinimumLength": 8
27             }
28         },
29         "Id": "ap-northeast-1_DpExb5BW8",              "Arn: "arn:aws:cognito-idp:ap-northeast-1:129824596365:userpool/ap-northeast-1_DpExb5BW8"
30}

Step3 確認 User Pool

再次使用 Step1 的 list-user-pools Command,這時後會看到 User Pool。

 1$ aws cognito-idp list-user-pools --max-results 10 
 2
 3# Result                                                                                                         
 4 {
 5     "UserPools": [
 6         {
 7             "CreationDate": 1625996882.861,
 8             "LastModifiedDate": 1625996882.861,
 9             "LambdaConfig": {},
10             "Id": "ap-northeast-1_DpExb5BW8",
11             "Name": "helloWorld"
12         }
13     ]
14 }

Step4 建立 User Pool 的 Client

一個 User Pool 可以有多個 Client ,例如想要區分 App, Web 等等

如果正在使用這邊文章做範例請紀錄 ClientId,後面建立 User 需要使用

  • –user-pool-id 這邊需要 Step3 or Step2 的 ID (User Pool 的 ID)
  • –explicit-auth-flows 特別指定驗證方式, USER_PASSWORD_AUTH 是同意使用 User & Password 去做驗證,這邊是因為後面方便要 Demo 使用 Cli 去做 Login 的關係,否則正式環境 App 建議使用 (SRP)[https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html] 驗證。
  • –client-name: Client 的名字
 1$ aws cognito-idp create-user-pool-client \
 2   --user-pool-id  ap-northeast-1_DpExb5BW8 \
 3   --explicit-auth-flows  USER_PASSWORD_AUTH \
 4   --client-name cli
 5
 6# Result
 7{
 8     "UserPoolClient": {
 9         "UserPoolId": "ap-northeast-1_DpExb5BW8",
10         "LastModifiedDate": 1625999636.182,
11         "ClientId": "400d11al9p25pmjg3igflo5h4i",
12         "AllowedOAuthFlowsUserPoolClient": false,
13         "TokenValidityUnits": {},
14         "ExplicitAuthFlows": [
15             "USER_PASSWORD_AUTH"
16         ],
17         "RefreshTokenValidity": 30,
18         "CreationDate": 1625999636.182,
19         "EnableTokenRevocation": true,
20         "ClientName": "cli"
21     }

建立 & 驗證新 User

這邊需要使用建立 Client 時的 Client Id ,如果忘了拷貝下來,可以使用 list-user-pool-clients 取得。

 1$ aws cognito-idp list-user-pool-clients --user-pool-id ap-northeast-1_DpExb5BW8                                                                        
 2
 3# Result
 4 {
 5     "UserPoolClients": [
 6         {
 7             "ClientName": "cli",
 8             "UserPoolId": "ap-northeast-1_DpExb5BW8",
 9             "ClientId": "400d11al9p25pmjg3igflo5h4i"
10         }
11     ]
12 }

Step1 建立新 User

  • –client-id 從 Create Client 時拿掉
  • –username 這邊要填寫 User Name ,由於艾倫建立的 User Pool 強制要求需要使用 Email 當 User Name,所以要使用 Email 喔
  • –password 密碼,記得要 Follow 之前的 PasswordPolicy
 1$ aws cognito-idp sign-up \
 2   --client-id 400d11al9p25pmjg3igflo5h4i \
 3   --username  allen.hsieh.aws@gmail.com \
 4   --password  Aa123456!
 5
 6# Result
 7{
 8     "UserConfirmed": false,
 9     "UserSub": "c57c5fb9-7fbd-406d-9bb3-19f88cfc9ac0",
10     "CodeDeliveryDetails": {
11         "AttributeName": "email",
12         "Destination": "a***@g***.com",
13         "DeliveryMedium": "EMAIL"
14     }
15 }

這時後透過 AWS Console 進入 User Pool 裡面的 Users & Groups 裡面,可以看到剛剛註冊的用戶,這邊可以看到 Account Status 是 UNCONFIRMED

user-pool-user1

Step2 嘗試 Login

Step1 註冊 Command 是 Sign-up,這邊是驗證,而並非登入,所以 Command 不是 Login ,而是 initiate-auth

  • –auth-flow 驗證方式,這邊使用 USER_PASSWORD_AUTH 單純使用 UserName & Password 。

這邊要特別注意, Client 必須要 Allow 可以使用 USER_PASSWORD_AUTH,艾倫在 Create Client 時有特別特定

  • –auth-parameters 這邊是 USERNAME & PASSWORD

結果會是噴 Exception ,原因是 Account Status 還是 UNCONFIRMED

1aws cognito-idp initiate-auth \
2 --auth-flow USER_PASSWORD_AUTH \
3 --client-id 400d11al9p25pmjg3igflo5h4i \
4 --auth-parameters USERNAME=allen.hsieh.aws@gmail.com,PASSWORD=Aa123456!
5
6# Result
7An error occurred (UserNotConfirmedException) when calling the InitiateAuth operation: User is not confirmed. 

Step3 驗證 User

Cognito 會寄送 verification code 到你的 mail ,信箱內應該要有一封 email title 叫做 Your verification code,如果沒有看到,建議先到垃圾郵箱裡面查看。

verification_code_mail

這邊使用 confirm-sign-up 去認證 ,注意正常認證成功是不會有任何 result

1aws cognito-idp confirm-sign-up \
2 --client-id 400d11al9p25pmjg3igflo5h4i \
3 --username  allen.hsieh.aws@gmail.com \
4 --confirmation-code 328625

如果沒有收到 Email 或覺得麻煩 ,想要直接認證的話,可以先使用 ADMIN 的權限認證

1aws cognito-idp admin-confirm-sign-up \
2  --user-pool-id ap-northeast-1_DpExb5BW8 \
3  --username  allen.hsieh.aws@gmail.com

Step4 確認驗證

這時後透過 UI,可以看到 Account Status 已經是 CONFIRMED

user-pool-user2

Step5 再次 Login

這時候,再次 call initiate-auth 就會成功,而且這時候 Result 會有三個 Token,分別是 IdToken, AccessToken 和 RefreshToken,這三個 Token 都是使用 JWT Token。RefreshToken 就如字面的意思,主要用來更新 Token。Response 中有 ExpiresIn 這個是告知 Token 過期時間,這邊單位是秒。IdToken 主要有包含客戶的一些資訊(例如 email, phone),可以知道這個人是誰。而 AccessToken 主要是給你 call 一些不需要知道個人資訊的 API,API 只是單純要知道這個 User 擁有權限就可以使用 AccessToken。這邊也可以使用 JWT.io 將 Token decode 回來,看看裡面有什麼資訊。

 1aws cognito-idp initiate-auth \
 2 --auth-flow USER_PASSWORD_AUTH \
 3 --client-id 400d11al9p25pmjg3igflo5h4i \
 4 --auth-parameters USERNAME=allen.hsieh.aws@gmail.com,PASSWORD=Aa123456!
 5
 6# Result
 7{
 8     "AuthenticationResult": {
 9         "ExpiresIn": 3600,
10         "IdToken": "eyJraWQiOiJhTmJjOWZpbUw5TjUxWWk2VkV6VlUwZ1EyZUdkRFVPNElQVURYYmN4MVhFPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJjNTdjNWZiOS03ZmJkLTQwNmQtOWJiMy0xOWY4OGNmYzlhYzAiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLmFwLW5vcnRoZWFzdC0xLmFtYXpvbmF3cy5jb21cL2FwLW5vcnRoZWFzdC0xX0RwRXhiNUJXOCIsImNvZ25pdG86dXNlcm5hbWUiOiJjNTdjNWZiOS03ZmJkLTQwNmQtOWJiMy0xOWY4OGNmYzlhYzAiLCJvcmlnaW5fanRpIjoiNGVlYmIyMTQtMzBlOC00N2Q3LWJjOGMtODA3NjM5ZTljMzU2IiwiYXVkIjoiNDAwZDExYWw5cDI1cG1qZzNpZ2ZsbzVoNGkiLCJldmVudF9pZCI6IjM3Nzk5N2RlLTFhMWYtNDA5ZS1iNjM1LWVjNzk2ZTBjZTEwYiIsInRva2VuX3VzZSI6ImlkIiwiYXV0aF90aW1lIjoxNjI2MDAzNjU5LCJleHAiOjE2MjYwMDcyNTksImlhdCI6MTYyNjAwMzY1OSwianRpIjoiOWZkN2M1MjYtNTBkMy00NWFmLTg5YmUtOWMxNWMzMjMzMjUxIiwiZW1haWwiOiJhbGxlbi5oc2llaC5hd3NAZ21haWwuY29tIn0.Asp7HF1mpPzWUE3l_7S8EH1nY8x9y3o2NK_8fBb_-Hz0PPXTkPGTvjqooEbHRLGGWAf8d7jwoBbsqV3jhFN-UHvrfcAYNRX_DAOeyxi4tCDTQXDk_Ozu8bCU9Clmb5zyg8Jo_SBAZdnKJewbWLEuhqi9_g7AHQrI_sufec3G6L8Ufr3mdfm-eTGtMcICmgkCxImd_zVgWSGkT5gUgcN0DvFzu6cqWC6kjaM3UoUx1uJ-iLpgA2rXzoBEKI2MqGWlIFSJcxL-IFtZyBvU2k4WNDrT5wN1s30UBa6_zeP_-8ZOp-BdU5D4omWltJfns16fqsPBmhbVoyuycamhxv_htQ",
11         "RefreshToken": "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.BJz_JPV5gRBtqiMLhiQroXg0GsTg33jpT9JOlpT1COkmYCTQBfYblnkoGb4tNCL7y7JWreHJDAFXRafaq4fYkOU9gob0FSxUv3MhVs8V1AKiWunM-iytuKFjNALe2Xlmsa70hBcjiEda5agWMdoYG8Q4eL59-ogmJUgsn46h69-B2cxnWx7RQQ9RwJpauLV8zEEKCUYkVE7C5umlceJjQI0OWN76fcoWoCdx-IUgAJEBf72PJ0KBWeZ1p4ElSMWFtg3Egso-OcES1rzW1gE1qG7N_Hcn_58td8fnQAZEONMxPbHzH2C6AaUPF1NaLcQl9mRSs7KgODZkGFQ6FmAc8Q.jitGP9kMECesDsZj.QODsHRk2PYwx9LktJrkbGz__7i2eh7Fz7ueI5qrgrR3uK0nC5jU06RVuMQqn9m_EnlWh0dj9HEU_e9j5XMrbeRA4ZCJkL3wlsZmfRFfF04pAhjO7H7_pxsq_-gGw-5ZNGyh0osd3MICYIYBoJ90RrRHetKKfna9X_hktv8f9A63lqjZLSWDFhAWmXYD_UiLsi7Pf9JAw5g9w3MGYxQ2RN_KRmoaxQg213_6r4fZjqpH0QDpo7Y7bsMu82kGusv8HXwP-O4l7pE6IMTH8k2oX2q2Yg85o-fwEjFZmIiCwnkQfOK607I9qUIN1w8ACtfW6KbN8AXch3CXsnyYeZbprAtgzwYJCERIImfv6UakKwvqlzBCIr3o_idkHnBJrn4HnVhlNTzttl6cyySvO3ombFdHWfn8KbztaAVAZwazQC5WNTm6sCwvz5oR30A-i2taDeT7gwXe_CSg-YiSG8ODKCcTBOeCmCKOpx2Z7maMBP1dAsKqHQ_0-Ozfp_VPJ57TsjTVyLiUCwOCKlZPcMJbKz_svgkc7yTHv9EAYklOLDC3eEEvIIuhXqkmdW0CEvlwvfQLXiRvMbrVPxxGvOrHyIN7f7MSEY8NjZZ-LaXxCg7ilCHEk_pNGn8tru6r_FjTs657tVnxRg0CL5yOu-heKwcYXrK1syS5YRWYi4DYwkBI2_2ymPOlWxZY94cM6LfJpW7EVFxvF7ZUu3aMm4F2s5lQsFuWo6LNIm7umdpm6hJyP6lsgzn3VHsHXBN1WQZDqqUWmVSOcAVQjzvYVaisin90r5LSwMqGE2cxgSzlykE7tSwzZbK8I3sO-iUwdZoaVQYHqSKEa9dwRUc_y3yJxfo81vaLvy73FFg2QnTUA8l6mjziRk0-pG3aM6ODneCGewXm0lyZ6sTbzk2euT6ohrj0CgdJ3kDe1IS0IyV8G5wd_bxE0a5U5IVMnPMexZo-HeJxMbiRKh7dFZ3lCoLvJ3r9vfOGIJCMCo5u-nCC9KkQDJQY5P8mWbKRh4pO6XmPMIj4c96reguvnXmm-eA09y89hQm9AkOafnjMDhYRNEISr0WO1zP5GQRpY98BRdz5T-e8xzQKufoerg3Ogalciw_r6FJgjUJeyfG9PEQlEQXz4nvfLFI5DwHXs768g-_02i0tRZ-mVl8cXRMQ2Sa8L-w2ptvznmayCLEL8rMdggGEHQZSuQKP5pmQu9Mq-nP4mWjR2GWey-UAMLHTZ1l-VsoextWfwdsZaTRJQNXUmOpHgZHHYb8yAOae6nQP60CNGA9DnvAkalrhaNZlOdXG5paDG8hUz1wNFsOLunEe3fhon573QivWx0dUOmxf2DGCqlDC1GrB2UvY6lw.1uaq1g1lQ9BBIMa3X5VG1Q",
12         "TokenType": "Bearer",
13         "AccessToken": "eyJraWQiOiJKcXVVM0N3dk1YNW1oMTNhb0ZrRnp6VTJ5VytIdXcwUnR4cGdRSXZEVFIwPSIsImFsZyI6IlJTMjU2In0.eyJvcmlnaW5fanRpIjoiNGVlYmIyMTQtMzBlOC00N2Q3LWJjOGMtODA3NjM5ZTljMzU2Iiwic3ViIjoiYzU3YzVmYjktN2ZiZC00MDZkLTliYjMtMTlmODhjZmM5YWMwIiwiZXZlbnRfaWQiOiIzNzc5OTdkZS0xYTFmLTQwOWUtYjYzNS1lYzc5NmUwY2UxMGIiLCJ0b2tlbl91c2UiOiJhY2Nlc3MiLCJzY29wZSI6ImF3cy5jb2duaXRvLnNpZ25pbi51c2VyLmFkbWluIiwiYXV0aF90aW1lIjoxNjI2MDAzNjU5LCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAuYXAtbm9ydGhlYXN0LTEuYW1hem9uYXdzLmNvbVwvYXAtbm9ydGhlYXN0LTFfRHBFeGI1Qlc4IiwiZXhwIjoxNjI2MDA3MjU5LCJpYXQiOjE2MjYwMDM2NTksImp0aSI6ImMyMDlhYWQyLTYxZDEtNDgwNy1hMmFlLWJjODNhNmJiY2I2NyIsImNsaWVudF9pZCI6IjQwMGQxMWFsOXAyNXBtamczaWdmbG81aDRpIiwidXNlcm5hbWUiOiJjNTdjNWZiOS03ZmJkLTQwNmQtOWJiMy0xOWY4OGNmYzlhYzAifQ.IuD31XgD2C93v0H9PUCCIWCBYUVX7QdSq3S3MQlR0LhWI7Ai-w5yEI7KVT0Zy6U943sqsvoagU3ykNS756ivfJbPzI1un4L6d8Y4bZBR9ZY4QRylLx7s3sDY4gnNd02KFGcwYQxj_MGm3SDXQF9xFyDJ6_gP7j1XXgIWknhvrN1-QzEaNqGDiY5v97jT1pLLGvlAe5VuUkcbptMdhStM10fAWww-xMVO_2W3mZcCJwUQ5NR6CaGo2HP_EwkkZ5_Qy7w8q3yTzdrNlX67e6TCHdU5Yu2p-bceSU3_2X3IeHfUhAOobhR_BuntsoSdXLJgnJcCYYg2BiHcUY2jIf8h6A"
14     },
15     "ChallengeParameters": {}
16 }

![jwt_decode_example]jwt_decode_example.webp?width=1024px#center)

總結

AWS Cognito 真的很方便,對於還沒有自己的用戶管理App,可以快速的部署。內部還有許多功能例如 Host Login UI 、客製化 Lambda trigger 或者使用第三方認證。艾倫之後有空的話,也會寫使用第三方認證如 Facebook 或 Gmail,或者建立 Serverless Application (API Gateway 驗證)相關文章~