AWS Cognito提供了身份验证授权服务以及用户管理服务。

  • 用户池 (中国区目前不支持)
  • 身份池

App以第三方身份登录以后,可以通过Cognito identity pool以assume role的方式获取访问AWS资源的临时身份,应用端根据这个临时身份访问所需要的AWS资源。

架构图

image.png

  1. App从微信登录
  2. 请求经过API Gateway,从Lambda发起微信身份验证,并获取openid
  3. 查找dynamodb是否存在openid对应的identity id
    1. 如果存在,获取identity id并从cognito获取token
    2. 如果不存在,调用GetOpenIdTokenForDeveloperIdentity创建identity id以及获取token并保存identity id到dynamodb里
  4. 通过第3步中获取的token调用STS获取临时权限
  5. 通过第4步中获取的临时权限去访问AWS内部资源

创建身份池

这里需要注意的是身份验证选Custom,我们并不能用Facebook或者Google登录,目前也不支持微信,QQ,支付宝登录。
image.png

通过用户ID获取Token

在AWS环境中运行(Lambda+API Gateway)

首先需要从第三方身份登录服务商那边获取到用户的唯一标识,如微信的UnionID或者OpenID,这里假设我们已经拿到了这个OpenID

为这个OpenID在身份池里创建相对应的Identity。AWS提供GetOpenIdTokenForDeveloperIdentity这个API来创建新的Identity或者绑定用户到到已存在的Identity上。

这里需要注意的是,因为目前Cognito不支持国内主流的三方认证方式,所以选择经过开发人员验证的身份,也就是第一步设置的自定义身份提供商(login.wechat.pocapp)

用户信息可以存在dynamodb

  1. import boto3
  2. client = boto3.client('cognito-identity')
  3. def get_token_for_user(user_id):
  4. response = client.get_open_id_token_for_developer_identity(
  5. IdentityPoolId='cn-north-1:xxxxxx-identity-poolId-in-step-1',
  6. Logins={
  7. 'login.wechat.pocapp': 'user_id'
  8. },
  9. TokenDuration=120
  10. )
  11. print(response)
  12. return response
  13. get_token_for_user('test-user-1')

API返回:

  1. {
  2. 'IdentityId': 'string',
  3. 'Token': 'string'
  4. }

从identity browser里可以看到新的identity已经创建好了

image.png

通过Token从STS获取临时身份

在AWS环境中运行(Lambda+API Gateway)

我们可以使用AssumeRoleWithWebIdentity这个API来获取扮演这个角色的临时身份。
调用这个方法我们需要找到在创建身份池时候绑定的角色的ARN以及上一步获取到的Token。

  1. sts_client = boto3.client('sts')
  2. def get_sts_credential(token_response):
  3. sts_response = sts_client.assume_role_with_web_identity(
  4. RoleArn='arn:aws-cn:iam::your-account-id:role/Cognito_WeChatAppPOCAuth_Role',
  5. RoleSessionName='test-session-1',
  6. WebIdentityToken=token_response['Token'],
  7. DurationSeconds=900
  8. )
  9. print(sts_response)
  10. return sts_response
  11. get_sts_credential(get_token_for_user('test-user-1'))

API返回:

  1. {
  2. 'Credentials': {
  3. 'AccessKeyId': 'string',
  4. 'SecretAccessKey': 'string',
  5. 'SessionToken': 'string',
  6. 'Expiration': datetime(2015, 1, 1)
  7. },
  8. 'SubjectFromWebIdentityToken': 'string',
  9. 'AssumedRoleUser': {
  10. 'AssumedRoleId': 'string',
  11. 'Arn': 'string'
  12. },
  13. 'PackedPolicySize': 123,
  14. 'Provider': 'string',
  15. 'Audience': 'string'
  16. }

测试临时权限访问S3服务

应用端运行(Android/IOS)

从上一步中获取到的临时权限中创建一个会话,然后由这个会话再去创建S3的Client,最后打印出有权限的S3桶。

  1. def test_s3(sts_credentials):
  2. credential = sts_credentials['Credentials']
  3. session = boto3.Session(
  4. aws_access_key_id=credential['AccessKeyId'],
  5. aws_secret_access_key=credential['SecretAccessKey'],
  6. aws_session_token=credential['SessionToken'],
  7. )
  8. s3_client = session.client('s3')
  9. response = s3_client.list_buckets()
  10. print(response)
  11. test_s3(get_sts_credential(get_token_for_user('test-user-1')))

因为我创建的Cognito角色并没有给访问S3的策略,所以返回了个Access Denied
image.png

Github Source Code