태그 보관물: amazon-ecs

amazon-ecs

API 백엔드에서 AWS Cognito의 JWT를 확인하는 방법은 무엇입니까? 내 서버 (또는

Angular2 단일 페이지 앱과 ECS에서 실행되는 REST API로 구성된 시스템을 구축하고 있습니다. API는 .Net / Nancy 에서 실행 되지만 변경 될 수 있습니다.

Cognito를 사용해보고 싶습니다. 이것이 인증 워크 플로를 상상 한 방법입니다.

  1. SPA가 사용자를 로그인하고 JWT를받습니다.
  2. SPA는 모든 요청과 함께 JWT를 REST API로 보냅니다.
  3. REST API는 JWT가 인증되었는지 확인합니다.

내 질문은 3 단계에 관한 것입니다. 내 서버 (또는 오히려 상태 비 저장, 자동 확장,로드 밸런싱 된 Docker 컨테이너)가 토큰이 인증되었는지 확인할 수 있습니까? “서버”는 JWT 자체를 발행하지 않았기 때문에 자체 보안 비밀을 사용할 수 없습니다 ( 여기 의 기본 JWT 예제에 설명 됨 ).

Cognito 문서를 읽고 많이 봤지만 서버 측에서 JWT로 무엇을해야하는지에 대한 좋은 지침을 찾을 수 없습니다.



답변

내가 문서를 제대로 읽지 않은 것으로 밝혀졌습니다. 여기에 설명되어 있습니다 ( “웹 API에서 ID 토큰 및 액세스 토큰 사용”으로 스크롤).

API 서비스는 Cognito의 암호를 다운로드하고이를 사용하여 수신 된 JWT를 확인할 수 있습니다. 완전한.

편집하다

Groady의 코멘트 @ 포인트에 :하지만 어떻게 당신은 토큰의 유효성을 검사합니까? 이를 위해 jose4j 또는 nimbus (둘 다 Java) 와 같은 전투 테스트를 거친 라이브러리를 사용하고 처음부터 확인을 구현하지 마십시오.

다음 은 최근에 java / dropwizard 서비스에서 구현해야 할 때 시작된 nimbus를 사용하는 Spring Boot의 구현 예입니다.


답변

NodeJS에서 서명을 확인하는 방법은 다음과 같습니다.

var jwt = require('jsonwebtoken');
var jwkToPem = require('jwk-to-pem');
var pem = jwkToPem(jwk);
jwt.verify(token, pem, function(err, decoded) {
  console.log(decoded)
});


// Note : You can get jwk from https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json

답변

인증 코드 부여 흐름 실행

다음과 같이 가정합니다.

  • AWS Cognito에서 사용자 풀을 올바르게 구성하고
  • 다음을 통해 가입 / 로그인하고 액세스 코드를 얻을 수 있습니다.

    https://<your-domain>.auth.us-west-2.amazoncognito.com/login?response_type=code&client_id=<your-client-id>&redirect_uri=<your-redirect-uri>
    

브라우저가 다음으로 리디렉션되어야합니다. <your-redirect-uri>?code=4dd94e4f-3323-471e-af0f-dc52a8fe98a0


이제 해당 코드를 백엔드로 전달하고 토큰을 요청해야합니다.

POST https://<your-domain>.auth.us-west-2.amazoncognito.com/oauth2/token

  • 사용자의 설정 Authorization에 헤더를 Basic사용 username=<app client id>하고 password=<app client secret>AWS Cognito에 구성된 앱 클라이언트 당
  • 요청 본문에 다음을 설정하십시오.
    • grant_type=authorization_code
    • code=<your-code>
    • client_id=<your-client-id>
    • redirect_uri=<your-redirect-uri>

성공하면 백엔드가 base64로 인코딩 된 토큰 세트를 수신해야합니다.

{
    id_token: '...',
    access_token: '...',
    refresh_token: '...',
    expires_in: 3600,
    token_type: 'Bearer'
}

이제 문서 에 따르면 백엔드는 다음을 통해 JWT 서명을 검증해야합니다.

  1. ID 토큰 디코딩
  2. 로컬 키 ID (아이)와 공개 키드 비교
  3. 공개 키를 사용하여 JWT 라이브러리를 사용하여 서명을 확인합니다.

AWS Cognito는 각 사용자 풀에 대해 두 쌍의 RSA 암호화 키를 생성하므로 토큰을 암호화하는 데 사용 된 키를 파악해야합니다.

다음 은 JWT 확인을 보여주는 NodeJS 스 니펫입니다.

import jsonwebtoken from 'jsonwebtoken'
import jwkToPem from 'jwk-to-pem'

const jsonWebKeys = [  // from https://cognito-idp.us-west-2.amazonaws.com/<UserPoolId>/.well-known/jwks.json
    {
        "alg": "RS256",
        "e": "AQAB",
        "kid": "ABCDEFGHIJKLMNOPabc/1A2B3CZ5x6y7MA56Cy+6ubf=",
        "kty": "RSA",
        "n": "...",
        "use": "sig"
    },
    {
        "alg": "RS256",
        "e": "AQAB",
        "kid": "XYZAAAAAAAAAAAAAAA/1A2B3CZ5x6y7MA56Cy+6abc=",
        "kty": "RSA",
        "n": "...",
        "use": "sig"
    }
]

function validateToken(token) {
    const header = decodeTokenHeader(token);  // {"kid":"XYZAAAAAAAAAAAAAAA/1A2B3CZ5x6y7MA56Cy+6abc=", "alg": "RS256"}
    const jsonWebKey = getJsonWebKeyWithKID(header.kid);
    verifyJsonWebTokenSignature(token, jsonWebKey, (err, decodedToken) => {
        if (err) {
            console.error(err);
        } else {
            console.log(decodedToken);
        }
    })
}

function decodeTokenHeader(token) {
    const [headerEncoded] = token.split('.');
    const buff = new Buffer(headerEncoded, 'base64');
    const text = buff.toString('ascii');
    return JSON.parse(text);
}

function getJsonWebKeyWithKID(kid) {
    for (let jwk of jsonWebKeys) {
        if (jwk.kid === kid) {
            return jwk;
        }
    }
    return null
}

function verifyJsonWebTokenSignature(token, jsonWebKey, clbk) {
    const pem = jwkToPem(jsonWebKey);
    jsonwebtoken.verify(token, pem, {algorithms: ['RS256']}, (err, decodedToken) => clbk(err, decodedToken))
}


validateToken('xxxxxxxxx.XXXXXXXX.xxxxxxxx')

답변

비슷한 문제가 있었지만 API 게이트웨이를 사용하지 않았습니다. 제 경우에는 AWS Cognito Developer Authenticated identity route를 통해 얻은 JWT 토큰의 서명을 확인하고 싶었습니다.

여러 사이트의 많은 포스터와 마찬가지로 외부에서 즉, 서버 측 또는 스크립트를 통해 AWS JWT 토큰의 서명을 확인하는 데 필요한 비트를 정확히 맞추는 데 문제가있었습니다.

AWS JWT 토큰 서명확인 하고 요점을 파악한 것 같습니다 . PyCrypto의 Crypto.Signature에서 pyjwt 또는 PKCS1_v1_5c를 사용하여 AWS JWT / JWS 토큰을 확인합니다.

네, 제 경우에는 파이썬 이었지만 노드에서도 쉽게 수행 할 수 있습니다 (npm install jsonwebtoken jwk-to-pem 요청).

나는 이것을 알아 내려고 할 때 나는 대부분 옳은 일을하고 있었지만 파이썬 딕셔너리 순서와 같은 뉘앙스가 있거나, 거기에 json 표현이 부족했기 때문에 주석에서 몇 가지 문제를 강조하려고했습니다.

바라건대 누군가 어딘가에 도움이 될 수 있습니다.


답변

짧은 대답 :

다음 끝점에서 사용자 풀에 대한 공개 키를 얻을 수 있습니다.이 공개 키를
https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json

사용하여 토큰을 성공적으로 디코딩하면 토큰이 유효하지 않으면 위조됩니다.

긴 답변 :

코크 릿을 통해 성공적으로 인증하면 액세스 및 ID 토큰을받습니다. 이제이 토큰이 변조되었는지 여부를 확인하려고합니다. 전통적으로 우리는 토큰이 유효한지 확인하기 위해 이러한 토큰을 인증 서비스 (처음에이 토큰을 발행 한)로 다시 보냅니다. 이러한 시스템 은 비밀 키를 사용하여 페이로드를 암호화하는 symmetric key encryption것과 같은 알고리즘 HMAC을 사용하므로이 시스템 만이이 토큰이 유효한지 여부를 알 수 있습니다.

기존 인증 JWT 토큰 헤더 :

{
   "alg": "HS256",
   "typ": "JWT"
}

여기서 사용되는 암호화 알고리즘은 대칭입니다 (HMAC + SHA256).

그러나 Cognito와 같은 최신 인증 시스템 은 공개 및 개인 키 쌍을 사용하여 페이로드를 암호화하는 asymmetric key encryption것과 같은 알고리즘 RSA을 사용합니다. 페이로드는 개인 키를 사용하여 암호화되지만 공개 키를 통해 디코딩 할 수 있습니다. 이러한 알고리즘을 사용하는 주요 이점은 토큰이 유효한지 여부를 알리기 위해 단일 인증 서비스를 요청할 필요가 없다는 것입니다. 누구나 공개 키에 접근 할 수 있기 때문에 누구나 토큰의 유효성을 확인할 수 있습니다. 유효성 검사로드는 상당히 분산되어 있으며 단일 실패 지점이 없습니다.
Cognito JWT 토큰 헤더 :

{
  "kid": "abcdefghijklmnopqrsexample=",
  "alg": "RS256"
}

이 경우에 사용되는 비대칭 암호화 알고리즘-RSA + SHA256


답변

cognito-jwt-verifier 는 최소한의 종속성으로 노드 / Lambda 백엔드의 AWS Cognito에서 얻은 JWT 토큰에 액세스하고 ID를 확인하는 작은 npm 패키지입니다.

면책 조항 : 나는 이것의 저자입니다. 나는 나를 위해 모든 상자를 체크하는 것을 찾을 수 없기 때문에 그것을 생각해 냈습니다.

  • 최소한의 의존성
  • 프레임 워크 불가지론
  • JWKS (공개 키) 캐싱
  • 테스트 범위

사용법 (자세한 예는 github repo 참조) :

const { verifierFactory } = require('@southlane/cognito-jwt-verifier')

const verifier = verifierFactory({
  region: 'us-east-1',
  userPoolId: 'us-east-1_PDsy6i0Bf',
  appClientId: '5ra91i9p4trq42m2vnjs0pv06q',
  tokenType: 'id', // either "access" or "id"
})

const token = 'eyJraWQiOiI0UFFoK0JaVE...' // clipped 

try {
  const tokenPayload = await verifier.verify(token)
} catch (e) {
  // catch error and act accordingly, e.g. throw HTTP 401 error
}