How to restrict a user to THEIR resources using JWT when there is no identity in the Access Token? - jwt

We have a REST resource like this:
/customer/{customerId}/bill
We want to use the JWT tokens returned from AWS Cognito to secure access to this resource.
The {customerId} here is not the Cognito user id, it's a domain specific id. We have added this domain specific id to the Cognito user as a custom attribute. It comes in the ID token that Cognito returns like this:
{
"sub": "xxxxxxxx-852f-474d-aa9e-a50fd832bcb8",
"aud": "xxxxxxxxsijed6uf54dh0uhi",
"custom:customerId": "4044",
"event_id": "xxxxxx-fc0c-4ffc-affa-f8987714fb2b",
"token_use": "id",
....
}
If we use this ID Token in Authorization: Bearer <ID Token> we can write code (custom authoriser or in-app code) that ensures the customerId in /customer/{customerId}/bill is equal to the value of custom:customerId in the supplied token, and we have secured our API.
But then we read that you should not use ID tokens to secure APIs . The key point being:
"The audience (the aud claim) of the (ID) token is set to the application's identifier, which means that only this specific application should consume this token."
So it seems we need to send an Access Token to secure the API. With Cognito, there is no way we can add any concept of who the user is into the Access Token. We can't add a custom scope like user:4044 for example.
What folks suggest as an approach here is to call the /userinfo endpoint of Cognito on the server-side with the supplied Access Token to learn who the user is. This would enable us to write code (custom authoriser or in-app code) that calls this endpoint and asserts permission. But it's an endpoint call for every request, which seems crazy.
One thought that crossed our minds was to use the Access Token to secure access to the API itself, but also require the ID token, either as a query parameter or a header to allow us do the fine grained access control. But that too starts to feel wrong.
Surely this is a solved problem? What is the right thing to do here?

Sorry, this question is a year old, so my answer is probably irrelevant. But for future wanderer, I would say that, given the limitations of cognito in allowing custom claims in the access token, a call to the /userinfo route is definitely the best way.
The API GATEWAY lets you cache authorizer response for a given user, so you won't be calling the endpoint on every request. Note that some implementations recommend it as a way to make sure that the token haven't been revoked.

Related

Is it right to put the user's identifier in the payload of the access token(JWT)?

I am currently developing financial services as a personal project.
In order to strengthen security in the project, it is designed and implemented to process authentication at the gateway stage using AWS API Gateway.
I tried to log in using a mobile phone number and the received authentication number, and I don't think this is appropriate for Cognito and IAM identifiers, so I'm going to run the Node Auth Server that issues and verifies JWT tokens in AWS Lambda.
In the process, I tried to include an identifier such as user_id or uuid in the payload of the JWT token, but my colleague opposed it.
His opinion was that access token should only engage in authentication and that the token should not contain a user identifier.
I agreed with him to some extent, but if so, I wondered how to deliver the user identifier in an API such as "Comment Registration API".
Should we hand over the user identifier along with the access token to the client when login is successful?
in conclusion
Is it logically incorrect to include the user identifier in Access Token's Payload?
If the answer to the above question is yes, how should I deliver the user identifier when login is successful?
I wanted to hear the majority's opinion, so I posted it.
Thank you.
Typically you want enough information in the access token so that you can also do proper authorization about what the user/caller is allowed to do.
Typically, you separate authentication and authorization like the picture below shows:
So, to make an effective API, you do want to avoid having to lookup additional information to be able to determine if you are allowed to access some piece of data or not. So, I typically include the UserID and some other claims/roles in the token, so that I can smoothly let the user in inside the API.
However, adding personal information in the access token might have some GDPR issues, but sometimes it might be necessary to also add. But I don't see any issues adding information like UserId and roles in the token.
Yes it is logically correct and a normal thing to do. To see how to do it in a Node Auth Server, you can look at this: https://auth0.com/blog/complete-guide-to-nodejs-express-user-authentication/

REST Validating token in business API generated by authentication service

I have two different REST API’s say Authentication API & Business API hosted in Windows azure.
Authentication API is responsible for authenticating user using WsFederation or OpenID or custom authentication
Business API is responsible for executing business logic. Only legitimate user should be able to access this API.
Client will contact first Authentication API and will acquire the token and then it will pass that token to Business API.
Since client can send any token to business API. Business has to validate the token. It simply cannot trust on the token. Since token Is generated by Authentication API, how business API will validate the token.
What is a standard way to validate the token in such scenario where Authentication API and Business API are hosted separately?
Good References
Principles of Token Validation (Vittorio's blog) - Awesome article, most of the information is generic so applies with or without Azure AD, and it's coming from a very knowledgeable author of course
Manually validating a JWT access token in a web API - This one is specific to validating an Azure AD issued token. Has complete code implementation too. You don't need to do exactly this, because your validation will depend on token format/claims that you use but may provide a helpful reference.
You can look at how token validation is recommended for applications secured by Azure Active Directory as a case study (and may be some other systems as well), then decide what works best for your case.
Brief explanation on how Azure AD example is relevant.. as validating received token is required just like in your case
When you develop any web API (or web app) secured by Azure Active Directory and use bearer token based authentication, the way things flow is very similar to what you explain above with your 2 APIs (just for understanding purpose, your authentication API is doing what Azure AD token endpoints would do.. i.e. provide a valid token).
To access the secure web API, calling application/client first interacts with Azure AD endpoints to get a relevant token for required resource, then it sends a request for actual resource along with the token in Authorization header to the web API. Now, the first thing web API does is to validate this token and only if everything is correct continue with execution to eventually return a response. I hope this matches at a high level with your flow.
What to validate?
1. Token Signature
The key used to sign the issued token is uniquely associated to the issuing authority, so nobody else can issue a token with your Authority's signature.
a. This helps to check that the token was in fact issued by Azure AD (or in your case your trusted STS, using the Authentication API you mention).
b. This also makes sure that since the token was issued and till it reached your web API, no body has tampered with it. If any attempt is made to change any information in the token, the signature will break.
2. Token Claims
This will depend on what claims/information you send as part of the token (for example, if same Authentication API issues tokens for multiple different APIs, then something like audience and issuer might make sense for you as well. Token validity time period using something like nbf and exp below are also pretty generic)
Taking Azure AD issued tokens as example here are the important ones that should be validated:
audience claim (aud), to verify that the token was intended to be given to your application
not before (nbf) and expiration time (exp) claims, to verify that the token has been received within it's validity period
issuer claim (iss), to verify that that token was in fact issued by the said authority. Notice this is a second way, apart from signature for the same purpose and generally both signature and issuer check are used together. (See Vittorio's blog)
nonce, as a token replay attack mitigation
tenant claim (tid), to verify the tenant. This is useful in case of multi-tenant applications.
Sample JWT Token from Azure AD
Actual Value: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1N... (a long encoded string continues) Decoded Value (you can check this easily using a website like https://jwt.ms or https://jwt.io):
{
"aud": "https://service.contoso.com/",
"iss": "https://sts.windows.net/7fe81447-da57-4385-becb-6de57f21477e/",
"iat": 1388440863,
"nbf": 1388440863,
"exp": 1388444763,
"ver": "1.0",
"tid": "7fe81447-da57-4385-becb-6de57f21477e",
"oid": "68389ae2-62fa-4b18-91fe-53dd109d74f5",
"upn": "frankm#contoso.com",
"unique_name": "frankm#contoso.com",
"sub": "deNqIj9IOE9PWJWbHsftXt2EabPVl0Cj8QAmefRLV98",
"family_name": "Miller",
"given_name": "Frank",
"appid": "2d4d11a2-f814-46a7-890a-274a72a7309e",
"appidacr": "0",
"scp": "user_impersonation",
"acr": "1"
}
How to do token validation?
Make use of standard libraries as much as possible. See the code implementation sample Manually validating a JWT access token in a web API. This one makes use of JwtSecurityTokenHandler.ValidateToken Method (JwtSecurityToken). Your case will depend on your token format/implementation etc.

Confusion around Access, ID, and Refresh JWT authentication

I've been developing my first REST API to serve as the back-end for a mobile application. I'm pulling info from different resources, and am a little confused when it comes to the token implementation (I'm using JWT).
The access token is used to ensure that the requester has access to the resource that is being called. My understanding is that I will then encode the user details in the ID Token, such that the relevant information can be returned. The refresh token is used as a security mechanism, to keep the user authenticated after the short-lived ID and access tokens expire.
The access token seems a little redundant, and maybe it is an interchangeable term for ID token? Can I just remove that part from my authentication scheme?
In the proposed scheme access and ID tokens are used interchangeably and do not provide any value over the other. All information provided in the access token can be stored in the ID token, or vice versa. The entire authentication scheme will then simply consist of an access token (containing both info on access permissions, and user info), and a refresh token (ensuring that users don't need to login again every t minutes).

OIDC - What's to stop someone from spoofing a JWT access_token?

When you authenticate with an OIDC provider you get back an id token and if you specified scopes for an API you get back an access token so that client applications can make requests to protected resources on the end user's behalf. Typically the access token is also a JWT.
But what is to stop someone from spoofing one of these access tokens, and creating one and passing it to an API? I understand there are safeguards to prevent modification because the signature will be different than what any validation logic is expecting, but what if a malicious user created a brand new one manually? Especially because these tokens can be validated 'in place' by any API that requires an access token (not all API's use the introspection endpoint... especially with a JWT). I do understand there is metadata around the signing keys for JWT's from OpenID Connect providers and that it is available in the OIDC discovery document. For example, here is Google's JWK metadata. Given that you have signing information publicly available, and JWT access token's can be validated without any requests to the OIDC provider, how are JWT's secure? What is preventing a person from creating one and passing it as a bearer token to an API that requires an access token?
But what is to stop someone from spoofing one of these access tokens, and creating one and passing it to an API?
Spoofing and reconstruction of signature is nearly impossible without the private key (assuming you are using asymmetric signing algorithm like RS256) that used for signing the original JWT.
The JWK information available via OIDC discovery document only contains the public key.
Also Use HTTPS for authorization / token exchange to avoid token sniffing.

Implicit flow, but requires access to multiple API's

Say I have a website secured with IdenityServer3. It uses the implicit flow so users are asked for their credentials. Now I'm facing a requirement where this application should access multiple (Web)API's on behalf of the user. These API's are secured using roles.
How do I go about this? Do I get one access_token for all by adding "token" to the ResponseType of the OpenIdConnectAuthenticationOptions? Do I have to change the Implicit flow (to Hybrid?) and manually request access_tokens for each individual API and add those to the user's claims, after authentication?
I'm not sure on how to approach this. Any help would be greatly appreciated.
You can keep using Implicit flow and add "token" to reponse_type (so it becomes "id_token token"). That will give you access token as well. Additionally, you can specify any scopes that those APIs might require in order to include claims that might have to be present for API Authorize attribute to allow the call through.
Once you have the response from IdentityServer in your application along with access token, you call the api with that token in the Authorization header.
I was able to resolve my requirement by moving to a Hybrid flow. For the response type I ended up needing "id_token", "token" and "code". I also had to retrieve the user claims manually in a separate request and manually add them to the user's identity, because adding "token" besides the "id_token" would remove all claims from the server response (to keep the token smaller).