how to refresh token servicestack typescript - servicestack

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";

Related

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

Verify JWT from Google Chat POST request

I have a bot in NodeJS connected to Google Chat using HTTPs endpoints. I am using express to receive requests. I need to verify that all requests come from Google, and want to do this using the Bearer Token that Google Sends with requests.
My problem is that I am struggling to find a way to verify the tokens.
I have captured the token and tried a GET reuqes to https://oauth2.googleapis.com/tokeninfo?id_token=ey... (where ey... is the token start).
Which returns:
"error": "invalid_token",
"error_description": "Invalid Value"
}
I have tried what Google recommends:
var token = req.headers.authorization.split(/[ ]+/);
client.verifyIdToken({
idToken: token[1],
audience: JSON.parse(process.env.valid_client_ids)
}).then((ticket) => {
gchatHandler.handleGChat(req.body, res);
}).catch(console.error);
And get the following error:
Error: No pem found for envelope: {"alg":"RS256","kid":"d...1","typ":"JWT"}
Any idea where I should head from here?
Edit: https://www.googleapis.com/service_accounts/v1/metadata/x509/chat#system.gserviceaccount.com found this, investigating how to use it. The kid matches the one I get.
Worked it out, eventually.
You need to hit: https://www.googleapis.com/service_accounts/v1/metadata/x509/chat#system.gserviceaccount.com to get a JSON file containing the keys linked to their KIDs.
Then when a request arrives, use jsonwebtoken (NPM) to decode the token and extract the KID from the header.
Use the KID to find the matching public key in the response from the website above, then use the verify function to make sure the token matches the public key.
You also need to pass the audience and issuer options to verify, to validate that it is your particular service account hitting the bot.
The solution above maybe the correct for Google Chat, but in my experience Google services (e.g. Google Tasks) use OIDC tokens, which can be validated with verifyIdToken function.
Adding my solution here, since your question/answer was the closest thing I was able to find to my problem
So, In case if you need to sign a request from your own code
on client, send requests with OIDC token
import {URL} from 'url';
import {GoogleAuth} from 'google-auth-library';
// will use default auth or GOOGLE_APPLICATION_CREDENTIALS path to SA file
// you must validate email of this identity on the server!
const auth = new GoogleAuth({});
export const request = async ({url, ...options}) => {
const targetAudience = new URL(url as string).origin;
const client = await auth.getIdTokenClient(targetAudience);
return await client.request({...options, url});
};
await request({ url: 'https://my-domain.com/endpoint1', method: 'POST', data: {} })
on the server, validate OIDC (Id token)
const auth = new OAuth2Client();
const audience = 'https://my-domain.com';
// to validate
const token = req.headers.authorization.split(/[ ]+/)[1];
const ticket = await auth.verifyIdToken({idToken: token, audience });
if (ticket.getPayload().email !== SA_EMAIL) {
throw new Error('request was signed with different SA');
}
// all good
Read more about Google OpenID Connect Tokens

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.

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.

Missing SetToken and SetTokenCookie on JsonServiceClient from TypeScript

We are having trouble assigning the BearerToken when calling ServiceStack using JsonServiceClient from TypeScript. The SetCookie and SetCookieToken methods appear to be missing as described in this question:
ServiceStack Javascript JsonServiceClient missing properties
We tried the solution described there:
this.client.headers.append("Authorization" , "Bearer " + jwtToken);
But when we try this we get an HTTP 400: Bad Request
How can we use the the JsonServiceClient from JavaScript with our BearerToken?
I've just added an explicit setBearerToken() API (in v0.0.28) that adds the Bearer Token HTTP Header:
setBearerToken(token:string): void {
this.headers.set("Authorization", "Bearer " + token);
}
And confirms that it works in this node/browser compatible test:
var client = new JsonServiceClient("http://test.servicestack.net");
client.setBearerToken(jwtToken);
var success = await client.post(new Authenticate());
Your issue is unrelated to JWT. You should check your HTTP Response Headers to find out more about. A 400 Response is a Bad Request Exception, you would get a 401 Unauthorized response for Unauthorized exceptions.
Token Cookie vs Bearer Token
Also note that a setTokenCookie() which passes the JWT Token in the ss=jwt Cookie is unrelated to the Bearer Token which is added to the Authorization HTTP Request Header.
If your JWT is configured correctly when you authenticate via a different auth provider, e.g:
var request = new Authenticate();
request.provider = "credentials";
request.userName = userName;
request.password = password;
var authResponse = await client.post(request);
var jwtToken = authResponse.bearerToken;
The JWT Token will be populated in authResponse.bearerToken which you can use with client.setBearerToken() to add the the HTTP Request Header. After your authenticated you can also go to /auth to see the JWT BearerToken.
If the bearerToken is not populated then JWT Provider isn't populating it for that request. If you're not using https you need to ensure RequireSecureConnection=false to allow it to work over http.
If you prefer you can instead tell ServiceStack you want to use JWT Cookie instead which you can specify when you authenticate with:
var request = new Authenticate();
request.useTokenCookie = true;
In which case instead of returning it in bearerToken the JWT Token is populated in the ss=tok HttpOnly Cookie which JavaScript can't access directly, but will be transparently be sent with each future ServiceClient requests.

Resources