可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm building a system consisting of an Angular2 single page app and a REST API running on ECS. The API runs on .Net/Nancy, but that might well change.
I would like to give Cognito a try and this is how I imagined the authentication workflow:
- SPA signs in user and receives a JWT
- SPA sends JWT to REST API with every request
- REST API verfies that the JWT is authentic
My question is about step 3. How can my server (or rather: my stateless, auto-scaled, load-balanced Docker containers) verify that the token is authentic? Since the "server" hasn't issued the JWT itself, it can't use its own secret (as described in the basic JWT example here).
I have read through the Cognito docs and googled a lot, but I can't find any good guideline about what to do with the JWT on the server side.
回答1:
Turns out I didn't read the docs right. It's explained here (scroll down to "Using ID Tokens and Access Tokens in your Web APIs").
The API service can download Cognito's secrets and use them to verify received JWT's. Perfect.
Edit
@Groady's comment is on point: but how do you validate the tokens? I'd say use a battle-tested library like jose4j or nimbus (both Java) for that and don't implement the verification from scratch yourself.
Here's an example implementation for Spring Boot using nimbus that got me started when I recently had to implement this in java/dropwizard service.
回答2:
Here's a way to verify the signature on 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
回答3:
I had a similar problem but without using the API Gateway. In my case I wanted to verify the signature of a JWT token obtained via the AWS Cognito Developer Authenticated identity route.
Like many posters on various sites I had trouble piecing together exactly the bits I needs to verify the signature of an AWS JWT token externally i.e., server side or via script
I think I figured out out and put a gist to verify an AWS JWT token signature. It'll verify an AWS JWT/JWS token with either pyjwt or PKCS1_v1_5c from Crypto.Signature in PyCrypto
So, yes this was python in my case but it's also doable easily in node (npm install jsonwebtoken jwk-to-pem request).
I attempted to highlight some gotchas in the comments because when I was trying to figure this out I was mostly doing the right thing but there were some nuances like python dict ordering, or lack there of, and json representation.
Hopefully it may help somebody somewhere.
回答4:
Short answer:
You can get the public key for your user pool from the following endpoint:
https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json
If you successfully decode the token using this public key then the token is valid else it is forged.
Long answer:
After you successfully authenticate via cognito, you get your access and id tokens. Now you want to validate whether this token has been tampered with or not. Traditionally we would send these tokens back to the authentication service (which issued this token at the first place) to check if the token is valid. These systems use symmetric key encryption
algorithms such as HMAC
to encrypt the payload using a secret key and so only this system is capable to tell if this token is valid or not.
Traditional auth JWT token Header:
{
"alg": "HS256",
"typ": "JWT"
}
Note here that encryption algorithm used here is symmetric - HMAC + SHA256
But modern authentication systems like Cognito use asymmetric key encryption
algorithms such as RSA
to encrypt the payload using a pair of public and private key. Payload is encrypted using a private key but can be decoded via public key. Major advantage of using such an algorithm is that we don't have to request a single authentication service to tell if a token is valid or not. Since everyone has access to the public key, anyone can verify validity of token. The load for validation is fairly distributed and there is no single point of failure.
Cognito JWT token header:
{
"kid": "abcdefghijklmnopqrsexample=",
"alg": "RS256"
}
Asymmetric encryption algorithm used in this case - RSA + SHA256
回答5:
this is working for me in dot net 4.5
public static bool VerifyCognitoJwt(string accessToken)
{
string[] parts = accessToken.Split('.');
string header = parts[0];
string payload = parts[1];
string headerJson = Encoding.UTF8.GetString(Base64UrlDecode(header));
JObject headerData = JObject.Parse(headerJson);
string payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(payload));
JObject payloadData = JObject.Parse(payloadJson);
var kid = headerData["kid"];
var iss = payloadData["iss"];
var issUrl = iss + "/.well-known/jwks.json";
var keysJson= string.Empty;
using (WebClient wc = new WebClient())
{
keysJson = wc.DownloadString(issUrl);
}
var keyData = GetKeyData(keysJson,kid.ToString());
if (keyData==null)
throw new ApplicationException(string.Format("Invalid signature"));
var modulus = Base64UrlDecode(keyData.Modulus);
var exponent = Base64UrlDecode(keyData.Exponent);
RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
var rsaParameters= new RSAParameters();
rsaParameters.Modulus = new BigInteger(modulus).ToByteArrayUnsigned();
rsaParameters.Exponent = new BigInteger(exponent).ToByteArrayUnsigned();
provider.ImportParameters(rsaParameters);
SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider();
byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(parts[0] + "." + parts[1]));
RSAPKCS1SignatureDeformatter rsaDeformatter = new RSAPKCS1SignatureDeformatter(provider);
rsaDeformatter.SetHashAlgorithm(sha256.GetType().FullName);
if (!rsaDeformatter.VerifySignature(hash, Base64UrlDecode(parts[2])))
throw new ApplicationException(string.Format("Invalid signature"));
return true;
}
public class KeyData
{
public string Modulus { get; set; }
public string Exponent { get; set; }
}
private static KeyData GetKeyData(string keys,string kid)
{
var keyData = new KeyData();
dynamic obj = JObject.Parse(keys);
var results = obj.keys;
bool found = false;
foreach (var key in results)
{
if (found)
break;
if (key.kid == kid)
{
keyData.Modulus = key.n;
keyData.Exponent = key.e;
found = true;
}
}
return keyData;
}