recently I've started my project in SpringBoot and Kotlin and I wanted to create user management system. After a few searches, I've looked at this video regarding the implementation of JWT tokens for the authentication process.
Everything seems to work perfectly, besides the fact that currently, I need to pass the username and password in each authentication - which means that the client calling the API needs to either store it or ask for it each time.
After looking in this article it seems that option 3 is actually what I'm looking for. From what I'm understanding the process of authentication should be as follow:
a client calls the authentication method
2 tokens are returned - 1 with a short period which is the authentication token, and one with a longer period which allows renewal of the authentication token.
Once the authentication token is expired, the client will as to renew the expired token using only those tokens without the original details of the user.
I have 2 questions in this regards:
From the security perspective - what will happen if a hacker "catches" those 2 tokens? that basically means that the hacker can authenticate as this user from now on without any problem - isn't it?
More or implementation question, from what I could see, jjwt does not support this functionality (as I can read the information from the token as long as the token did not expire). From my search, I couldn't find something that gives it "out of the box" - is there a standard that to implement this functionally?
Related
Background
We've developed a CMS in ASP.Net Core 2.2 a few years ago for company internal day-to-day stuff. It holds several hundreds of users and I'm considering adding an android app to it. The android app should mainly serve as a front end to existing logic in the ASP.Net controllers. The benefits of the Android app I expect to have are:
Better UX
User not having to type the password for each session (the ASP.Net page logs them out after 20 or 30 minutes of inactivity)
Push notifications etc.
Existing infrastructure
The CMS has already an API (JWT) which is used by several backend services and could easily be expanded to be used by every user through the android app. Unfortunately I'm not a professional developer in general and are quite new to Java especially, so please bare some patience with me as I'm asking for a general advise of how to handle the authentication in the android app (Java).
I read about curity.id and stuff like this, but IMO this would be kind of overkill to the whole project and would also require major changes to the already working fine ASP.Net website.
Now my question is whether the following would be an acceptable practice to do:
When the user opens the app for the first time, I ask him for his username and password (and optionally 2nd factor).
I make an http request to the API of the CMS in order to authenticate the user and get back the JWT token on successful authentication.
The JWT token gets stored in some way (e.g. SQLite DB) and is being used for subsequent requests being made to the API.
Since the the lifetime of the JWT token is also limited to a few hours and the goal is to keep the app running without prompting the user for his credentials all the time, I need to store the password also in some way in the app or a DB on the android device. This is actually the part which confuses me the most and I have no clue how I would store the password in a safe way, so it may be used again for authentication requests down the road. (I'm aware that if the user uses 2nd factor there is no way around but to ask for the 2nd factor every time the token expires or to exclude the device or so, but this should not be within the scope of this question)
So I would be grateful to get some judgements about this approach, if it is acceptable in general and what would be the best way to do about point #4.
Thanks a lot
Do not store password
If store (and frequently send) passwords you defeat the entire purpose of a token. The token itself exists as a disposable means to substitute the primary authentication (yet is cryptographically sufficiently strong to replace it without reducing system security).
SIMPLE: Use a long token
Create a new authentication API (or add some parameters to the existing one) to enable the mobile APP to require a long-lasting token (for example 6 months). If you then store this token in the SecureStorage of the device you can be quite confident it can not be extracted from there.
Remember that with this solution, you lose control over the authentication. This is not the best practice but is the easiest solution to adopt in the case of existing JWT infrastructures that use only one token.
RECCOMENDED: Create a renew token system
Once you issued a token you cannot revoke it. Hence the main issue introduced by the 'long token' solution is that if a device gets lost it can not be de-authenticated. This can be solved by a renew token:
You create an authentication API that after the confirmed authentication it creates a unique sessionId, stores it in the DB, and issues a special token that contains the sessionId among other information. This is called the renew token. This special token can be used only for the exchange API
You then create an exchange API, that expects a renew token in exchange for a normal token (that can have the same properties your current token has). This API will always check if the sessionId present in the renew token is still marked as active in the DB otherwise will not issue the normal token.
This approach allows you to have very long expiring tokens but still have control to remotely log out the app (by masking the sessionId as deactivated in the DB) and even monitor how frequently the app is used.
I am creating JWT based authentication in my application deployed on Wildfly server.
For any request arriving at my server, the request is validated to be having a Basic Auth header. In cases, where I don't get the header, I get the POST request payload with Username and Password. I wrap the request with HttpServletRequestWrapper and add Basic auth header to it and call another application which returns 200 OK after authenticating with Login module automatically. Also, I have a singleton with a ConcurrentHashMap<String, MyUser> in which I store user logging in and the User object with other user details.
On receiving 200 OK, I create a JWT and return it to the client. The client then sends JWT with each request to the EJB services. I get the Authorization header as Bearer *******. I decode the user from the JWT token, then using the map, I again create Basic Auth header and send request to another application which exposes the services only when the Basic Auth header is found.
My doubts:
a) In 1) above, I am sending POST payload, which I can see in Browser network logs. And, I can plainly see the username and password being sent. Is there something extra that needs to be done to prevent this. Or, this wont be visible to other users? Or, https is the solution?
b) In 2) above, anybody can get my token if they are doing MITM attack or know my credentials as in a).
Then, they can seamlessly get access the services. Can, I add IP address of the requested user and store in the map and while validating JWT, validate the IP as well so I can prevent MITM attacks?
There are other posts on SFO and Okta Developer I visited, and they answered about why and how JWT is insecure. But, I am curious, if I can do anything extra in my case which I am missing?
There are several levers you can pull here to vary the amount of security in your JWT setup. Consider the following options:
No SSL and no encryption of your JWT. In this case, your JWT would be visible not only to the user, but also to any man in the middle who is listening (either legitimately or maliciously). This means that any sensitive information contained within the JWT might be visible to others. However, the JWT still cannot be hacked, even in this maximally unsecure setup. The reason for this is that every JWT contains a checksum, which is a hash of the token's content, signed using a key which only the server has. So even if a MITM or user messes around with your JWT, the server would immediately recognize that the token has been hacked when the checksum fails.
SSL is enabled, but the JWT is not encrypted. This offers the same protection as above plus it also makes it very unlikely that a MITM could ever get a hold of any JWT going to or fro in transit. Therefore, sensitive information inside the JWT would only be visible to the recipient.
SSL is enabled, and the token is encrypted. In this maximally secure setup, no MITM can see the JWT in transit. Also, once received, the user must decrypt the JWT using some key. Therefore, even if the JWT ends up in a cookie, in theory it is not a security risk, provided that it can't be decrypted by anyone other than the intended recipient.
Most of the time, we will tend to lean towards option #2, but we will not put sensitive information into the JWT. There is no need for storing a login password in a JWT, as the token itself can prove authentication. The JWT is tamper proof, and if someone malicious finds a way to see claims such as the expiry date or username, we generally don't care.
1 -> Yes HTTPS is the only solution no matter how much secure you make your APIs without
HTTPS MITM attacks are easy.
2-> With multiple benefits come some tradeoffs, JWT leak or stealing is a real problem, and it is not easy to solve, adding an IP address to JWT is not a good idea as they get to change a lot and you user have to authenticate again.
What you try is a quick JWT expiration in this case user has to authenticate every time the token is expired, now for solving this problem you can use a refresh token, it is not so hard to implement.
wanna get deeper here you go --> https://www.youtube.com/watch?v=DPrhem174Ws&t=1089s
Right now, my company runs a java app that uses IMAP and SMTP to read/send e-mails without user interaction. The authentication protocol we use is Basic Authentication.
Microsoft has announces End-of-support for Basic authentication, which will be replaced by Oauth2. Unfortunately, they did not provide a clear solution for applications without user interaction.
https://learn.microsoft.com/en-gb/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth
One of my ideas is to have users authenticate themselves (using Oauth2 authorization code flow) just once, the first time they start the service. Then, refresh tokens will be used to get access tokens without user interaction. It is very important that the connection is not broken because of short expiration dates or revocations. There will probably be nobody around to re-authenticate if the app will be unable to get new access tokens.
I cannot seem to find any information about expiry dates for these tokens (if it is possible to set it). The ideal would be that they never expire...
Does anyone know more about this? It would help me a lot to know whether my idea is a viable approach.
Access tokens can be automatically refreshed. But to me more provider specific I did find some info on refresh token expiry here
https://learn.microsoft.com/en-us/linkedin/shared/authentication/programmatic-refresh-tokens
According to this link LinkedIn tokens access and refresh expire after a max of 365 days.
Also if you see this link it says that the refresh token expires in 90 days if inactive but doesn’t say anywhere that it will expire if active.
https://learn.microsoft.com/en-us/office365/enterprise/session-timeouts
Can you provide a reference to the API that you are trying to use so that I find some more info on it (maybe)
I am working on a solution to read log files from the GCP for an internal process. However, i am having a difficult time trying to generate an Auth Token for the request to grab the logs needed. This is more of a flow\context question rather than a whats wrong with my code one
The key issues i am having is that i do not want to prompt for web-browser authentication. I want to be able to do this all through API request and have no user interaction. Everywhere i have looked and all implementations i have tried, i am prompt for user interaction in some way and that is just not feasible for this solution.
How can this be achieved?
We do not have IAM enabled, so i cannot generate a JWT token.
I am trying to do this through using a Service Account created using client id and client secret.
I have tried getting a "code" to pass into a request to generate an authorization token, but that has been prompting me for user authorization in the browser which will not work, even when I add the query parameter 'prompt' or 'approval_prompt' to none or force.
I feel like i am missing one crucial piece to be able to achieve this flow and any help/guidance will be greatly appreciated.
There are several ways to authenticate API calls. If you want to do it without user interaction, you will need to use a Service Account (more info here). The process would be the following:
You use the client ID and one private key to create a signed JWT and construct an access-token request in the appropriate format. Your application then sends the token request to the Google OAuth 2.0 Authorization Server, which returns an access token. The application uses the token to access a Google API. When the token expires, the application repeats the process.
For this, you can use Client Libraries or you can do it manually with HTTP requests directly. In the docs there is a guide to do so.
My company is building a RESTful API that will return moderately sensitive information (i.e. financial information, but not account numbers). I have control over the RESTful API code/server and also am building the Android app. I've setup the API to use OAuth 2 with authorization code grant flow (with client ID and secret), and I auto-approve users without them having to approve the client since we own both client and provider. We use CAS for SSO and I am using this for the Authorization server as part of the OAuth 2 process when the user logs in to retrieve the token.
I am contemplating various ways to secure the data on the Android app. I've concluded that storing the client id and secret on the device is definitely not going to happen, but am thinking that storing the auth token might work, since it is only risk to the individual user (and really only if they happen to have a rooted phone).
Here are two options I have thought of. They both require me to have a sort of proxy server that is CAS protected, does the dance with the API server, and returns the auth token. This gets rid of the need for storing the client id and secret in the app code.
Here are what I've come up with:
1) Require the user to enter their password to access data each time they startup the App. This is definitely the most foolproof method. If this were done, I'd probably want to save the userID for convenience, but in that case couldn't use the CAS login (since it's web-based). I might be able to use a headless browser on the backend to log the user into CAS and retrieve the token based on what they enter in the Android form, but this seems hacky. Saving the userID is similar to what the Chase app does (if you happen to use this one) - it saves the userID but not your password between sessions.
2) Store the auth token on the Android device. This is a little less secure, but almost foolproof. When the user starts the app for the first time, open the webpage to the CAS login of the proxy server that returns the token (similar to https://developers.google.com/accounts/docs/MobileApps). After the user logs in and the token is returned to the app, encrypt it and store it private to the application. Also, use ProGuard to obfuscate the code, making the encryption algorithm more difficult to reverse engineer. I could also work in a token refresh, but I think this would be more of a false sense of security.
3) Don't use CAS but come up with another way to get an auth token for the service.
Any advice of how others have implemented similar scenarios (if it's been done)?
Thanks.
Well the reason why standards like OAuth are developed is that not everyone has to rethink the same attack vectors again and again. So most often it is your best choice to stick to something already available instead of baking your own thing.
The first problem with clients that are not capable of secretly storing data is that the user's data could be accessed by some attacker. As it is technically not possible to prevent this (code obfuscation won't help you against an expert attacker), the access token in OAuth 2 typically expires after short time and doesn't give an attacker full access (bounded by scope). Certainly you shouldn't store any refresh token on such a device.
The second problem is client impersonation. An attacker could steal your client secret and access your API in his own (maybe malicious) app. The user would still have to login there himself. The OAuth draft there requires the server to do everything it can to prevent this, but it is really hard.
The authorization server MUST authenticate the client whenever possible. If the authorization server cannot authenticate the client due to the client's nature, the authorization server MUST require the registration of any redirection URI used for receiving authorization responses, and SHOULD utilize other means to protect resource owners from such potentially malicious clients. For example, the authorization server can engage the resource owner to assist in identifying the client and its origin.
I think Google are the first to try another approach to authenticate a client on such devices, by checking the signature of the application, but they are not yet ready for prime time. If you want more insight into that approach, see my answer here.
For now, your best bet is to stay on the OAuth way, i.e. having the access token, client ID and client secrect (when using the authorization code grant flow) on the device, and configure your server to do additional checks. If you feel more secure obfuscating these, just do it, but always think of it as if these values were publicly available.