【问题标题】:AWS Cognito Authorization code grant flow without using the hosted UI不使用托管 UI 的 AWS Cognito 授权代码授权流程
【发布时间】:2018-08-07 07:14:32
【问题描述】:

在没有托管 UI 的情况下使用 AWS 的 Cognito,给定用户名和密码,我希望在不使用托管 UI 的情况下接收授权代码授权。

这可能吗?我正在编写自己的注册、登录表格,但似乎找不到有关此主题的文档。

目前我可以使用 AWS.CognitoIdentityServiceProvider 和initialAuth 函数来交换用户名密码以获得令牌,但我不想在重定向 URL 中返回这些令牌,我宁愿返回一个可以交换令牌的授权代码授权。

那么我怎样才能使用initiateAuth接收授权码授权而不是令牌。

更新:使用下面的正确答案,这就是我最终在节点中执行此操作的方式。

const base64url = require('base64url');
const crypto = require('crypto');
const request = require('request');
const querystring = require ('querystring');

function generateCodeVerifierHash(code_verifier) {
  return crypto.createHmac('SHA256', code_verifier)
  .digest('base64');
}

function generateCodeVerifier() {
  var text = "";
  var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._`-";

  for (var i = 0; i < 64; i++)
    text += possible.charAt(Math.floor(Math.random() * possible.length));

  return base64url.encode(text);
}

var CLIENT_ID="Your Client Id";
var CLIENT_SECRET="Your Client Secret";
var RESPONSE_TYPE="code";
var REDIRECT_URI= encodeURIComponent("Your Redirect Url");
var SCOPE="openid";
var AUTH_DOMAIN= "Your Cognito Auth Domain";
var USERNAME="User's Username";
var PASSWORD="User's Password";
var CODE_CHALLENGE_METHOD="S256";

// Challenge
var code_verifier = generateCodeVerifier();
var code_challenge = generateCodeVerifierHash(code_verifier);

// Get CSRF token from /oauth2/authorize endpoint
var csrfRequestUrl = `https://${AUTH_DOMAIN}/oauth2/authorize?response_type=${RESPONSE_TYPE}&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&scope=${SCOPE}&code_challenge_method=${CODE_CHALLENGE_METHOD}&code_challenge=${code_challenge}`;
// Post CSRF Token and username/password to /login endpoint
var codeRequestUrl = `https://${AUTH_DOMAIN}/login?response_type=${RESPONSE_TYPE}&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}`;

request.get(csrfRequestUrl, (err, res, body) => {
    var XSRFTOKEN = res.headers['set-cookie'].filter( (header) => header.substring(0,10) == 'XSRF-TOKEN')[0];

    form = {
      '_csrf': `${XSRFTOKEN.split(';')[0].split('=')[1]}`,
      'username': `${USERNAME}`,
      'password': `${PASSWORD}`,
    }

    var formData = querystring.stringify(form);
    var contentLength = formData.length;

    request({
      headers: {
        'Content-Length': contentLength,
        'Content-Type': 'application/x-www-form-urlencoded',
        'Cookie': `${XSRFTOKEN}`,
      },
      uri: codeRequestUrl,
      body: formData,
      method: 'POST'
    }, function (err, res, body) {
      var authorizationCodeGrant = res.headers.location.split('=')[1];
      console.log(authorizationCodeGrant);
    });
});

【问题讨论】:

    标签: amazon-web-services amazon-cognito aws-cognito


    【解决方案1】:

    使用initialAuth 和respondToAuthChallenge 无法做到这一点,因为这仅涉及对您的用户池的身份验证,最终结果将是您将获得令牌。

    但是,完全可以通过代码与授权、令牌和登录端点进行交互。如果您查看以下 shell 脚本代码,您将了解需要传递到端点的信息:

    #!/usr/bin/env bash
    
    #===============================================================================
    # SET AUTH DOMAIN
    #===============================================================================
    AUTH_DOMAIN="MY-DOMAIN.auth.REGION.amazoncognito.com"
    
    #===============================================================================
    # AUTH CODE/IMPLICIT GRANTS, WITHOUT PKCE, WITHOUT CLIENT SECRET
    #===============================================================================
    
    ## Set constants ##
    CLIENT_ID="USER_POOL_CLIENT_ID"
    RESPONSE_TYPE="code"
    #RESPONSE_TYPE="token"
    REDIRECT_URI="https://example.com/"
    SCOPE="openid"
    
    USERNAME="testuser"
    PASSWORD="testpassword"
    
    ## Get CSRF token and LOGIN URL from /oauth2/authorize endpoint ##
    curl_response="$(
        curl -qv "https://${AUTH_DOMAIN}/oauth2/authorize?response_type=${RESPONSE_TYPE}&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&scope=${SCOPE}" 2>&1
    )"
    curl_redirect="$(printf "%s" "$curl_response" \
                        | awk '/^< Location: / {
                            gsub(/\r/, ""); # Remove carriage returns
                            print $3;       # Print redirect URL
                        }')"
    csrf_token="$(printf "%s" "$curl_response" \
                       | awk '/^< Set-Cookie:/ {
                           gsub(/^XSRF-TOKEN=|;$/, "", $3); # Remove cookie name and semi-colon
                           print $3;                        # Print cookie value
                        }')"
    
    ## Get auth code or tokens from /login endpoint ##
    curl_response="$(
        curl -qv "$curl_redirect" \
            -H "Cookie: XSRF-TOKEN=${csrf_token}; Path=/; Secure; HttpOnly" \
            -d "_csrf=${csrf_token}" \
            -d "username=${USERNAME}" \
            -d "password=${PASSWORD}" 2>&1
    )"
    curl_redirect="$(printf "%s" "$curl_response" \
                        | awk '/^< Location: / {
                            gsub(/\r/, ""); # Remove carriage returns
                            print $3;       # Print redirect URL
                        }')"
    auth_code="$(printf "%s" "$curl_redirect" \
                    | awk '{
                        sub(/.*code=/, ""); # Remove everything before auth code
                        print;              # Print auth code
                    }')"
    
    ## Get tokens from /oauth2/token endpoint ##
    GRANT_TYPE="authorization_code"
    curl "https://${AUTH_DOMAIN}/oauth2/token" \
        -d "grant_type=${GRANT_TYPE}" \
        -d "client_id=${CLIENT_ID}" \
        -d "code=${auth_code}" \
        -d "redirect_uri=${REDIRECT_URI}"
    
    #===============================================================================
    # AUTH CODE/IMPLICIT GRANTS, WITH PKCE, WITH CLIENT SECRET
    #===============================================================================
    
    ## Set constants ##
    CLIENT_ID="USER_POOL_CLIENT_ID"
    CLIENT_SECRET="USER_POOL_CLIENT_SECRET"
    RESPONSE_TYPE="code"
    #RESPONSE_TYPE="token"
    REDIRECT_URI="https://example.com/"
    SCOPE="openid"
    
    USERNAME="testuser"
    PASSWORD="testpassword"
    
    ## Create a code_verifier and code_challenge ##
    CODE_CHALLENGE_METHOD="S256"
    # code_verifier = random, 64-char string consisting of chars between letters,
    #                 numbers, periods, underscores, tildes, or hyphens; the string
    #                 is then base64-url encoded
    code_verifier="$(cat /dev/urandom \
                        | tr -dc 'a-zA-Z0-9._~-' \
                        | fold -w 64 \
                        | head -n 1 \
                        | base64 \
                        | tr '+/' '-_' \
                        | tr -d '='
                    )"
    # code_challenge = SHA-256 hash of the code_verifier; it is then base64-url
    #                  encoded
    code_challenge="$(printf "$code_verifier" \
                        | openssl dgst -sha256 -binary \
                        | base64 \
                        | tr '+/' '-_' \
                        | tr -d '='
                    )"
    
    ## Get CSRF token from /oauth2/authorize endpoint ##
    curl_response="$(
        curl -qv "https://${AUTH_DOMAIN}/oauth2/authorize?response_type=${RESPONSE_TYPE}&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&scope=${SCOPE}&code_challenge_method=${CODE_CHALLENGE_METHOD}&code_challenge=${code_challenge}" 2>&1
    )"
    curl_redirect="$(printf "%s" "$curl_response" \
                        | awk '/^< Location: / {
                            gsub(/\r/, ""); # Remove carriage returns
                            print $3;       # Print redirect URL
                        }')"
    csrf_token="$(printf "%s" "$curl_response" \
                    | awk '/^< Set-Cookie:/ {
                        gsub(/^XSRF-TOKEN=|;$/, "", $3); # Remove cookie name and semi-colon
                        print $3;                        # Print cookie value
                    }')"
    
    ## Get auth code or tokens from /login endpoint ##
    curl_response="$(
        curl -qv "$curl_redirect" \
            -H "Cookie: XSRF-TOKEN=${csrf_token}; Path=/; Secure; HttpOnly" \
            -d "_csrf=${csrf_token}" \
            -d "username=${USERNAME}" \
            -d "password=${PASSWORD}" 2>&1
    )"
    curl_redirect="$(printf "%s" "$curl_response" \
                        | awk '/^< Location: / {
                            gsub(/\r/, ""); # Remove carriage returns
                            print $3;       # Print redirect URL
                        }'
                    )"
    auth_code="$(printf "%s" "$curl_redirect" \
                    | awk '{
                        sub(/.*code=/, ""); # Remove everything before auth code
                        print;              # Print auth code
                    }')"
    
    ## Get tokens from /oauth2/token endpoint ##
    authorization="$(printf "${CLIENT_ID}:${CLIENT_SECRET}" \
                        | base64 \
                        | tr -d "\n" # Remove line feed
                    )"
    GRANT_TYPE="authorization_code"
    curl "https://${AUTH_DOMAIN}/oauth2/token" \
        -H "Authorization: Basic ${authorization}" \
        -d "grant_type=${GRANT_TYPE}" \
        -d "client_id=${CLIENT_ID}" \
        -d "code=${auth_code}" \
        -d "redirect_uri=${REDIRECT_URI}" \
        -d "code_verifier=${code_verifier}"
    
    #===============================================================================
    # CLIENT CREDENTIALS GRANT
    #===============================================================================
    
    ## Set constants ##
    CLIENT_ID="USER_POOL_CLIENT_ID"
    CLIENT_SECRET="USER_POOL_CLIENT_SECRET"
    GRANT_TYPE="client_credentials"
    
    ## Get access token from /oauth2/token endpoint ##
    authorization="$(printf "${CLIENT_ID}:${CLIENT_SECRET}" \
                        | base64 \
                        | tr -d "\n" # Remove line feed
                    )"
    curl "https://${AUTH_DOMAIN}/oauth2/token" \
        -H "Authorization: Basic ${authorization}" \
        -d "grant_type=${GRANT_TYPE}"
    
    #===============================================================================
    # LOGOUT
    #===============================================================================
    
    ## Set constants ##
    CLIENT_ID="USER_POOL_CLIENT_ID"
    REDIRECT_URI="https://example.com/"
    
    ## Hit /logout endpoint ##
    curl -v "https://${AUTH_DOMAIN}/logout?client_id=${CLIENT_ID}&logout_uri=${REDIRECT_URI}"
    

    【讨论】:

    • Cognito 最近添加了对自定义域的支持,因此您不再需要这样做:docs.aws.amazon.com/cognito/latest/developerguide/…
    • 效果很好!似乎来自oauth2/authorize 的响应现在返回了两个 Set-Cookie 标头,所以我不得不对脚本进行一次改编。 | awk '/^&lt; Set-Cookie:/ { -> | awk '/^&lt; Set-Cookie: XSRF-TOKEN=/ {
    • @IonutTrestian,仍然无法通过托管您自己的 UI 获得授权码。
    • @miyasudokoro 遇到了同样的问题,如果我们托管自己的 UI,有没有办法获得授权码?找不到任何关于放大的文档,signIn API 不断返回所有令牌而不是代码
    猜你喜欢
    • 2021-12-31
    • 2020-06-12
    • 2019-08-02
    • 2020-10-15
    • 2014-11-04
    • 2016-10-15
    • 2019-12-12
    • 2020-09-23
    • 2020-11-25
    相关资源
    最近更新 更多