How can I make Spring Security OAuth2 work with load balancer? - java

We currently have 4 Spring applications that use Spring Security Oauth2 project for authentication. The applications are REST APIs that are consumed by other internal applications in the company I work for.
Everything was working fine in the development and QA environments as we were not doing load balancing, now that we are in pre-production we are facing an issue with the load balancer (LB).
This is the workflow for this issue:
Client sends request for the oauth token
LB redirects the request to Box 1
Box 1 authenticates and returns a valid Bearer Token
Client receives the token and store it for using through the sesion
Client sends request for a service in the REST API adding the previously retrieved token to the headers
LB redirects the request to Box 2
Box 2 fails to authenticate as it does not recognize the token and returns an Invalid Credentials response
We are using an in memory user store:
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore" />
Is there a way to make different boxes to share the same token store? I know there is a JdbcTokenStore that can be used to persist tokens to the db, but I would prefer to avoid persisting tokens as these applications point to a legacy database that only stores business information.

I'm a bit late to this question, but maybe this will help someone searching for a similar answer. There are two major things you need to watch out for when using multiple oauth servers on a load balancer:
As #chris-h alluded to in his answer, you need to ensure that the information backing an access token issued by any of the oauth servers can be read (and trusted) by any of the other oauth servers. You can use a JDBC token store as he suggested, but this has the disadvantage that if Server A has to validate an access token issued by Server B, it always has to hit the database to do so.
A better solution (IMO) is to use JWT access tokens, where all the information needed to validate the token is encrypted within it. So long as all oauth servers use the same encryption key, they can read the data in each other's access tokens and trust that the data is valid since it is encrypted. The advantage is there are no database calls needed to validate an access token. The disadvantage is that there's no easy way to invalidate an access token once it's been issued. If you ever wondered why refresh tokens are needed when you could just increase the expire time of the access tokens themselves, this is the biggest reason.
The second thing to be aware of is that the Spring oauth implementation uses sessions to keep track of where the user is an the authentication process. If you aren't careful, you could wind up in an "endless loop" scenario. Suppose you have two oauth servers--Server A and Server B:
The user goes to a web page or service which requires authentication, so gets redirected to "foo.com/oauth/authorize". The load balancer sends this request to Server A.
Since Server A doesn't have any session information which proves the user is already authenticated, it redirects the user to the login page at foo.com/oauth/login. The redirect goes back through the load balancer, and since the load balancer is working in a "round-robin" fashion, this time it sends the request to Server B.
The user logs in successfully, so session information is written to keep track of this. This session information is only known to Server B.
Since the login was successful, the user gets redirected back to "foo.com/oauth/authorize" to continue the authentication process. Once again the redirect goes back through the load balancer. Since the load balancer is working in a "round-robin" fashion, this time it sends the request to Server A. But Server A doesn't know about the successful login that happened on Server B. Go back to step 2!
Probably the best (current) solution to this problem is to ensure that your load balancer supports "sticky sessions"--that is, once it sends a particular user to Server A or Server B, it always sends that user to the same server for a while.
A better solution might be for oauth implementations to not use sessions at all. Instead, use encrypted data passed as a parameter to /oauth/* that denotes where you are in the login process. Similar to how JWT tokens work, the information can be trusted by all servers if they all share the encryption key.

All authentication servers and all resource servers must share the same tokenStore in order to validate tokens.
That means switching to the JdbcTokenStore or a custom TokenStore implementation which is capable of sharing tokens between servers in some way (shared datastore, NFS shared filesystem, etc.). Of course, it may even be possible to share an InMemoryTokenStore using Terracotta or a similar memory-sharing product if you are willing to go that route.

Specifically for implementing authorization code grant to work with load balancer I stored Spring sessions in Redis. Redis being a shared resource ensures Spring session information is shared among all the running instances of the Spring application.
There is an alternative approach evaluated by having sticky sessions in place at load balancer which is also a good option but this implementation requires session replication in order to maintain HA.
Sticky Sessions
IMHO Centralized cache store implementation gives more control at the application end having all configurations at the application with guaranteed HA without any additional overheads.
With Cache Store
Below is how the sessions are stored in Redis
application.properties
spring.session.store-type=redis
server.servlet.session.timeout=3600s
spring.session.redis.flush-mode=on-save
spring.session.redis.namespace=spring:session
And for overriding HttpSession management
#Configuration
#EnableRedisHttpSession
public class HttpSessionConfig extends AbstractHttpSessionApplicationInitializer {
#Bean
public JedisConnectionFactory redisConnectionFactory() {
...
JedisConnectionFactory jedisConFactory = new JedisConnectionFactory(redisConfig);
...
return jedisConFactory;
}
}

Related

Spring OAuth2 automatical login with Keycloak

I am not well experienced in Spring Security so my questions may sound a little weird.
My environment consists of 4 parts:
Keycloak authorization server (authorization server)
Spring resource server application (resource server)
2 OAuth2 client applications (clients)
I configured well everything, the OAuth2 and SSO work fine with all of the applications.
Besides the OAuth2 configuration, there is more to do. When my user uses the client application, he will provide his credentials on a form when trying to access the resource. But my model (for the resource) is more complex, and I have to do some background (housekeeping) tasks from time to time. These tasks are not done by the user(in the name of the user) but requested by the client application itself. My question is, how to access the resource server's endpoint from my clients in the name of the client application itself.
My first thought is that the client application should store a long-term JWT token and use this token during the calls. But I am not sure about it. I don't want to waste time on bad paths that's why I am asking in the first place.
The crucial and most doubtful parts for me:
How to get a long term JWT from Keycloak
How to automatically login a background call (via SSO and OAuth2)
How to store the credentials for the automatic login.
Thank you for your ideas or advice.
You should never have long term tokens, because that is a security risk.
If someone manages to get hold of a long term token, they then have long time to use it maliciously.
I can't guide you how to set this up in keycloak etc. But i would use the Client Credential Flow to authenticate the server, and get a token each time one of these "housekeeping jobs" needs to be made.
You can read more about the flow here Client Credential Flow

What is the most scalable/secure way to handle refresh tokens in a JWT authentication setup?

I am building my own authentication microservice and although I have the main setup in place (generating access tokens etc.), I am a bit lost when it comes to refresh tokens.
I feel there are a lot of different way to handle this.
You can either store them in Redis or in the database.
You can use a whitelist or a blacklist them
Right now, my idea is to add another database table that links a valid refresh token to a user entity. When a user hits the logout endpoint, the refresh token gets destroyed.
I was wondering if this was a good solution and otherwise, if there are other possible solutions to consider. I have seen a number of articles when googling but they stem from anywhere between 2015 and 2019, and they all have different approaches.
The issue with refresh tokens is not so much where or how you store them on the server side, as well if and how you store them on the client side.
It all depends on whether you can trust your client (software using the token) to keep secrets. You only want to issue refresh tokens to a client you can trust to keep these tokens secure. Typically, this means only issue refresh tokens to confidential clients, i.e. web applications that run on a web server. These clients can also have their own (client) credentials to authenticate themselves with when using the refresh token.
For public (non-confidential) clients, like Single Page Applications, some OAuth2 libraries use an hidden IFRAME and a cookie session with the authorization server to issue new access tokens.
So, the answer to your question depends on what kind of clients you will be using.

Authenticating, using and reusing password and Kerberos credentials in Java

Warning: This is an extremly long post, simply because I don't know how to explain our specific problems any other way (sorry).
Short version:
We are building a very modular suite of Java applications (both client and server side). Some clients have to authenticate against the servers, and the servers can authenticate against different types of user stores. Servers can also call other servers on behalf of their authenticated user, i.e. using their credentials. This has worked fine so far using simple username/password credentials, but now we have to optionally support SSO via Kerberos (and later other authentication systems). So far, we're failing (partly miserably).
Long version:
Our central Java library for handling principals is called User Access Layer (UAL). This library is used by both client and server applications and provides three types of funcationality:
Authenticate users based on their credentials (results in a failed authentication or basic information about the user, i.e. at least a login name or ID)
Perform principal queries
Perform principal modifications in the user store, if the backend supports it
(2) and (3) can be performed either using credentials specified by the caller or (if the backend supports it) as a technical user.
Actual access to the user store is handled by the configured backend. We provide a number of backends (LDAP, WebDAV, a custom JDBC database, a custom XML file) which can all be configured through a unified configuration file, usually named useraccess.xml. This file defines which backend (or backends) should be used and how it is configured, e.g. LDAP server and structure data for the LDAP backend or the database URL and database user credentials for the database backend. All backends implement the same interfaces so the application code is independent from the backends configured for a particular installation.
In our product, UAL is used by two different types of applications:
Clients (both command line/desktop clients and web frontend applications opened by a user in a browser). These applications use UAL to perform principal queries (e.g. our file browser application when modifying the ACLs of a WebDAV resource) or principal modifications (we have a web based user management application). Depending on the application, they get the credentials used for their UAL operations in one of the following ways:
a. User provides the credentials on the command line when calling the application
b. Application opens a dialogue and prompts the user to input credentials when a server access requires them
c. A login screen displayed when the user first accesses a web frontend application
Servers. which use UAL to:
a. Authenticate a user based on the credentials provided via the used protocol (e.g. HTTP/WebDAV or SOAP/WSSE)
b. Perform authorization for a user based on the user's (or the user's groups') attributes
c. Perform UAL operations (queries/modifications) on behalf of the client
d. Call other servers on behalf of the client, i.e. passing another server the user's credentials (e.g. via HTTP/WebDAV or SOAP/WSSE)
Until now, all our credentials were user name/password pairs, which worked fine as long as we made sure to keep these credentials in the user's session where necessary to later use them for accessing another server. We could do that every call that retrieved credentials or passed them to a server went through some of our code which could store/provide the necessary credentials.
Everything has become much more complicated with the requirement to support SSO via Kerberos. We've tried several approaches and modified our code base several times, but every time we believe to be on the right track, we realise that there's one spot we overlooked which cannot work the way we intended.
What makes things so confusing is that we have to handle credentials in several different ways. These are some of the ways we have to provide credentials to a server:
Via Apache HttpClient when accessing a HTTP/WebDAV server
Via SOAP/WSSE when accessing a web service
Via JNDI when accessing the LDAP server (in UAL)
And some of the ways we have to receive and verify credentials from a client:
As a login module in Apache Jackrabbit
When receiving a SOAP/WSSE message in one of our JAX-WS web services
A very common use case for us is the following:
Client calls server A via SOAP, providing credentials
Server A retrieves the credentials from the SOAP message and verifies them (responds with an error if they are invalid (authentication error) or the user is not authorized to perform the desired operation (authorization error))
Server A then calls WebDAV server B on behalf of the user, passing the user's credentials so that the WebDAV operation can be carried out with that user's permissions (and using that user's name and other attributes)
Server B retrieves the credentials from the HTTP message and verifies them
Server B then performs a principal query on the user store C, passing the user's credentials to the user store (depending on the configured UAL backend, this may simply compare the user's name and password to those in the user store XML file, or use them to establish an LDAP connection and query the LDAP store as that user)
And the problem is: There seems to be very little information on the internet to help with our specific problems. For starters, most resources simply describe how to setup a JAAS configuration file for a container to let its web applications perform user authentications. But our code has to run on both clients and servers and use one configuration file to specify the user store config for both authentication and principal queries/modifications. Furthermore, this has to work, with the same code, with user name/password credentials against a variety of user stores (some of them custom written) and with Kerberos (and later other) tickets against an LDAP server. And finally, it's not enough to have an authentication library which reliably tells us that user has provided the correct credentials (as many JAAS login modules seem to do), since we actually have to keep the user's credentials for further calls.
Since Apache Jackrabbit, which is the base for one of our core components, needs us to configure a JAAS login module, and there already are JAAS login modules for LDAP and Kerberos authentication, we have successfully modified UAL to perform all its authentication tasks via JAAS. For this we had two write login modules for our custom backends, and I had to implement my own LDAP login module since the default JAAS one would successfully authenticate the user against the LDAP server, but then throw away both the user's credentials and the LDAP context, so we couldn't perform further LDAP query using the same credentials. All our own login modules store the credentials in the authenticated subject's private credentials set, which is what JAAS's default Kerberos login module does as well. Using the resulting subject, we can then perform user queries. This works with all our backends and with both passwords and Kerberos tickets.
We were also able to modify our SOAP services to extract the credentials from the SOAP message. In the case of password credentials, we can simply pass them to JAAS when the authentication callback asks for credentials. However, there doesn't seem to be a way to do the same with a Kerberos ticket. Instead, our SOAP services currently handle those on their own, passing them through the necessary GSS API calls to verify the ticket, retrieve a matching ticket for the SOAP service's configured service user, and create a Subject containing the credentials and user information. Using this subject, we can then perform queries/modifications through UAL. However, this not only means that our SOAP services completely bypass UAL when authenticating Kerberos tickets, they also need some Kerberos configuration data (the service user name, the realm and the keytab file) in their own configuration, in addition to the useraccess.xml which already contains the same data (but not directly accessible to a generic UAL client because these settings are specific to the UAL LDAP/Kerberos backend). Obviously, things will only get worse when we add support for other ticket based authentication methods and they also have to be manually implemented in each SOAP service in addition to the UAL backend that actually handles the user store access.
Worst of all, we're still unsure how to get all this into our Jackrabbit based WebDAV server. Jackrabbit needs a login module, which should be fine for handling user name/password credentials but (as far as we can tell) fail for Kerberos tickets. We could probably fetch those manually from the HTTP headers, but that won't stop Jackrabbit from calling the login module and the login module from failing because it will still ask for a password and then fail to authenticate against Kerberos without one.
I can't shake the feeling that either our approach or (quite possibly) our understanding of how all these pieces should fit together is fundamentally flawed, but nothing we can find on the net spans enough of our requirements to indicate what we're doing wrong (or, more importantly, how it should be done to make it right). Because of the complexity of even describing our problems I so far shied away from posting this as a question, but if you've read this far and can give us any pointers on how to resolve this, you could save us from weeks of frustration.
You can remove everything after
Until now, all our credentials were user name/password pairs, which worked fine as long as we made sure to keep these credentials in the user's session where necessary to later use them for accessing another server. We could do that every call that retrieved credentials or passed them to a server went through some of our code which could store/provide the necessary credentials.
You problem is plain simple: you need credential delegation with Kerberos. The basic technique is quite simple. Since you have multiple problem areas here, I would recommend to break them up to have you problem solved:
OS/environment configuration for Kerberos credential delegation
How to request a delegable service token
How to retrieve the delegated TGT from the client
How to reuse the TGT from the client and request another service token
Since your inbound channel is HTTP, here are the answers:
If you are in an Active Directory environment, request your admin to set "trusted for delegation" on the machine account which will accept the GSS security context.
This is a bit tricky because it depends on the client language and library. In Java it is as simple as setting GSSContext.requestCredDeleg(true). Eloborate on the rest.
Inspect the code of my Tomcat SPNEGO/AD Authenticator library, I am extracting the client's TGT and storing it in the Principal implementation served by HttpServletRequest#getPrincipal method.
Assuming that your backend client library supports GSS-API properly, there are basically two ways to do it: (1) explicit credential usage: pass the delegated GSSCredential instance to the client lib and it should the rest. (2) implicit: Wrap your client action in a PrivilegedAction, construct a Subject with the private GSSCredential and invoke Subject.doAs with both. JAAS will use the implicit credential from the thread's subject and perform the operation on behalf of your user.
It seems like you haven't even reached point 2 or 3 yet.

Sharing REST tokens between servers

I have a requirement for a REST API that has token-based authentication: we will have replicated application servers with a load balancer, since tokens are generated by one server when a user is authenticated, and different requests from the same client can be handled by different servers, is there a generic technique or technology to share those tokens between the different servers?
About technologies, we will be using the Java stack, more specifically Grails.
About the application servers, we might have more than one database. This comment is important because discussing with colleagues, someone suggested to manage the token sharing using the same database from all application servers. I'm looking for a solution that doesn't need a centralized database, that let us scale on the DB side.
When using token based authentication, there's a server that authenticates the user and issues a security token. Authenticating the user can be done in many ways (verifying username/password against a database, verifying a certificate on a smart card etc).
Once the token is issued and signed by the authentication server, no database communication is required to verify the token. Any service that accepts the token will just validate the digital signature of the token.
The client (caller of your service) is responsible to send the token along with the request. So no matter which server behind your load-balancers handles the incoming request, it only needs the public key associated with the signing key to verify whether the request is valid.
Which security protocol to chose depends on the requirements you have. OAuth is used often for internet applications. WS-Federation and SAML-P are used a lot in enterprise environments.
As far as I see JWT (JSON Web Token) is supported in grails - it seems that this is what you're looking for. Basically you need to separate the authentication server as in this image. Authentication verifies the user/pass being sent and issues a token that is easily parseable without any further access to DB. To only thing that needs to be shared is the key that will be used to decode the incoming JWT. See, how it works.

Authentication token storage recommendations

I have a case where non-interactive devices have to push data to a server periodically over HTTP. I am thinking of taking an auth token approach to verify the validity of requests from these devices.
First the device wakes up and initiates a ssl connection and submits its credentials to the server; the server verifies the credentials and generates a SHA based token, based on the credentials + some random input, and sends the token back to the device
This token must be present as a header in each http request that the device sends up. The server will use a servlet filter that looks for this header and filters out messages that don't have it.
There is no sensitive information transferred, I just want to make sure that the device talking to the server is a valid one, and not someone trying to mess around with invalid data. (Wannabe hackers, script kiddies etc..)
The token needs to be stored somewhere where multiple 'nodes' can verify that the request is valid - where do you recommend doing this ?
I can think of 3 approaches
1) Have a separate web service that maintains tokens and does the authentication ( I cringe on the performance overhead of this for each request)
2) Maintain a Set of authenticated token in the Session, and let the servlet container take care of it using the built in clustering support (Not sure if this is the most fool proof way )
3) Use a database to store the tokens and verify it (Considering Redis for this)
Also, I think this approach has a vulnerability of allowing man in the middle attacks, but since the client sends data only for a few minutes I am taking a chance, any better approaches would be welcome.
My opinion:
1) Have a separate web service that maintains tokens and does the authentication
If it's about performance and you have to maintain a lot of devices, I agree that this may become a performance bottleneck.
2) Maintain a Set of authenticated token in the Session, and let the servlet container take care of it using the built in clustering support.
Personal opinion: never rely on sessions in a systems integration scenario. Second, in clustered environments you have to replicate session state between members. Although the container takes care of this it will have impact on performance as well in case of in-memory replication.
3) Use a database to store the tokens and verify it (Considering Redis for this)
If a database is already in place, do it here
Alternative: Use a symmetric hashing approach. After the device authenticated itself return a one time token (digest) each server node can verify independently (based on certain criteria, e.g. a password). "Shared nothing".
BTW: no question, the transport has to be secured (TLS/SSL).
I think that the 1st solution will be the most scalable and flexible.
Try OpenAM with SAML. This is out-of-the-box solution. It has such a filter and can manage repository with such data. A more bulletproof solution could be based on WebSphere DataPower and SAML. If SAML is too complex you can use lightweight, custom solution, but IMHO the 1st idea will be the best.

Categories