Background: I am creating a file server. I am using Tomcat behind Nginx reverse proxy. I have a main server which host the UI and lots of edge servers where I hope to store user uploaded files and serve them back. It's a private file storage so only the file uploader should be able to download their uploaded files.
Problem: I authenticate users on the main server when they login to their account by creating a session and keep it to validate further requests from users (regular stuff). This information is not propergated to edge servers, edge servers serve files to any request. That's not what I want. I want to authorize downloads only to file owner.
Now, here I am trying to avoid Tomcat clustering where edges and main servers configure as a cluster that has sessions distributed. Site is SSL supported. Is there a way to validate a user's login status when a file download request reach an edge server from a user who is already logged in(created a session) the main server?
I see several approaches you could use
You can use a form of "claim based authentication" or resource access token.
Claim based authentication - you could use e.g. a JWT token, which should contain some necessary information (user id, account id, authorization, expiration, ..) and is signed by a shared secret (between the main server and file servers) or by a private key from the main server.
Advantage is, that the edge server can validate the token based on the hash or signature without contacting the main server. Disadvantage is, that there must be logic to decide whether the user has access to the requested resource.
see: https://jwt.io/
Resource access token - this approach that is used some cloud storage providers (AWS S3, IBM Object Storage, ...) - the main site will return a resource URL with some extra parameters - e.g. account, nonce, expiration, signature. The resource (edge) server must check the expiration and signature and provide or deny the resource
Advantage is, that the edge server doesn't care about any authentication and the authentication and authorization is completely in scope of the main server. Disadvantage is, that the provided resource URL must have limited expiration time.
see: http://s3-expiry.50projects.com/
Note:
placing the condition that the resource server may not communicate with the main server effectively you're unable to check if the user is logged in, except using SAML SSO with SLO (single logout) or OIDC with session management
Related
On their Getting started guide, reference, Spring Authorization Server have a piece of code where they declare two beans - UserDetailsService and RegistredClientRepository. I wanted to play around retrieving some tokens from the server. I tried using postman. Within authorization tab I entered the values from the guide:
it answered with:
So then I tried inputting client credentials from RegistredClientRepository entry:
And it worked.
So my question is: what is the purpose of UserDetails and RegistredClient being both available in this case?
And another little bit off-topic question: if I create my own authorization server for client credentials flow between my servers, how does every server know that token belongs to legitimate server and not to some attacker who can just register with needed clientId if there's an opportunity? How can resource server actually verify that token owner is the server it trusts?
Generally in oauth2, a "client" is an application which users might use to interface with some service - for example something like an App people can install on their phones or a web application.
Most oauth2 implementations have "scopes" (basically defined sets of access rights). Clients (client applications) are registered with some metadata (name, author, ...), a set of scopes they might use and some details to improve security - like "where is that application hosted" (in form of "which redirect URIs are valid"), client ID and client secret.
Depending on the oauth2 flow chosen, your App needs to prove being that app by some means - e.g. by having a valid combination of client ID and redirect URI or by doing HTTP basic auth with their client ID as username and client secret as password when exchanging a "code" for a "token".
All this was about clients - now about users: they are what you would expect, the users of a service - like you and me are users on Stackoverflow.com
And another little bit off-topic question: if I create my own
authorization server for client credentials flow between my servers,
how does every server know that token belongs to legitimate server and
not to some attacker who can just register with needed clientId if
there's an opportunity? How can resource server actually verify that
token owner is the server it trusts?
for this you could either use a form of signed tokens (look at e.g. JWT) or store the currently valid tokens per user in a database reachable by all your servers - both have pro's and con's, tokens in a database are easier to revoke, while signed tokens don't require you to store any state in a database (which can be expensive in big distributed systems)
Update - OP actually wants to do server-to-server authentication, see comments below
Server-to-server authentication can often work very well without any extra authentication server, in cases where your parties a less dynamic - like when you always have "those three servers" and they don't come and go very often.
In such cases, using a simple token is probably better and easier than using an oauth2 stack. Each server could just have a list of randomly generated strings in its config file, let's call this "the set of valid tokens" and also knows which token to send when communicating with a specific other server. When a request comes in, the server checks if the given token is in its set of valid tokens - done.
Can we implement Single Sign On (SSO) using offline cookie in Keycloak ?
I have application 'A ' connected to Keycloak 7.0.1 (KC) server for authentication. KC generates the active session and offline session for user under 'clientA' and returns access token & offline token to user. Offline token has validity of say X days. So when user tries connecting to application 'A" again it uses offline cookie to get the access token and user gets authenticated. So authentication is working using that offline cookie for Application 'A'.
Now I have another application 'B' , lets say it is hosted on same or different domain. KC is able to access the same offline cookie but it doesn't allow the authentication. The client for this application is 'client B'. The error which I get is "Session doesn’t have required client"
Looking at error and reading at articles I understood that two different clients cannot access same offline cookie. But still I wanted to know do anyone came across this scenario and what was the way used to allow both the applications to use same offline cookie or does we have any keycloak configuration which is missing .
Editing this question:
Can we have any REALM level Offline Token instead of creating different offline tokens for each client in realm ?
The keycloak is there to generate the credentials for the different applications, you need to create a second client in the same realm for the application b (you say you have already done that).
I do not think that you can use the access token from application a to application b. Maybe you could do that depending in the way that you generate the token if the two applications share the same private key, but if you do that then probably you do not need keycloak.
Then make an authorization request in this client, since you already have a session in keycloak this should happen automatically and generate the credentials that you need for application b.
To see if this works correctly, I would try to login directly in application b, go to keycloak, login there and see if the token provided can log me in application b.
Sorry for the high level response, but your question is also quite high level itself.
I have 4 separate software systems which implemented separately using java EE, Spring,hibernate etc.I want to integrate all of them and build a master application. I want to have a single login as well. Now they have their own databases and I want to have a shared single database as well because they have some common information.
What is the best method which can be used to achieve this task with having minimum changes to currently implemented systems?
Do I have to implement a new service layer( eg: using JAX-RS) or something on top of new db to access the new shared database and provide all db access services with business logic to above software systems??
For DataBase:
Spring/hibernate applications support connecting to one database by default. If you want to connect to multiple databases (own db + common db) then you will have to take care of database objects (Jdbc connection + pools + lifecycle/transaction management + other db initialisations) by yourself.
In my opinion DB connection+lifecycle initialisation yourself can be a huge pain and will take away your focus from solving real business cases. I would suggest using a single DB for the applications if possible. Most databases allow you to use file-per-table and even distribute the table files across multiple machines/servers (this is an optimisation).
Code Unification
For Unifying the code base into one (I assume you want to unify the codebases), you can make each application a separate module each with its own resource path. For example if you have Service1, Service2 and Service3 then in your new code base all your Service1 resources will be hosted inside /service1 path, Service2 resources inside /service2 path and so on. To do this you will simply need to modify the Path specifiers in your resource files (usually an #Path annotation).
Q: How to change all the api calls to these services since their path changed?
A: Now if you already pickup the paths to api call for these services from a config file then its great, just change the paths in your config file. Else you can actually start using this config approach, and specify something as below:
In your config file:
api-paths: {
service1: /service1/
service2: /service2/
...
}
Config Unification
You can put all your configs in a single file which most frameworks support. Another option to look at is putting separate config files for each service. For 2nd option take a look at TypeSafe Config Lib. It allows you to use multiple config files with overrides.
Note: In case codebase unification is not needed then use a reverse proxy like nginx. Its just how huge websites like google/fb work. You see a single domain which hides all the microservices behind layers of reverse proxies and a CDN.
For Auth/Login
You can do this in a servlet filter. In your config have an exclusion list, these excluded paths can be accessed without login. For example the /login path must in exclusion list so people can access the login page without login first. Now your servlet filter can implement a simple client cookie + server side session store based auth. You will need a password store as well.
The login flow will be like:
User open /login page
User enters username+password (credentials)
Server receives request for login with credentials. Server checks credentials against its own credential/password store.
If successful then server sends response back to client to set a cookie with some expiry time. If failed then send Http Unauthorised response.
Server stores the cookie in its session store as well (cookies will be stored per-client, user1+chrome=1 cookie, user1+firefox=another cookie, user2+any=another set of cookies)
In further requests the client sends the cookie and server (the servlet filter) verifies against its session store. If verification passes then server allows the api call to work.
If cookie expired or no cookie in request then redirect user to /login. Continue from step-1.
Note: Always hash your credentials on client end before sending on network. On server side store only hashed credentials, no raw text passwords. Also if security is paramount then look at salting your credentials as well.
I think a good aproach for this is to have a Look at netflix technology Stack. There is a project called zuul which Acts as reverse proxy. This proxy can route incoming requests to your underlying Services. This proxy can be the frontdoor to your services where every request only can pass throu if it is authenticated.
Hope this will help a bit.
I have 4 single page applications with same technologies: Spring MVC, Spring Security, Angulajs.
Each application has own ldap authentication and authorization. We want to build a single sign architecture and make a central authentication application. And make the other 4 application use this central application.
When user login into one of the apps, he should not need to login the others.
What is the easy way to implent this in server side and client side?
What you want is Single Sign-On (SSO). There are two options:
Use some existed SSO server like CAS.
Do it yourself using subdomain cookie technique.
First option is exactly what you want implement. When you open URL of app1 you will be redirected to SSO server and prompted for login/password. After successful authentication you will be redirected to app1 URL. Now if you open app2 URL you will be signed in automatically. One of advantages is that user password is stored only in SSO server.
Second option is more lightweight IMHO, because instead of using existed SSO server for sharing authentication information between your apps you use HTTP cookies. From the other side you need to write some minimal authentication code which may be less secure.
Subdomain cookie technique:
Use subdomains for all your apps (app1.domain.com, app2.domain.com)
When user connects to app1, generate some token (your session id), store it in some shared DB and as a cookie for domain.com
When user opens app2, check if token is present (as a cookie for domain.com), verify that it is valid (use shared DB) and allow access.
It is very simple algorithm that do not take into account all possible security vulnerabilities (like session fixation for example). So if you do not have enough time to solve them it may be better to go with first option.
Setup
We're developing a distributed application with Java and Spring where our existing client front end (complete with its own authentication, database, accounts, etc.) uses REST calls to access our new server for additional services. We want to protect these resources with Oauth.
Access should be restricted by role or account. However we don't want the user on the client side to have to worry about any additional authentication apart from the already existing account. At the same time we need to provide a means for third party applications to access some resources from the outside after going through some kind of registration against the server (which is why we're distributing in the first place).
So we have set up spring security on the server side to provide accounts that should be used to restrict access to the resources. The user should log in on the client side and then be able to access only those server resources assigned to him. We have some kind of registration process that sets up the user on the client side to be able to access the server services so any account setup I think should be done there.
So the questions are
How can I enable the client side to obtain an access token for the protected resources without the user having to log in to his server-side account?
And how do I setup the server side account without needing any user input?
My thoughts
This won't do
I'm thinking I'll have to either tell the client about a new account created on the server side for that user (but then, how would I choose and communicate a password?) or synchronize the client side account to the server, and use those credentials to authenticate the client against the server and generate access tokens. But how save can that be? Also the server has a much higher security (one way encrypted, salted passwords) on its accounts and I don't really want to compromise this by using the less save client accounts.
Maybe this will?
Maybe the way to go will be to tell the server about the client account during the first authentication, create an account on the server side, store the generated token on the client side and then authenticate the client against the server with that token for each subsequent request..? Will the server be able to log-in the client using its server-side account via that token for each request?
I'd need a special resource for that initial (2-legged?) handshake that can only be accessed from the client server, right?
Also:
Which would be better suited for the task, OAuth 1 or 2?
I'm hoping someone understands my problem and can help me sort through possible missunderstandings and knowledge gaps (I'm reading through Oauth and spring security documentations right now, so I'll update if I come up with a clearer picture and thus clearer questions of what to do)
Thanks for any help!
So our current status is to use OAuth2 mostly for reasons of simplicity. We're also sure that the flaws it might have concerning security we can cover ourselves as needed and they will most likely be addressed in the future by the implementation vendors or the IETF.
To handle the communication between REST server and REST client (both in our control) we use the formerly known as 2-legged authentication, now client credentials grant. I've asked a few questions on SO about that including
our current spring-security context setup
the client credentials flow in particular
the use of long lived tokens versus reauthentication
and how to limit REST access by HTTP method
Concerning the use of client based user accounts for authentication against the server we didn't get any further.
For now we authenticate the user against our old client web application as before and then authenticate the client against the server 2-legged. In theory this will allow any user to access any resource using the client accesstoken but for now that's okay for us so we will not investigate further down that road.
Still, should anyone have a good idea on how this might be solved we'll pick it up, just to tighten security further. So, I'll leave this question open.
My thoughts currently are along the line of registering a new client ID for each user on the authentication server with a generated secret and then synchronize those back to the client server and use those client_id / secret combinations to access resources for a user represented by the generated client_id in a client credentials flow.
For our latest application we'll store accounts on the REST server (authentication provider) and have the user login against that server and then use the token to access the REST resources as intended by the spec.