1

We are using an existing userpool in AWS Cognito, a separate client app is created for our api server.

When using the hosted UI from Cognito accessToken, idToken and refreshToken.

The issue is when adding JwtAuthProviderReader to AuthFeature for doing the token validation we get "HTTP/1.1 401 Unauthorized" for any endpoint we create with the [Authenticate] attribute.

Plugins.Add(new AuthFeature(() => new AuthUserSession(),
  new IAuthProvider[]
  {
    new JwtAuthProviderReader
    {
      Audience = "11rqr096c55xxxxxxxxxxxxxx", // App client id
      Issuer = "https://cognito-idp.eu-west-1.amazonaws.com/eu-west-1_xxXxxXXxX",
      HashAlgorithm = "RS256",
      PublicKey = new RSAParameters
      {
        Modulus = Base64UrlEncoder.DecodeBytes("JRDU3q2XoOcKGjcj1DsJ3Xj .... DTNVCGzUCGosKGYL0Q"),
        Exponent = Base64UrlEncoder.DecodeBytes("AQAB")
      },
      RequireSecureConnection = false,          
    }
  }
)
{ 
  IncludeAssignRoleServices = false
});

The modulus and Exponent is from e and n in Well-Known response ref https://cognito-idp.eu-west-1.amazonaws.com/eu-west-1_xxXxxXXxX/.well-known/jwks.json

Service protected by Authenticate attribute always returns HTTP/1.1 401 Unauthorized

[Authenticate]
public object Get(GetTenants request)
{
   return ...;
} 

How can we know that our JwtAuthProviderReader is setup correctly?

Kai-Rune
  • 462
  • 6
  • 17

1 Answers1

1

You can test whether your JWT can be validated with ServiceStack's JWT Auth Provider by testing the JWT Token in the IsJwtValid API of a configured JwtAuthProviderReader instance, e.g:

var jwtAuth = new JwtAuthProviderReader { ... };
jwtAuth.IsJwtValid(jwt);

This will return false if the JWT is not valid. There's a lot of reasons why a JWT wouldn't be valid, so the first thing I'd check is to test you can actually decrypt the JWE Token by calling GetVerifiedJwePayload(), e.g:

var jsonObj = jwtAuth.GetVerifiedJwePayload(null, jwt.Split('.'));

If successful it will return a decrypted but unverified JSON Object. This will fail with your current configuration because decrypting an RSA JWE Token requires configuring the complete PrivateKey, i.e. not just the PublicKey components.

If you're only using RSA256 to verify the JWT Signature instead of encrypting the JWE Token and jwtAuth.IsJwtValid(jwt) returns false, you can verify if signature is valid by calling GetVerifiedJwtPayload(), e.g:

var jwtBody = jwtAuth.GetVerifiedJwtPayload(null, jwt.Split('.'));

This will return null if the signature verification failed otherwise it will return a JsonObject with the contents of the JWT Body.

You can then validate the jwtBody payload to check if the JWT is valid, e.g:

var invalidErrorMessage = jwtAuth.GetInvalidJwtPayloadError(jwtBody);
var jwtIsValid = invalidErrorMessage == null;

Which returns null if the JWT is valid otherwise a string error message why it's not.

mythz
  • 141,670
  • 29
  • 246
  • 390
  • Since we're using RS256 there is only PublicKey part. jwtAuth.IsJwtValid(jwt) returns true, but jwtAuth.GetVerifiedJwePayload(null, jwt.Split('.')) throws exception: IndexOutOfRangeException: Index was outside the bounds of the array. – Kai-Rune Apr 05 '21 at 10:26
  • @Kai-Rune you need the private key to be able to decrypt a JWE Token which has 5 parts, but doesn't sound like you're using JWE. How many parts does `jwt.Split('.').Length` have? 5 or 3? and are you able to decode your JWT in https://jwt.io ? – mythz Apr 05 '21 at 11:33
  • It´s 3 parts, and jwt.io says "Signature Verified". I can´t use GetVerifiedJwePayload to verify the token, since it´s JWS and not JWE – Kai-Rune Apr 05 '21 at 13:13
  • 1
    @Kai-Rune ok then it's only using RSA256 for verifying the JWT signature. See my updated answer above of other APIs to call to test & validate the JWT. – mythz Apr 05 '21 at 16:42