Using JwtAuthProviderReader with ServiceStack and AWS Cognito - servicestack

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?

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.

Related

IDX10501: Signature validation failed. Unable to match key: kid

We want to switch our web apps to using Azure AD for authentication.
I'm using the example at: https://github.com/Azure-Samples/ms-identity-aspnet-webapp-openidconnect
It wouldn't work initially but after some modifications I got authentication to work. But when it redirects back to my app I get this error: IDX10501: Signature validation failed. Unable to match key: kid
I'm using https://login.microsoftonline.com/{0}/v2.0 for the authority where {0} is my tenant id.
This is the authentication code
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
// Custom middleware initialization. This is activated when the code obtained from a code_grant is present in the querystring (&code=<code>).
app.UseOAuth2CodeRedeemer(
new OAuth2CodeRedeemerOptions
{
ClientId = AuthenticationConfig.ClientId,
ClientSecret = AuthenticationConfig.ClientSecret,
RedirectUri = AuthenticationConfig.RedirectUri
});
IdentityModelEventSource.ShowPII = true;
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// This is needed for PKCE and resposne type must be set to 'code'
UsePkce = true,
ResponseType = OpenIdConnectResponseType.Code,
// The `Authority` represents the v2.0 endpoint - https://login.microsoftonline.com/{0}/v2.0
Authority = AuthenticationConfig.Authority,
ClientId = AuthenticationConfig.ClientId,
ClientSecret = AuthenticationConfig.ClientSecret,
RedirectUri = AuthenticationConfig.RedirectUri,
PostLogoutRedirectUri = AuthenticationConfig.RedirectUri,
Scope = AuthenticationConfig.BasicSignInScopes + " Mail.Read User.Read", // a basic set of permissions for user sign in & profile access "openid profile offline_access"
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false
},
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed,
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
},
// Handling SameSite cookie according to https://learn.microsoft.com/en-us/aspnet/samesite/owin-samesite
CookieManager = new SameSiteCookieManager(
new SystemWebCookieManager())
});
}
private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> arg)
{
arg.ProtocolMessage.SetParameter("myNewParameter", "its Value");
return Task.CompletedTask;
}
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
context.TokenEndpointRequest.Parameters.TryGetValue("code_verifier", out var codeVerifier);
// Upon successful sign in, get the access token & cache it using MSAL
IConfidentialClientApplication clientApp = MsalAppBuilder.BuildConfidentialClientApplication();
AuthenticationResult result = await clientApp.AcquireTokenByAuthorizationCode(new[] { "Mail.Read User.Read" }, context.Code)
.WithSpaAuthorizationCode() //Request an authcode for the front end
.WithPkceCodeVerifier(codeVerifier) // Code verifier for PKCE
.ExecuteAsync();
HttpContext.Current.Session.Add("Spa_Auth_Code", result.SpaAuthCode);
// This continues the authentication flow using the access token and id token retrieved by the clientApp object after
// redeeming an access token using the access code.
//
// This is needed to ensure the middleware does not try and redeem the received access code a second time.
context.HandleCodeRedemption(result.AccessToken, result.IdToken);
}
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
//notification.HandleResponse();
//notification.Response.Redirect("/Error?message=" + notification.Exception.Message);
return Task.FromResult(0);
}
How do I get it to validate the signature?
Thanks,
Skye
Please check if below points help:
On successful authentication, Azure AD issues a signed JWT token (id token or access token). The resource application needs to know the public key of the certificate used sign the token in order to validate the token signature . An OWIN asp.net application can throw the following error IDX10501: Signature validation failed. Unable to match ‘kid’ or IDX10501: Signature validation failed. Unable to match key when it’s not able to find the kid to validate the token signature:
Please check and add valid audience and issuer. And check if the kid
its validating is for symmetric key.
Check if you are passing the correct metadata endpoint.
ex:
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Sets the ClientId, authority, RedirectUri as obtained from web.config
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
MetadataAddress = "https://login.microsoftonline.com//.well-known/openid-configuration?appid="
PostLogoutRedirectUri = redirectUri,
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>($"{validissuers.Last()}/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
var openidconfig = configManager.GetConfigurationAsync().Result;
TokenValidationParameters = new TokenValidationParameters()
{
ValidateAudience = true,
ValidAudience = config.Resource,
ValidateIssuer = true,
ValidIssuers = new[] { config.GetAuthority() },
ValidateIssuerSigningKey = true,
IssuerSigningKeys = openidconfig.SigningKeys,
//or IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SuperSecretPassword2"))
}
}
Azure AD by default uses a certificate to sign an OAuth2 JWT token
using an asymmetric algorithm (RS256). Alternatively a JWT token can
be signed with a “shared” secret using a symmetric algorithm (HS256).
References:
get a symmetric key signing token (aaddevsup.xyz)
c# - IDX10501- Stack Overflow

How to put an Api key in the Authenticate message?

I'm trying to combine the api key auth provider with the encrypted messaging plugin.
var client = new JsonServiceClient(home);
client.BearerToken = "somesecret";
works
but i want my apikey to be in the message so i tried
var authResponse = client.Post(new Authenticate
{
provider = ApiKeyAuthProvider.Name,
UserName = "somesecret"
});
This post fails at runtime with a 401 not authenticated.
How do i get this to work?
IAuthWithRequest Auth Providers like the API Key Auth Provider needs to be sent per request with the Authenticated User Session it establishes only lasts for the lifetime of that request. It can't be used with the Authenticate Service to Authenticate the client as your example tried to do, it must be included in each request to an Authenticated Service.
The normal way to call a protected Service with the API Key is to just populate the BearerToken property:
var client = new JsonServiceClient(baseUrl) {
BearerToken = apiKey
};
Which will then let you call your [Authenticate] Service:
var response = client.Get(new Secure { Name = "World" });
Encrypted Messaging Support
Previously you could only embed the User SessionId within an Encrypted Messaging Request but I've just added support for Authenticating Encrypted Messaging Services with a BearerToken in this commit which works similar to populating a SessionId, where you can now populate a BearerToken as used in API Key and JWT Auth Providers by having your Request DTOs implement IHasBearerToken, e.g:
public class Secure : IHasBearerToken
{
public string BearerToken { get; set; }
public string Name { get; set; }
}
This will let you embed the BearerToken when calling the protected Service, e.g:
IEncryptedClient encryptedClient = client.GetEncryptedClient(publicKey);
var response = encryptedClient.Get(new Secure { BearerToken = apiKey, Name = "World" });
Where it will be embedded and encrypted along with all content in the Request DTO.
Alternatively you can also set the BearerToken property on the IEncryptedClient once and it will automatically populate it on all Request DTOs that implement IHasBearerToken, e.g:
encryptedClient.BearerToken = apiKey;
var response = encryptedClient.Get(new Secure { Name = "World" });
The new BearerToken support in Encrypted Messaging is available from v5.1.1 that's now available on MyGet.

signature verification failed for JWT - node/express/identityServer/asp.net.

I am having an issue with validating the JWT on the server side end of my node/express app. The token is being generated in Identity Server in an asp.net core app. The token that is generated is an RS256 token type, which means a private key and public key need to be generated on creation in the Identity Server. I need to retrieve a valid certificate with a valid signature.
On the client side (Angular) I'm passing in the Bearer token on all requests once signed in. I need to authenticate that token somehow. The way to do that with a RS256 token type is to make sure the public key matches. I'm using
const jwt2 = require('jwt-simple');
For my JWT validation.
The issue is the secret, here is the jwt-simple documentation jwt-simple link. If I make the third value in decode false it works, because it's ignoring the secret/cert that is required.
I'm making this validation in the middleware so all endpoints will hit it.
I saw this issue - SO Similar Issue and ran those same commands. I'm still getting the error because the token doesn't really have anything to do with the certs because I'm getting it from the Identity Server project. So I need to retrieve the cert public key from that project.
How would I be able to send that cert in the token or retrieve that valid cert somehow?
v1 - (using the self signed server.crt as the cert and getting this error)
Error: Signature verification failed
App.js
//This is for a self-signed certificate locally with no correlation to the token itself.
const options = {
key: fs.readFileSync('./key.pem', 'utf8'),
cert: fs.readFileSync('./server.crt', 'utf8')
};
app.use((req, res, next) => {
if(!req.headers.authorization){
return res.status(403).json({ error: 'No credentials sent!'});
} else {
let token = req.headers.authorization.split(' ')[1]
var decoded = jwt.decode(token, options.cert);
if(decoded){
let currentTime = new Date().getTime()/1000
if(decoded.exp <= currentTime){
return res.status(403).json({
error: 'Token has expired'
});
}
}
else if(!decoded){
return res.status(403).json({
error: 'invalid token'
});
}
}
next();
})
JWT.io parsed token structure -
Header
{
"alg": "RS256",
"kid": "1231231231231231231",
"typ": "JWT",
"x5t": "si7bdXd6......HnxhO4Wi_s"
}
Do I do anything with x5t? Apologies for the long post. Thanks.
If the signing public key is provided with the token and you blindly trust it, it basically defeats the purpose of having signed tokens.
You need some other mechanism for sharing the authentication service's public key. Depending on your requirements for key rotations and how your app works in general, you can either get it from a static path/url when your app starts up, or you may want to build in a mechanism to periodically check for updated valid public key(s).

What is the difference between Bearer Token and Refresh Token?

In ServiceStack, I am using JwtAuthProvider, where I got Bearer Token and Refresh token so how do I verify the token and authorize the web api service?
Code:
var client = new JsvServiceClient(ListeningOn) { UserName = "tuser", Password = "password" };
client.Send<AssignRolesResponse>(new AssignRoles
{
UserName = "tuser",
Roles = new ArrayOfString("TestRole"),
Permissions = new ArrayOfString("GetStatus")
});
var jwtToken = client.Send(new Authenticate()).BearerToken;
Here, What is the use of 'jwtToken' value? user is already authorized and authenticated so I don't get the idea why the token needed here?
Could anyone suggest me how do I take advantage of that token?
JWT Config:
this.Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[]
{
new JwtAuthProvider(AppSettings) {
RequireSecureConnection = false,
AuthKey = AesUtils.CreateKey(),
//CreatePayloadFilter = (payload,session) =>
// payload["CreatedAt"] = session.CreatedAt.ToUnixTime().ToString(),
CreatePayloadFilter = (jwtPayload, session) =>
jwtPayload["exp"] = DateTime.UtcNow.AddSeconds(-1).ToUnixTime().ToString()
},
new CredentialsAuthProvider(AppSettings),
new BasicAuthProvider()
}));
Please see this article on the purpose of JWT Refresh Tokens.
In summary, the BearerToken the actual JWT and what used to authenticate via JWT. It's contains a stateless snapshot of the Users Session and is typically a short-lived token, which after it expires needs to contact the Auth Server to fetch a new JWT Token.
The RefreshToken is a longer-lived token which can be used to request new JWT Tokens transparently (i.e without forcing the User to manually re-authenticate) and will be populated with the latest snapshot of the Users Session.
The shorter the lifetime of JWT BearerTokens the less stale the stateless Session information that's stored in the JWT but the more often the RefreshToken will need to be used to fetch an updated BearerToken. Only after a new BearerToken is requested will session information like the Roles and Permissions has or whether they're locked out.
The lifetime of each Token is configurable with the ExpireTokensIn and ExpireRefreshTokensIn JwtAuthProvider properties.
ServiceStack Clients built-in support for JWT and Refresh Tokens
ServiceStack's Service Clients automatically take care of transparently fetching new JWT Tokens using RefreshTokens. You would typically populate both the BearerToken and RefreshToken when initializing your Service Client, e.g:
var authResponse = authClient.Send(new Authenticate());
var client = new JsonServiceClient(baseUrl) {
BearerToken = authResponse.BearerToken,
RefreshToken = authResponse.RefreshToken,
};
The BearerToken is needed to make the request although since the Service Client automatically fetches new JWT Tokens with the configured RefreshToken you only need to populate the RefreshToken:
var client = new JsonServiceClient(baseUrl) {
RefreshToken = authResponse.RefreshToken,
};
As ServiceStack will automatically fetch a new JWT Token on first use, but you can save a round-trip by populating both.

how to refresh token servicestack typescript

On servicestack it says that for regular client it should be like that but for typescript it should be somehow different. Anyone knows how to do it?
var client = new JsonServiceClient(baseUrl);
client.OnAuthenticationRequired = () => {
client.BearerToken = authClient.Send(new Authenticate()).BearerToken;
};
Support for onAuthenticationRequired and refreshToken was added to the TypeScript servicestack-client in v0.0.32 where they can be used to transparently handle 401 Unauthorized Responses and re-authenticate the JsonServiceClient from within the callback before it automatically retries the original failed request:
Transparently handle 401 Unauthorized Responses
If the server returns a 401 Unauthorized Response either because the client was Unauthenticated or the configured Bearer Token or API Key used had expired (or was invalidated), you can use onAuthenticationRequired callback to re-configure the client before automatically retrying the original request, e.g:
client.onAuthenticationRequired = async () => {
const authClient = new JsonServiceClient(authBaseUrl);
authClient.userName = userName;
authClient.password = password;
const response = await authClient.get(new Authenticate());
client.bearerToken = response.bearerToken;
};
//Automatically retries requests returning 401 Responses with new bearerToken
var response = await client.get(new Secured());
Automatically refresh JWT Tokens
With the Refresh Token support in JWT you can use the refreshToken property to instruct the Service Client to automatically fetch new JWT Tokens behind-the-scenes before automatically retrying failed requests due to invalid or expired JWTs, e.g:
//Authenticate to get a new Refresh Token
const authClient = new JsonServiceClient(authBaseUrl);
authClient.userName = userName;
authClient.password = password;
const authResponse = await authClient.get(new Authenticate());
//Configure client with RefreshToken
client.refreshToken = authResponse.RefreshToken;
//Clients will automatically retrieve new JWT Tokens as needed
var response = await client.get(new Secured());
Send Refresh Tokens to an alternate server
Use the refreshTokenUri property when refresh tokens need to be sent to a different ServiceStack Server that issues new JWT Tokens, e.g:
client.refreshToken = refreshToken;
client.refreshTokenUri = authBaseUrl + "/access-token";

Resources