AWS Cognito 如何验证我自己的 IdP 颁发的身份令牌?

Posted

技术标签:

【中文标题】AWS Cognito 如何验证我自己的 IdP 颁发的身份令牌?【英文标题】:How AWS Cognito verify the identity token issued by my own IdP? 【发布时间】:2021-03-01 20:53:08 【问题描述】:

我有一个由移动应用和后端 (Django) 组成的系统,当前的身份验证工作流程:

移动应用使用我的身份验证登录并接收 JWT 服务 移动应用会随每个请求将 JWT 发送到 REST API

现在我想允许移动应用直接访问 aws 资源 (s3..etc) 而不是通过后端,因此所需的工作流程是:

但我不想使用亚马逊作为 idP(如上图),而是想创建自己的 idP,我的问题是:

当前身份验证工作流中的 JWT 令牌是否成为 openid connect 的身份令牌或访问令牌 授权码工作流程? Cognito 如何验证身份令牌是从同一 已配置的 idP(第 3 步)? 或者我应该分开普通用户认证(当前认证 wokflow)来自aws凭证身份验证工作流程?怎么样?

【问题讨论】:

【参考方案1】:

好问题,虽然我会以不同的方式绘制你的图表,就目标架构而言,因为这里有很多概念需要分开:

关键点

您的应用只与授权服务器 (AS)(在您的情况下为 Cognito)对话,并且只接收 Cognito 令牌。您可以控制 Cognito 行为,例如令牌声明和生命周期。

身份提供程序用于登录 - 这些可能是 Google 登录、基于 SAML 或基于 OIDC。 AS 负责向您的应用程序发布相同的 Cognito 令牌,而不考虑用于最终用户登录的 IDP。我的 Federated Logins 博客文章对此有更多信息。

AWS 资源是外部 API,您必须向它们提供所需的令牌。这涉及一种代币交换形式,在 Cognito 中,我相信这涉及使用Identity Pool。

我建议通过您自己的 API 进行令牌交换,因为从安全角度来看它会更干净,而不会将 DynamoDB 等 AWS 资源直接暴露给互联网。此外,如果令牌交换中涉及任何秘密,API 可以安全地发送它们,而移动应用程序则不能。

行业标准是只向 API 发送访问令牌而不是 id 令牌。 AWS 总是有可能违反此规则,但如果可以,请发送访问令牌。

如果您的应用通过授权码流 (PKCE) 实现推荐的移动流 OIDC,那么它自然会支持多次登录。然后其他部分应该相对简单地叠加在上面。如果有帮助,我的博客有一个 couple of sample mobile apps 可以运行,它们都以这种方式连接到 Cognito。

【讨论】:

感谢您的回答,但是,我有我的授权服务器,我不想使用 cognito 的用户池,我只想使用身份池。同样对于代币交换,这不是aws STS服务的责任吗?【参考方案2】:

关于 cognito 如何验证 id 令牌的部分答案,取自here:

    iss 参数必须与登录映射中使用的键匹配(例如 login.provider.com)。 签名必须有效。签名必须可通过 RSA 公钥。 托管公钥的证书的指纹匹配 您的 OpenId Connect Provider 上配置了什么。 如果存在 azp 参数,请对照列出的值检查此值 您的 OpenId Connect 提供程序中的客户端 ID。 如果 azp 参数不存在,请对照 aud 参数检查 您的 OpenId Connect 提供程序中列出的客户端 ID

【讨论】:

【参考方案3】:

我已经为我的身份验证服务实现了 Auth0 以接收 JWT 令牌并将令牌交换为 aws 临时凭证。但我决定将我的身份验证基础设施迁移到 Cognito 用户池以简化我的开发。

让我分享一下我所做的一般概念。我认为这将与您的情况相似:

    我已经有 auth0 客户端

    Obtaining the root CA thumbprint for an OpenID Connect Identity Provider。 我创建了自己的脚本来使用 NodeJS + Bash 生成证书指纹:

    // getCert.js
    
    var file=require("fs");
    var certs=file.readFileSync("./cert",'utf8');
    certs=certs.replace(/BEGIN CERTIFICATE-----\n/g,'BEGIN#####');
    certs=certs.replace(/\n-----END CERTIFICATE/g,'#####END');
    var allCerts=certs.match(/BEGIN#####([\s\S]*?)#####END/g);
    var lastCert=allCerts[allCerts.length-1].replace(/BEGIN#####/g,'-----BEGIN CERTIFICATE-----\n');
    lastCert=lastCert.replace(/#####END/g,'\n-----END CERTIFICATE-----');
    file.writeFileSync("./cert", lastCert, "utf8");
    
    # thumbprint.sh
    echo "Creating thumbprint certificate..."
    openssl s_client -showcerts -connect <AUTH0_CLIENT_DOMAIN>:<PORT> > cert &
    PID=$!
    wait $PID
    sleep 1
    node thumbprint/getCert.js
    preSHA="`openssl x509 -in cert -fingerprint -noout`"
    preSHA="$preSHA/SHA1 Fingerprint=/"
    echo $preSHA | sed -e "s/://g" > cert
    

    我去了 IAM 身份提供者,并使用创建的证书指纹将我的 Auth0 客户端注册为 OpenID 提供者

    使用 IAM 角色策略为经过身份验证和未经身份验证的角色创建新的身份池

    在我的身份池中,我通过放置创建的 openid ARN 和 Auth0 客户端域来设置身份验证提供程序以使用 OpenID Connect

    好了,我们可以通过下面的示例代码测试代币兑换:

    let url = <auth0_client_domain>;
    let cognitoParam = 
      'IdentityPoolId': <the_id_of_identity_pool>,
      'Logins': 
    ;
    cognitoParam.Logins[url] = <jwt_idToken>;
    AWS.config.credentials=new AWS.CognitoIdentityCredentials(cognitoParam);
    let gp=AWS.config.credentials.getPromise();
    gp.then(()=>
      console.log('getCredentials done: ',AWS.config.credentials.identityId)
    ).catch((err)=>
      console.log("Error login : ", err.message);
    )
    

【讨论】:

以上是关于AWS Cognito 如何验证我自己的 IdP 颁发的身份令牌?的主要内容,如果未能解决你的问题,请参考以下文章

API 无法识别向 AWS Boto3 Cognito-idp 传递参数

Cognito缺乏IDP发起的SSO

Lambda 无权执行:cognito-idp:AdminInitiateAuth

如何使用 cloudformation 在 AWS cognito 上设置验证属性?

如何在 Go 中验证来自 AWS Cognito 的 JWT 令牌?

如何在 NodeJS 上验证 AWS Cognito 访问令牌