AWS Cognito包含User Pool和Identity Pool兩個產品。基于User Pool可以快速實現一套用戶系統。Identity Pool用于實現聯合身份認證。User Pool跟Identity Pool的關系如下面幾張圖所示。
通過AWS labs的demo可以體驗Cognito的主要功能,github地址:https://github.com/awslabs/aws-sdk-ios-samples。
User Pool
aws-sdk-ios-samples里面CognitoYourUserPools-Sample
和CognitoAuth-Sample
兩個demo跟User Pool里面有關。要把這兩個demo跑起來,要先在User Pool里面添加一個pool,接著添加一個App,再接著修改項目里面對應的配置。
CognitoYourUserPools-Sample
支持注冊、登錄、找回密碼等功能。
還有更簡單的使用方式,那就是User Pool會提供一個OIDC server。
https://wla.auth.ap-southeast-2.amazoncognito.com/login?
response_type=code
&client_id=1t80si9ch1voi0bdusm5c9svn
&state=00a4715e-13b3-4da3-8553-dcc757d1d544
&redirect_uri=wla://signin
&scope=openid
&code_challenge=CHdOC4yZEQWOg3jZNCTm8b8v8jPnKjizRipIF0ltvr8
&code_challenge_method=S256
在控制臺可以配置icon、前景色和背景色。之后點擊Sign in
按鈕,會在In-App browser里面打開下面這個頁面,挺好看的。
看起來這是一個標準的OIDC服務器,可以看看它的Discovery,然后就會發現其實也不是那么標準,里面有authorization_endpoint,并沒有看到token_endpoint。看demo里面的玩法就更野了,根本不來請求這個Discovery,而是要使用Info.plist里面的AWS
的配置去拼這些endpoint。
上面兩個demo中都可以注冊賬號,可以在控制臺里面看到這些賬號。
為了滿足用戶多樣的需求,User Pool在很多環節都可以設置Lambda function,非常有用。
Identity Pool
CognitoSync-Sample
這個demo是展示Identity Pool功能的,這個demo就有點太粗糙了,居然也沒有加入User Pool登錄。
我配置了一下Facebook登錄,登錄了一下試試,感覺還挺溜。
token
User Pool登錄成功得到的信息如下所示。看起來是OAuth那一套,這個AccessToken很長,是一個JWT Bearer Token,跟IdToken信息有重疊。這兩個token都可以拿到https://jwt.io里面解開看看。具體的描述請參看:Using Tokens with User Pools。
{
"AuthenticationResult": {
"AccessToken": "eyJraWQiOiJicVQ4ZkViVzJCbXN2blZwcGRlUWQ0REhKaTZjdzNNZGhaTzJrN2s3c2w0PSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI4NzI2ZmFjZi1jNjNmLTRlNDQtOGM1YS1jOTE5YjdjNTMxMDYiLCJkZXZpY2Vfa2V5IjoiYXAtc291dGhlYXN0LTJfZjY1MjY0NjktNjBhMi00ZWUyLTk5MTMtNzAyYmE3NTllMjdjIiwidG9rZW5fdXNlIjoiYWNjZXNzIiwic2NvcGUiOiJhd3MuY29nbml0by5zaWduaW4udXNlci5hZG1pbiIsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC5hcC1zb3V0aGVhc3QtMi5hbWF6b25hd3MuY29tXC9hcC1zb3V0aGVhc3QtMl9IbkdrZVh5YUIiLCJleHAiOjE0OTgyMzU4MjEsImlhdCI6MTQ5ODIzMjIyMSwianRpIjoiNjcyMmUyM2EtMmQ4Mi00YTg2LWE1NWYtZjRjNjRjYjZmMTQ2IiwiY2xpZW50X2lkIjoiMXQ4MHNpOWNoMXZvaTBiZHVzbTVjOXN2biIsInVzZXJuYW1lIjoiaHMwMDIifQ.ONnjTQr0Qd2AaKCxfnBKT3TnMu-k8Jf_awBapH2A3QpzSIBBBYo3lQzL20JMP92gFfwho9XQGsUjPwNMfkIl19YZG_8BZSME1Aw6l9LT5Q35pGBiuaq1A82rOGmmfgS35RYQ25YxeF18_vO6e4gYdxvAHMSrK8zIJQvFWV6wQYkRpucphKg7bCrmdW5mJt_QyC64JA3JYuHGW2bkFM7IsTt9eop5igIxQTp7uR8oWsSLeYAsJ2nkAdRkbvVt1XvqNmFCU8iOIF0rtkm6bKGdYQprlxAHvyxFVTVBJt-42UWWGII7YaYQTF8k7Lhzu6HAlU27KzlSZ2279CVGKw2BVQ",
"ExpiresIn": 3600,
"IdToken": "eyJraWQiOiJlaFNtY1pjM1JBdEhGYkVWVFpucFBDNWZTSFwvSW15dUN1cmRHUk0wcUVrST0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiI4NzI2ZmFjZi1jNjNmLTRlNDQtOGM1YS1jOTE5YjdjNTMxMDYiLCJhdWQiOiIxdDgwc2k5Y2gxdm9pMGJkdXNtNWM5c3ZuIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTQ5ODIzMjIyMSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLmFwLXNvdXRoZWFzdC0yLmFtYXpvbmF3cy5jb21cL2FwLXNvdXRoZWFzdC0yX0huR2tlWHlhQiIsInBob25lX251bWJlcl92ZXJpZmllZCI6dHJ1ZSwiY29nbml0bzp1c2VybmFtZSI6ImhzMDAyIiwicGhvbmVfbnVtYmVyIjoiKzg2MTg5MDU4MTgyOTIiLCJleHAiOjE0OTgyMzU4MjEsImlhdCI6MTQ5ODIzMjIyMSwiZW1haWwiOiJjcWZ6am5lQGdtYWlsLmNvbSJ9.eV9osb4FQUAzCf4bDFGH9SHwuelC1v78oenxINihVGZ7aqJ82sozfSPtMMcsjN9sm32RZajoyBkw9Buni_bywwjv5FtVgoLb3aXkSvHxtNrXyT1Ligym2c3NJvsEC2aiVr5DFBIEZieSwdsLajcM5JSk9KYgO5OAiTuIe_TiKom2lfm7-n_uF0b09Z3GuYqHXkvufxJnXzb3gqxeY1_M6g3BqEu3Nta-kGqabzn_-6JsYgDY14jyBwRrydMqUqjfZjjLRgZxHBitWMWz0dp4wrTpHOGktqtVdjhj9m8-p4IXp-za3ADcoBh5QSCX49loHi2Mm45gfFiyEVLC3X4P7Q",
"RefreshToken": "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.VFeqGiKV-fH8uTpoihvbyS7FVcdPGiSQVZzf9CWL6UfD3ucm5RaL_J0Sm6kh_0T86gZJQdO1nZ2BBongemyipzSvrowOTUqnqk5sE-5mTU6VXZYzRevhnm-Gr2sF7o0Dx4IrI8Esjn2K0KMZx2TLGHGAHRvFTG5kmFJNR_80o9VRMXaN1X_1LEuJF58amfj1lMqUq0lWoLmWBb5AaCnfap3DuIBxq_egFTgV-JvhlZaHD9Y6L8RANh6Rv2YunCYdyMs8Dw8H7HZHSm5hSdB1sIh-e1M6PMVX-i0V_3R6_IkU0qnC_0NTQw_MvZFO6psjDKCNejAAbvMDoJO6zJsDVQ._B25kR6-WKz3zzJW.-5Xv72tm6qz-gnm2Ojf5QfSxGqFsvl_DlSqgHbYLaUJJLcjKlQGH1uhSohaivadFKT29cdoRH-rHATCSQuzia1RyiMXfYtF2a2DLv3gErS6Rgd4EyZWvddLTjWi0-QdrXvEp5Ry1P_2jakp7_Ng4vKRuvkm9kDHTeeLdLEwDIBJXNGXvIx7qHLBDaBbcP2O9ayU0vVSM3WdzkaHe2-hCEvaHe5w9ygLrzqEdpxv1MsP1botJcBPiBFIPCt-29EKMLoDNC9PU8jczJuGwqcGINuYt9S9kh6JMLw-rpX6wofX4h_B6uy8zEiN6JhQWIv4incFvJBjK1z5v8N51gcT3U1yzNtkeImiU8R-SWPl2gTp3SxguK_-FxhjBuQOHv8v1n_NMYkKz7PchFv77TVBEVDFmNg0cdB2gMhV2yk8x_HbNK5ttOncpN_sLDR1YML3YwIRjiWAz4cyCM6oAGOkSEFwkFcwqHy8PtLdXH2rxxhopUfGJAAUJdpxWC_NpoJFvrBT20794sUMEBRU9BBudsTargWSDZaycgYJN-VxYmFIfoqkejJt9iFs8XGZpx0Uz8qMxNic7g-Kl2CMtht5dgK-_DTlqity6d3CQe3kVjabmoVEwfniopEo_y_Uv7tICgAMc2ScOjg-YjFO3oKzdeOejlEmXV0EvivtICR-QChlqN-XlvWebwdruX6xkyYVh6Js12cH-V40Ll8PEaBVpIOlDe5hz53feR3X9hKmfbAqSZVp0Uk9fh_MJf7vh3XnHyamnTTpn4f-0q9lzHwbt6iiN4vxhCoePVwzLCl4FZFllO0XwVJJmhjCc9YbsIgV6BANjLjyHo5pmb8FfjrP95m5vJVHjRztNeYkr6njCL1rkNwxeAjXahPLKGyyyuIqEPfejWHIJJ7-HUfuof3SOwDVjXRK4j7rHpCMAVGUCWzffPtN4rFPqGNUHFMtXW2J05ydCXBXdHgA4WdbfYkrwSbSY51wLU_PW9RUubbAf0P9MS680TdyjZNfavy6l9LiMFfkwXAbVb0lFBbFzMzHRKotBdoJcr01RT2Kz8JOlihsFRDqXb-V0Z06Tku63uO23z9Usyxp3am8tnYhlBBrYe0L_cdRgEXqtSBipKchwfojsQh-uPzbzLX95XSMlLdedcSJSicaeT2VZqt8l-nJzXcGWQ9nFZxdzlT_XA-44hS-o8u1OrIvw_OlFyBd1KFAC5KBCu4SskkQWqg9iAH3Rn33mVGJqf8hcuSVKTYQ3YFyD8yvTmZOHYFnPW7Yc7Ecrg0-s4yXL._4s3xxrYlVU-0LPaIB6Auw",
"TokenType": "Bearer"
},
"ChallengeParameters": {}
}
解開IdToken之后,看看里面的iat
和exp
,可以發現IdToken的過期時間是一個小時。
Identity Pool登錄成功之后得到的信息如下所示。看起來就是STS token啊。
{
"Credentials": {
"AccessKeyId": "ASIAI46U752A4IDXV2TA",
"Expiration": 1498235294,
"SecretKey": "rj6Kd07s1lbTpRNOXBNdzxxCY56G2jRN26yddHcW",
"SessionToken": "AgoGb3JpZ2luEIP//////////wEaDmFwLXNvdXRoZWFzdC0yIoACNsPwXYx/zmX42sN3HEEHw/cY0yzELJdHTsF+C+HpUnbklUOmxTvgWIQ8/rVnUGWewsSrazIA5idbm4Chb2OAaPDKlGuAfq/ovZZS2HIhMWFBPnWzFggOsDlqe73QVhw96RW8t8dACTuoNEZjAlXPIPk90oZXHRctEiPr/vrzJYWy5M39RxgY3lxKAe3cosbIJhSARxIxuaudueYdC96xxQXSjQMn7sA5VGsjnU9hytgni4sDR0ozFUlfKi7OhuylKTiB5zIWaMmDnyt+SKfoC506RNPH/QNu3PLLqSUgVG6yMov0Eydub6T66lZgkDDYTmwmgKjze0Pd9aVw0qrapyrdBAjZ//////////8BEAAaDDk3ODM0MzM3MDU3NyIMMF7ptJyDwDX5/6gSKrEE3beePMNROAKR1bB54f+xnvo8ZUryymf430PkKHC3Q0oW1+eeBi44F1raX/Bv4tlq6WWK3RlrFl+gPNLeZC/b9xcyJI2q4luMBwYAbqXHKYJxwzlIhXHGjqxf27/01X9ksK3X8nove2y51TK84MHzxQjm4ABQfE2873mSS1YIbTgOBV7jDYn91pal4lXIrUilQIw1GUQ5m+SSHKfbpzwZpWTbnYesrXfWRd59pYaQ6yntat1/La/mZG5i/Dbk+HUOieyAGffMt5OICB75JpUkRjyMo8qNkdc4/nsEvMNTLl1LQdhVL6zmkKdztNZBbn76Uz1cr7u1sOJLCqHKY5P2uNuGvA11YaJN9MGLJwI+VNSGiDD7hT8Sor2bMHd3r9hy+SGAUfyhjPtch5D8tHwYTT/1GyBLyN2ws7ym/dYxHJ+4gpjK78J7VCjdR1BN5amtQTgzrZ9kjS7SkhVPVLxNJK3+GTk6M5asitd1V/BRwpBHtS1LCFqYAGdlOADVl+TKgo15OmATY9aKmwUoYQpZKV9cvk2ferOGCQwfv92F5QSqxGibb0+8/rJF4CIfpneXp2YLRu07FubicyhLa+pWuMJ5IwC2b6pbZho212PIjuuBrlH+TDEKNF6aC7f4RRnk87A/Chsgq32KxUZVgt5MtOr3LPrMmU1hB28yqb0ofabMq14o3vAgY3+SPa5NBWxQxhO/YN+vB9Y4J+I+p0dxb7amWQNzJLuCURUoK8RT0qfbMI7ntMoF"
},
"IdentityId": "ap-southeast-2:65d024cf-f342-4c5d-8ece-7d1736d24633"
}
IAM
User Pool和Identity Pool怎么跟AWS IAM關聯起來呢?那就是配置role。User Pool可以創建group,然后把用戶加入到group里面,而group可以設置role。詳細的描述請看文檔:Assigning IAM Roles to Groups。
Identity Pool同樣可以創建和指定角色。
這些角色在AWS IAM里面都可以找到。但是User Pool和Identity Pool自己并不會出現在IAM的用戶和組里面。
我有一個疑問是,如果一個賬號在User Pool里面的角色是A,然而Identity Pool使用角色是B,那么我在Identity Pool里面登錄了這個賬號,那么他對應的角色是A還是B呢?還是擁有兩個角色所屬權限的交集或并集?
最后的總結
User Pool貌似是配合AWS API Gateway一起使用的。畢竟OAuth/OIDC這套東西非常成熟和標準,適用于HTTP協議下。
A user pool is integrated with an API as a method authorizer that
is applicable for any method. When calling the methods with such
an authorizer enabled, an API client includes in the request
headers the user's identity token provisioned from the user pool.
API Gateway then validates the token to ensure it belongs to the
configured user pool and authenticates the caller before passing
the request to the backend.
To integrate an API with the Amazon Cognito identity provider,
you, as an API developer, create and own a user pool, create an
API Gateway authorizer connected to the user pool, and enable
the authorizer on selected API methods. You must also distribute
to your API client developers the user pool ID, a client ID, and
possibly the associated client secret that are provisioned from the
user pool. The client will need this information to register users
with the user pool, to provide the sign-in functionality, and to have
the user's identity token provisioned from the user pool.
接著去AWS API Gateway里面看看。我發現在Authorizers里面可以指定使用Cognito User Pool Authorizer
。
Identity Pool給的就是STS token,所以可以直接拿去訪問AWS Service。
Amazon Cognito Federated Identities enable you to create unique
identities for your users and federate them with identity providers.
With an identity, you can obtain temporary, limited-privilege AWS
credentials to synchronize data with Amazon Cognito Sync, or
directly access other AWS services.
使用API Gateway
User Pool的id token可以用來調用API Gateway的API。API Gateway可以設置custom authorizer,這是一個User Pool。如果一個id token對應的用戶屬于這個User Pool,那么拿著id token就可以訪問API。AWS也支持通過Identity Pool的方式來調用API Gateway,這樣做會麻煩一些,需要使用API Gateway生成的接口SDK,配合相應的IAM role,才能調用成功。
首先在API Gateway復制PetStore這個API。
在
Authorizers
里面創建一個Cognito User Pool Authorizer
,選擇好region和User Pool。
- 設置好之后,可以驗證一下id token。id token可以從
CognitoYourUserPools-Sample
demo的日志里面獲取。如果id token這個用戶不屬于wla-demo
這個User Pool,那么驗證會失敗。
- 對
pets
的GET接口設置Authorization,選擇上面創建好的custom authorizer。
- 發布接口之后。在Postman里面做測試。HTTP Header里面設置
Authorization
為id token即可。如果只是設置一下HTTP Header,通過curl也可以做到,哈哈,用Postman牛刀殺雞了。
- 如果不設置
Authorization
,那么會提示如下錯誤。
對于整個流程下面幾張圖也有比較好的闡述。
最后簡單總結一下吧。我覺得User Pool是一個完整的用戶系統,實現了OAuth 2.0,并且支持SAML做SSO。User Pool可以跟API Gateway聯動起來,方便用戶做一個完整的Serverless應用。而Identity Pool則支持主流的ID Provider,包括User Pool,用戶認證之后頒發STS token,用于訪問AWS自己的服務。在AWS服務的歸類中,Cognito屬于移動服務
這個類目下面。Cognito+API Gateway+Lambda function=Serverless,有這一套體系,加上AWS移動相關的SDK和服務,寫一個App是一件非常愜意的事情。