I used Spring Session and OAuth2 to build a SSO for a multi tenant application and have an Application with different frontend clients and a backend server.
(I used this tutorial https://www.baeldung.com/sso-spring-security-oauth2).
So the problem now is that each client (Tenant) runs on a different domain than the backend server and so the backend server has only one cookie and a user can't login with on two different tenants at the same time. (Each tenant has different bearer tokens so only the cookie is the problem)
The Idea would have been to somehow configure spring to create a different SESSION cookie on the server too (Which I didn't manage to do/Don't know if this is even possible)
And additionally I don't understand why I need that cookie, since I am using a token based authentification.
I do understand that if I want to automatically log the user in if he closes the browser and opens it again, that I need to save some data on the client side (e.g. a cookie, but why use a JSESSIONID for that and don't save the bearer token in a cookie)
Additionally I do belive that after the login process the session cookie is used for authorization and not the oAuth token. (I removed it from the token store + refresh token and on the next request it simply created a new one)
So basically what I want to know is:
Can I somehow configure my backend to create one cookie per tenant ? (without running multiple instances of it)
Why do I need the session cookie ?
How is the user reauthentificated in oAuth after I delte the tokens from the token store, without having to enter the password again (I guess the session cookie is responsible for that tho)
My yaml config looks like this:
server:
port: 8082
servlet:
context-path: /ui
session:
cookie:
name: UISESSION
security:
basic:
enabled: false
oauth2:
client:
clientId: SampleClientId
clientSecret: secret
accessTokenUri: http://localhost:8081/auth/oauth/token
userAuthorizationUri: http://localhost:8081/auth/oauth/authorize
resource:
userInfoUri: http://localhost:8081/auth/user/me
client2:
clientId: SampleClientId
clientSecret: secret
accessTokenUri: http://localhost:8081/auth/oauth/token
userAuthorizationUri: http://localhost:8081/auth/oauth/authorize
resource:
userInfoUri: http://localhost:8081/auth/user/me
How OAuth2 works would be as follow:
The access token is granted for that tenant only and can't be used to access resource of other tenant.
The tenant website then need to store this access token to use later, either it can be stored in:
Cookie - you have no issue as each tenant have different domain.
Local Storage - you have no issue because it's also different storage.
SESSIONID is the cookie created by Spring and will not be used in this case.
Related
I've set up a Spring Security application to use my AWS Cognito user pool as the identity provider.
Cognito authentication works as expected when I set my Cognito dashboard's callback url to http://localhost:9001/login/oauth2/code/cognito and set my spring boot's application.yml file's redirectUriTemplate property to the same value (the application.yml file is below).
When I access "http://localhost:9001", I am redirected to the AWS Cognito login and can authenticate and be redirected to my app just fine:
spring:
security:
oauth2:
client:
registration:
cognito:
clientId: "myClientId"
clientSecret: "myClientSecret"
scope: openid
redirectUriTemplate: "http://localhost:9001/login/oauth2/code/cognito" # Getting a "redirect_mismatch" error when using "https://mycorporatehostname.com/login/oauth2/code/cognito"
clientName: my-client-name
provider:
cognito:
issuerUri: "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_zBBqRy5Wv"
userNameAttribute: cognito:username
When I deploy the same application, it becomes accessible through a DNS hostname (i.e: "https://my.corporatehostname.com"). so I updated my AWS Cognito dashboard's callback url, and my application.yml's redirectUriTemplate, to use this DNS hostname in the callback urls, meaning that the "callback url" on the Cognito dashboard, and the redirectUriTemplate in my application.yml file both become: "https://my.corporatehostname.com/login/oauth2/code/cognito"
Unfortunately, when I try to access "https://my.corporatehostname.com", I expect to be redirected to the AWS Cognito login screen, but instead get a redirect_mismatch error.
Is there a way to see what the redirect_mismatch error is? I believe I've configured my application correctly, and am not sure why it would work on localhost, but not when deployed.
Additionally, my DNS hostname is only accessible via my corporate intranet--it is not publicly accessible. I'm not sure if that makes a difference or not.
Any help would be greatly appreciated!
I using Google for authentication in my REST API, but it gives me only Session cookie for 30min and then all my requests are being failed.
How I can configure google button to keep me signed in?
I have searched a lot of pages trying to find any answers, but nothing is going to work.
My current status:
security:
oauth2:
client:
registration:
google:
client-id: {someId}
client-secret: {someSecret}
Then I used /oauth2/authorization/google endpoint to have JSESSIONID cookie and it gives me ability to handle requests for 30min.
And then I have to make it again and again. I want to keep my session for a long time.
As I understand while I searched this question, that rememberMe functionality in springSecurity do not support oauth2.
I have a simple microservices project with an api-gateway, a product service and keycloak as authentication server. I beleive that i made the configuration correctly because on the browser i get the right response, but on postman what i get back as response is the keycloak default login page (on HTML).
This is the response on the browser
This is the postman response
Postman cookies :
Api-gateway config :
spring:
cloud:
gateway:
default-filters:
- TokenRelay
routes:
- id: product-resource-server
uri: http://localhost:9191
predicates:
- Path=/product/**
security:
oauth2:
client:
provider:
my-keycloak-provider:
issuer-uri: http://localhost:2727/auth/realms/demo-microservice-realm
registration:
keycloak-spring-gateway-client:
provider: my-keycloak-provider
client-id: demo-cloud-gateway-client
client-secret: 39ea2ef6-90a7-47ca-9892-fda60127f47e
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/login/oauth2/code/keycloak"
My service config :
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: http://localhost:2727/auth/realms/demo-microservice-realm/protocol/openid-connect/certs
server:
port: 9191
ProductController :
#RestController
public class Controller {
#GetMapping("/product")
// #RolesAllowed({"product_read"})
public String getProduct(Principal principal) {
return "Response from Product Service, User Id:" + principal.getName();
}
}
So what i really wanna know is if this is a postman Bug or it is a config problem (project, keycloak ...).
Actually i can't test any Post resquest for the same raison and because it not possible to test them on the browser.
I hope this is understandable.
Well, I added spring-boot-starter-oauth2-resource-server in the api gateway project and it works fine.
Please refer to this tutorial : Microservices – Authentication, and Authorization With Keycloak
In postman when you use Authorization type oAuth2. Postman redirect you to login page to get access token from keycloak and store it in memory via Get New Access Token button. Then when you send a new request to the resource endpoint via the Send blue button; Postman sets the available token on a header field (Authorization) and sends it to server. therefore when Authorization header not set in your request.(for example for the first time that you not get a new token yet) your gateway detects not authorize error and redirects you to login page.
but on browser, the authorization mechanism is different totally in the way you call endpoint. It uses stored cookie (JSESSION) not token. you can test it by delete all stored cookie in your browser.
I encountered the same problem.
When I sent the token request from Postman, I received a response with 200 code but body was in html, which is useless.
In my case I found out I just wrote wrong url. So the solution was correcting the called url.
Your url should look like this:
http://{wherever-your-keycloak-runs}/auth/realms/{name-of-your-realm}/protocol/openid-connect/token
But I had mistake in {wherever-your-keycloak-runs} part, so apparently I was calling the wrong part of the keyclock.
I have a backend API server which was initially bearer-only mode which is accepting token from FE. Now, there's a need for the server to call another service in the same keycloak realm which grant type is usually client_credentials.
User -> FE server --(bearer only)--> BE server --(client credential)--> Other service
The question is, how to combine bearer-only and client credential in the BE server? Do I have to define 2 clients in the Keycloak realm for the same BE (one is bearer only, the other one is client credentials).
We have solved this with two separate clients in Keycloak
Client #1 (token is generated from SPA client and used for Bearer Auth)
Access Type: Public
Client #2 (for server to server)
Access Type: Confidential
Service Accounts: On
On the Service Account Roles Tab: define which roles that token will get
EDIT:
On the spring side, you just need to reference Client #2 when setting up your keycloak AdapterDeploymentContext in your security config class. That is because any token generated by Client #1 or Client #2 will be a SSO token and your spring backend will point back to the realm for token verification.
I am able to login with Facebook into my SpringBoot application only if I disable the "Require App Secret" option in the Facebook Developer application configuration.
When the extra (recommended) security check is enabled I get an error
org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_user_info_response]
An error occurred while attempting to retrieve the UserInfo Resource: Error details: [UserInfo Uri:
https://graph.facebook.com/me?fields=id,name,email, Error Code: {message=API calls from the server
require an appsecret_proof argument, type=GraphMethodException, code=100, fbtrace_id=xxxx}]
It is evident that Spring Security tries to access Facebook (calling https://graph.facebook.com/me?fields=id,name,email) to retrieve the authenticated user but it fails to pass the extra parameter appsecret_proof.
It is not immediately clear (after digging into SpringSecurity documentation and forums) how I can let Spring add the extra token.
I had a very similar situation - I could not log in to my application using the facebook user account.
First make sure that the given client id and secret id are correct (sometimes it can be confused with your other project). If you are sure that the entered data is okay, I would start by checking the available permits. So open the facebook login plugin panel (you should have a tab on the right side of the screen).
For my case, the following configuration was sufficient:
What's more, check the available permission and features:
public_profile access is required to extract basic information of a user who wants to log in to your website. In addition, it is also worth adding an email request.
Also check the configuration for the facebook client, which allows you to get the user token:
spring:
security:
oauth2:
client:
registration:
facebook:
clientId: your-client-id
clientSecret: your-client-secret
redirectUri: "{baseUrl}/api/oauth2/callback/{registrationId}" # Note that facebook now mandates the use of https redirect URIs, so make sure your app supports https in production
scope:
- email
- public_profile
provider:
facebook:
authorizationUri: https://www.facebook.com/v3.0/dialog/oauth
tokenUri: https://graph.facebook.com/v3.0/oauth/access_token
userInfoUri: https://graph.facebook.com/v3.0/me?fields=id,first_name,middle_name,last_name,name,email
In my case, the above configuration was enough for the user to authorize himself on an external website.