Correct Micro Service JWT flow - security

I am currently building out a micro service architecture and started with the auth server and client. I also wanted to confirm the best flow of authenticating a user with a token.
In the above image. Step 3 is were I start getting confused.
I thought of 2 solutions to the problem.
One every api passes the token to the auth server and waits to get approval that the token stored inside matches the db and it is still valid.
Two is to include a secret phrase in the JWT token and just have the API service parse and check for itself if the token is valid.(The secret phrase would be so that if a hacker tried to fake a token and it parsed to a valid id somehow the phrase would be off without the secret code used to encrypt the token. Which I don't even know if it is possible. If not then I guess 2 would be the best course of action)

A hacker cannot create a valid JWT token if he does not know the the signing key. If he somehow manages to get that signing key it is reasonable to assume that he is able to get your "secret phrase" also.
About the checking: JWT tokens can be checked by the API service as they contain all the information needed (except the signing key that must be known by the API service). The expiration can be checked here also. Anyway, you also need the information stored inside the token, like user ID. You should do this if you want better scalability.
The only reason why you would need to check a JWT token against a third Auth service is to see if it has been invalidated; for this you need a central service although you could replicate the list of invalid tokens to all the API services for better resilience.

You really don't have to forward the request to Auth-server to validate the JWT token. A JWT token is like a bill note, once it's signed it can be validated by anyone who is sharing the key.
I would recommend you to have an edge service in front of all your API-services. The edge service either shares the key by which JWT token is signed by Auth service or has the public key to verify the signature.
Once the signature is verified, the edge service can extract the required information from the token and add it to request header. Your downstream services can consume this information according to their need.
You can use Https to enforce that your request isn't intercepted by anyone over the network. In case, even if someone tries to mess up with the JWT token, the signature won't match and you can detect that. Please go through JWT/KONG: Cannot create JWTs with a shared secret to know more about creating-parsing the JWT token with public-private keys.

Related

Correct Application Structure to Safely Verify Cognito JWT Tokens

I'm building a React application that uses API Gateway and Lambda on the back-end. I'm going through the process of integrating Cognito authentication. I've completed the following:
Generate user pool
Upon login redirect to my React application with Auth Code
Extract Auth Code and send it to Token Enpoint
Receive back the id, access and refresh JWT tokens
I covered all that in detail in a post here: AWS Cognito Notes
What I'm confused about is the concept of verifying the signature of the JWT tokens. The AWS Docs describe how to do that here: https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html
In short I have to download the JSON Web Key and then match it to the key on the tokens. It's not clear to me how this makes the process any safer. That key is publicly available. If I was conducting a man in the middle attack I could simply get that key and then attach it to my phony JWT token.
Which makes me think that I have a fundamental misunderstanding of this process. Should the Auth Code and JWT tokens not be sent to the React app in the first place?
Should I be setting the User Pool redirect URL to API Gateway, and have that trigger a Lambda function (that contains the client secret), to retrieve the JWT Tokens and THEN send the JWT Tokens to the React app? But then I would have the same problem of not knowing if the Tokens were legit I think? The tokens much be verified AT THE CLIENT, right?
If anyone has any insight on this or could point me to a good article I would much appreciate it.
You can use an API Gateway Authorizer to do this for you. It will check the header for ID Token and check if it is valid for your userpool. Expired and invalid tokens are rejected.
You can read more about this here: https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html
The answer to this was that the JWK can be stored in any capacity with the client. The JWK is publicly available, however the public key cannot be used to fake the signature itself, because the signature can only be generated with the PRIVATE key.
I did a full write up on this topic here:
https://ncoughlin.com/posts/verify-decode-cognito-jwt-tokens/

How safe is an acess token?

I have been reading about OAuth 2.0 Authorization Code flow to protect APIs in microservices architectures but I dont understand how an access token issued by the Auth Server is supposed to protect an API hosted in another server.
Is that same access token also kept in the API and when the client tries to access it with the access token issued by the Auth Server, the API checks if contains it? If so, does that mean that the access token is sent both to client and the protected API in the authentication process?
I hope to have explained my problem well. Thanks in advance.
Anunay gave a pretty good analogy of how JWTs work at a high level as portable, trustable identifier, but since OAuth supports more than just JWT authentication it might warrant sharing a bit more detail.
Token introspection
In your question you rightly assumed that tokens need some way of being trusted, and that one such way would be to store the token in a private database and do a lookup whenever a token is presented to determine its validity. You would absolutely be able to instrument a valid OAuth server using a method like this by issuing the token using whatever form you wish and writing an introspection endpoint that performs the lookup. The OAuth spec is intentionally abstract so that the functional behavior of token introspection can take many forms.
One of the reasons for this level of abstraction is because while storing the tokens for direct lookup might be easy, it means that you have to store copies of these tokens in some form in a private database for comparison. This storage would in turn make you a honeypot for bad actors, both internally and externally, who would seek to impersonate your users en-masse. It's for this reason that many implementations of OAuth prefer to issue and validate tokens using public/private key encryption instead of direct lookups. This process is very much like the one Anunay described in his comment in that it issues tokens that are signed with a private key and verified with a public one. With this process, you no longer need to keep everyone's token in a private database, and instead simply need to secure private and public keys that are used to sign and verify tokens respectively.
JSON Web Tokens (JWTs) and reducing number of introspection calls
Anunay's response specifically referred to a common token structure that is generated using public/private key encryption and issued to users, JSON Web Tokens. These tokens are structured in such a way that they include the user information a backend service might need like the User ID, email address, and sometimes more, in a raw format that is directly readable to the backend API. In addition to this raw information however, JWTs include a duplicate copy of the data, but this duplicate copy is private-key encrypted. In order to trust a JWT token, all you have to do is use the public key and ensure that the private-key encoded payload is verifiable by applying the public key to the raw payload. Since public keys rarely change, many backend services cache the keys used for verification and elect not to do a token introspection on the issuing server since they already can verify the payload. This is how you'd optimize throughput on backend services that are protected via OAuth.
Since public keys can only be used to verify payloads and not produce them, these public keys are often broadcast by the servers that issued the tokens allowing anyone to "trust" the tokens it issues if they so choose. To learn more about this process, I'd recommend you research OpenID Connect.
Access token can be understood as an passport that government issue to the citizen based on proof of identity.
When you take it to another country, they look at the document and trust it because they trust the country and you because you are the holder of that document with you details.
They trust the fact that passport cannot be fiddled with and allow you entry
Now for access token, in very simple terms, authorization server verifies the user. Once verified it issues the user a JWT token (Access Token). This token is signed with private key. It has your details and is encoded along with signature. Now you can take this token to any third party who has got the public key and trust the authorization server. Now when you share the access token with this third party, it use public key to verify the token and check for expiry. If valid it allows you in.
So API doesn't really need to talk to auth server or keep any details about the token. All its needs is a public key to decode the token.
Now there are two important things. One if you ever let loose your access token, or some one who is not intended to get hold of your token gets it, he can do what ever he wants and auth server will not be able to do much. However as you see this approach reduces the chattiness of the systems specially microservices.
So to address this we limit the expiry of access token. Like passport, it comes with expiry. Shorter you keep it,user have to go and get the token refreshed with auth server. Every time he does so, auth server gets a change to verify creds and other details. If they do not match access token will not be refreshed.

does JWT containing userID need verification from the database?

I sign a JWT (JSON Web Token) with userID and iat (issues at) like so
jwt.encode({sub: user.id, iat: timestamp}, jwtSecret);
When I receive a JWT from the client, I decode it to extract the userID. Do I need to validate the userID by checking its existence in the database every time I need to allow the user to access a secure route (see first example)? Or can I just assume that the user is who she says she is, and allow her to access the secure path?
My feeling is that I need to access the database to validate the user on every request, this would be expensive and defeat the purpose of using a JWT.
Your token is signed. If someone changes the token on client side, it would fail validation and the server side framework would reject it. Therefore you can trust your token.
Of course, the jwtSecret should be a secret only known by your authentication server and resource server.
You generate the token only if you trust the user who requested it.
You trust the token as long as it has not expired and can be verified with the secret.
The whole idea of JWT is that can verify the integrity of the claims contained within it. If you can decode successfully the token you can be sure that this token contains information previously encoded by you. For someone to pass malformed data has to also know the secret you use to sign the tokens.
For more information read this.

Restricting traffic to server from only my mobile application

I have a requirement to secure my JAX-RS resources and only accept requests that originate from authorized mobile applications. Is this possible? How can this be done?
All of my resources are protected already with user authentication, the goal here is to reduce user ID fishing attempts. I know one solution would be to keep the response error with an invalid user ID generic, but the application is very large and at the moment this isn't possible.
One idea I came up with is to use JWT tokens signed with a shared secret. Then I could add an Authorization filter on the server to check the signature. If it doesn't validate then discard the request. Does this sound like a viable option?
My concern is the security of the shared secret on a mobile device, could it be compromised with a rooted device?
Using tokens is the preferred way. But the secret key is not shared. Only the server has access to it. That secret key is used to generate the message authentication code(MAC) of the JWT. Since secret key is only known by the server, no one else can generate a JWT with a valid signature. Secret may be persisted or application scoped.
Once a client is authenticated using credentials, server must send a signed JWT to the client.
That JWT must contains necessary information to identify the client and state(if necessary).
Then client send that token in a header field along with all the other requests.
Server validates the JWT using secret key and process the request.
Even though client can change the JWT body, he cannot get it verified. That's the whole point of using a signature.

Exchange IP-STS JWT token for ACS JWT token

I have an azure ACS service, which trusts an IP-STS.
For an active scenario, I first get a JWT token from my IP-STS using username, password credentials. There is Oauth2 endpoint and everything works quite well.
Is it possible to "exchange" this IP-STS token for a JWT token issued by my azure ACS ? If so, is there an example of code that does this. (To make things worse, all my code is in JavaScript (actually TypeScript) but that doesn't really matter).
Update :
I'm working on your hint with the ACS OAuth2 draft 13 endpoint.
I proceed as follows : I ask my custom STS (ThinkTecture STS) to give me a JWT token for the "ACS OAuth2 draft 13 endpoint" realm. This requires an oAuth client id and secret which are global in the TT STS and I assume they are irrelevant. In the TT STS management I have a symmetric key configured for this realm : key1. I receive the 3-part JWT token. The signature on the token is indeed made with key1.
I then pass this token to ACS with client id and secret from a service identity and parameters as specified
var form = new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", "http://oauth.net/grant_type/jwt/1.0/bearer" },
{ "assertion", rawtoken (the header dot body dot signature form TT STS },
{ "scope", "http://localhost"}
});
Unfortunately I now get
{"error":"invalid_client","error_description":"ACS50027: JWT token is invalid. \r\nTrace ID: b107cda5-393b-4b50-b14a-ebaa0ac41913\r\nTimestamp: 2012-12-05 08:58:10Z"}
I understand JWT is in beta and therefore ACS50027 is not yet documented. The hard part is that there is no known way to debug this. Thanks for nay help.
This is definitely possible, but I don't think any of the existing ACS samples do it so you're slightly in uncharted territory.
The approach I would recommend is to use the ACS OAuth2 draft 13 endpoint (as in this sample, but JWT instead of SAML and IdP instead of service identity). The request would be something like:
grant_type=http://oauth.net/grant_type/jwt/1.0/bearer&assertion=(JWT)&scope=(RP realm).
You'll need the issuer of the JWT to match your registered identity provider, along with an associated signing key, plus rules to pass through or change any claims as necessary and the RP to have a token type of JWT.
Ok, I finally found a way to do this. Your hint was crucial for the success. The (very) tricky part is that the IP-STS uses a symmetric key to sign the JWT token. I had to programmatically (using the odata api) insert this key in the ACS backoffice under the Identity provider. (the managament console does not show this key anywhere) Everything is nearly fine. The only trouble is that the ACS copies the audience ("aud") from the original token, while I request a token for a different audience ("scope"). Any ideas there ?
Nope,
You can't Exchange tokens in ACS. The only possibly viable way of doing so, is if you are able to register your IP-STS as an IdP in ACS (you will be able to do so, if your IP-STS supports WS-TRUST protocol). This will throw you on the passive scenario.
The only possible active scenario way is to get JWT token directly from ACS using Username/Password authentication with Service Identities. Well managing service identities in ACS is not as managing ASP.NET Membership provider, but it has a Management API.
The possible ways for authenticating with a Service Identity is Password, Symmetric Key, X.509 Certificate. So, if you twist enough your original IP-STS, to give you a Symmetric Key as a Claim in its JWT token, you will be able to get JWT token from ACS using Service Identity authenticating with that Symmetric Key.
But, if you already have a JWT token, why you need another one from ACS?
UPDATE
Well, the claims transformations are only option in the "Passive" mode. For active mode, the only way (that I know) is to use Service Identities, thus no claims transformations.
The issue with Passive is that because of security reasons, ACS will create a hidden POST form to POST submit transformed token to your Relying Party Application (RP). So there is no way to Actively get a Passive behavior. What you could do just for the sake for trial, and that will fail because of the passive POST in hidden form is the following:
If you are willing to experiment (and I have not tested and I am totally unaware of the result), you may try simulating wsignin1.0 action providing the original JWT token. You can look at the WS-Federation Passive Requestor Profile, section 3.1 on how to construct a Sign-In request. You can also use Fiddler to fully track a Passive scenario chain of events. The one that you shall try reconstruct is the wsignin1.0 request that goes from the registered IdP back to ACS.
At some point you will have the resulted HTML, which contains a FORM element. That form will have two hidden fields - wa with value of wsignin1.0 and wresult which will contain URL encoded <t:RequestSecurityTokenResponse xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">.... This is what you want.
If you succeed in recreating the original SignIn request to ACS from your IdP, than this will work.

Resources