So 2 questions.
are the UserPoolId and AppClientId secret for Aws Cognito user pools?
if so how do you keep them secure? All the libraries I have seen (aws-amplify/amazon-cognito-identity-js) seem to be exclusively client based.
What am I missing here. It seems like you give any malicious user with JS access keys to the Cognito kingdom with these 2 pieces of information.
They are not secret.
In fact, the ID token contains the iss claim (property), which is the User Pool ID, and the aud claim, which is the App Client ID.
The Access token contains the iss claim, which again is the User Pool ID, while it's the client_id claim which represents the App Client ID.
Should either of these tokens be intercepted by a bad actor, then they can decode the tokens, as they are just base64 encoded (not encrypted).
However, just knowing these 2 pieces of information is not usually terribly useful for an attacker, as long as the JWTs are validated correctly.
It does not give the attacker access to the User Pool itself as that requires AWS credentials, which are only assigned to users, or identities that have already been properly authenticated (and then issued credentials e.g. by ID Pools).
In terms of accessing an api, an attacker might want to modify the payload in some way in order to change the data in the request. For instance they may want to change a hypothetical role claim from user to admin in order to escalate privileges and access areas that they shouldn't. This is mitigated by correctly validating the JWT tokens server-side to ensure that the payload has not been tampered with.
Another type of api attack could be to use a token that was correctly authenticated for one api to access another api (JWT substitution). This is mitigated by validating the iss and aud claims in order to confirm that the JWT was specifically issued to the expected User Pool and App Client.
Related
After authenticating with a provider, an application will often receive both an ID token and an access token on behalf of the user. Now it seems there are two ways to assert who the user is.
Verify the ID token and then read the ID token.
Pass the access token to the userinfo endpoint and read the JSON response.
Both seem like acceptable avenues, but are there certain scenarios in which one or the other should be used?
If you have both tokens and the ID token contains all info you need, you can use either way. Below are few differences that came to my mind:
Verifying and reading an ID token can be done without accessing its OAuth2 server (if you have its certificate already downloaded locally), which makes it faster and there are fewer possible errors to deal with - no network requests.
If the user info was changing often, an ID token could contain obsolete data, but it's hardly ever a case.
Access tokens can be revoked (ID tokens cannot), so if you need it, they will do the job better.
Apart from the technical differences there's a semantic difference as well: the id_token and the info in the there represents and identifies an authenticated user. That user is "present" and logs in to the application.
The access_token and the information returned from the userinfo endpoint represents information about the user who issued the access token to the entity that presents it. That user doesn't need to be "present" or logged in (anymore).
An id_token is typically "one-time usage" and an access_token usually can be used for a short period of time.
Now in the case that both tokens are issued and received at the same time when a user logs in with OpenID Connect, the two overlap.
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.
I'm trying to implement OpenID Connect Implicit Flow. The frontend Single Page App passes the ID Token down to the backend server (using Authorization header) where I need to validate it.
The documentation requires me to check that I trust the audience of the token (aud & azp fields). I'm struggling to understand the significance of this validation step and what are the security implications of not doing so. Why should I distrust the token if I'm not the intended recipient?
My reasoning is that if I trust the issuer it doesn't matter who was the token issued for. I would expect the claims to be the same for any clientId (is this wrong?). Ideally when I pass the ID Token around my microservices all they should know is what issuers to trust (and use discovery protocol for figuring out the keys).
What is the attack vector if I skip this validation step?
The issuer could be issuing tokens to different applications and those applications could have different permissions. Not checking the audience would allow an attacker to use a token issued for application A at application B and may lead to permission elevation.
To your suggestion: the claims may indeed differ per Client.
Here's another reason: If you check that aud claim only contains your client, it prevents other apps' stolen tokens from being used on your app. If a user's token gets stolen from another app, nobody will be able to impersonate the user on your app because the aud claim will not be correct.
I'm answering this for posterity.
You should check the issuer and if your client_id is the only one in the audience if you are receiving tokens from an external OpenId Provider. One that could have more than your client.
Claims are not global to the OpenID Provider, they can be per-client. A user can have "Admin" role on app-A, gets a token there, then tries to send app-B (your application) the same token hoping that your are not checking to which client it was issued for (its audience).
I have a web site that requires user to log in by providing their email and password to gain access token, where the access token token is used to access api.
User can then gain access to read/write with the scope provided by the access token.
So, what I would like to understand here is that what roles does client id and client secret play in such a case, and what benefits can implement client id and client secret provide? Because i really do not see the need of implementing client id and client secret since user may just use access token to gain access right.
You don't have to issue client IDs if you can achieve what you want to do without them. For example, if you have privileges to handle email (user ID) and password directly, you don't need a client ID.
In general, client IDs are needed only when you want to allow (third-party) client applications to access (your service's) users' data with restricted privileges. In this case, each client application must be given authorization by a user. As a result, your system will need client IDs to know which client application the user has granted permissions to.
BY using client id/ secret you can potentially control which clients are allowed to connect to your API and make decisions such as rate limit them or block them. This is the way that e.g. Twitter can ensure that no other twitter client is allowed to have over 100,000 users.
I know there are already many posts about Oauth, Oauth2, JWT, etc.. I have read many and I more confused than ever so I am looking for some clarification. I will propose my view on the subject and I hope somebody can tell me if my implementation is secure enough or what I am doing wrong and how to improve it.
I am building an API Rest server for serving my resources to my users. Let's suppose it is a bank app where users can deposit, withdraw and transfer money.
I am using nodejs, hapijs, jsonwebtokens, and bcrypt for my server. I want to implement two token authentication flow (Oauth2).
This is the way I am doing it:
User logs in to the auth server by giving some credentials (username and password).
The server verifies the user's credentials, if they are valid, it will grant access to the user and return a refresh token and an access token.
These tokens are saved into the local storage of the browser or mobile device.
The access token:
is signed as a jsonwebtoken.
contains issued date, expiration date (5 min), user data (id, username).
The refresh token:
is signed as a jsonwebtoken and encrypted with bcrypt.
contains a unique identifier
may contain an expiration date
is saved in the database.
As long as the access token is valid, that means, it has not expired and contains valid user data, the resource server serves the user the requested resources.
When the access token is no longer valid, the auth server requests the client to provide a refresh token in order to issue a new access token
The server receives the refresh token from the user, decrypts it, compares it to the one in the database, checks if it has been revoked, and checks its unique identifier.
If the refresh token passes all tests, the server issues a new access token to the client.
If the refresh token fails one test, the server requests the user to re-authenticate.
Notes: I am trying to avoid the usage of cookies.
Questions:
If the user is able to steal an access token, I guess it can also steal the refresh token. So, how can I make the refresh token more secure?
Is my perspective of the Oauth2 flow correct?
What can I improve?
Am I missing something?
The reason OAuth2 is so confusion to many people is because it uses different authentication flows depending on what kind of client is used.
OAuth2 distinguishes two client type, confidential or public. Next to that, there are 2 grant flows that are redirection based (auth code and implicit) which are meant to be used with a browser or browser control.
The other two flows (resource owner password and client credentials) are meant to be used from non-browser apps (CLI, background services, trusted mobile clients).
I've described the different flows and when to use them in more detail in this answer here.