不使用托管 UI 的 AWS Cognito 授权代码授予流程

AWS Cognito Authorization code grant flow without using the hosted UI

在没有托管 UI 的情况下使用 AWS 的 Cognito,给定用户名和密码我想在不使用托管 ui.

的情况下获得授权码授予

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

目前我可以使用 AWS.CognitoIdentityServiceProvider 和 initiateAuth 函数来交换令牌的用户名密码,但我不想 return 重定向中的那些令牌 URL,我宁愿return授权码授予,可兑换令牌。

那么我如何使用 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);
    });
});

没有办法通过使用 initiateAuth 和 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 ;       # Print redirect URL
                    }')"
csrf_token="$(printf "%s" "$curl_response" \
                   | awk '/^< Set-Cookie:/ {
                       gsub(/^XSRF-TOKEN=|;$/, "", ); # Remove cookie name and semi-colon
                       print ;                        # 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 ;       # 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 ;       # Print redirect URL
                    }')"
csrf_token="$(printf "%s" "$curl_response" \
                | awk '/^< Set-Cookie:/ {
                    gsub(/^XSRF-TOKEN=|;$/, "", ); # Remove cookie name and semi-colon
                    print ;                        # 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 ;       # 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}"