servicestackservicestack-auth

Using JwtAuthProviderReader with ServiceStack and AWS Cognito


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?


Solution

  • 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.