I wish to use Spring Security (version 5.1.2) to generate a CSRF token for my Angular 7 application. I have the following in my SecurityConfig file:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
with the following RequestMapping in my controller:
#RestController
#RequestMapping("/authentication")
public class AuthenticationController {
#GetMapping("/csrf")
public void getCsrfToken(){...}
#PostMapping("/register")
public RegisterOutputDTO register(#RequestBody UserDTO input){...}
}
I gathered from various sources that the csrfTokenRepository would automatically generate a cookie with header XSRF-token on my first GET call (which is what /authentication/csrf is for), but I am not getting a cookie back from the server. Hence on my next POST call I am getting a 403 response. What could I possibly be missing?
As indicated on the comments to my question, I found the answer to my problem. A cookie can not be sent cross-domain.
My frontend was deployed on localhost:3000 and my backend on localhost:9080, which are considered different domains apparently. If I go to localhost:9080 (I get a white page, but that doesn't matter) and I then go to the application tab in Chrome, I find that the XSRF cookie I was looking for is stored like I was expecting all along. The cookie was available from the GET call I executed from my front-end. The problem is that the cookie needs to be available for localhost:3000 so that Angular can make use of the cookie.
There are multiple ways you can solve this issue in your local environment.
Use a proxy
You can use a proxy to map certain url paths to your backend. This is the solution I went with.
I run my Angular application with a webpack dev server. Webpack provides an easy way to proxy certain urls to your backend. For example:
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 3000,
proxy: {
'/api': 'http://localhost:9080'
}
}
The Angular application runs on localhost:3000. Any calls to localhost:3000/api/*. will be forwarded to localhost:9080/api/*. So in my case I no longer perform a GET call on localhost:9080/api/authentication/csrf, but I call localhost:3000/api/authentication/csrf which will then get forwarded to my backend. (I added /api to the path in my rest controller, for those wondering.)
Deploy both applications on the same port
Using the frontend-maven-plugin you can build the frontend to its dist folder and then let maven package the dist folder along with the backend for deploy. I haven't tried this, but there are various resources that show this should be easy to do with Spring boot. So both frontend and backend would be available through localhost:9080 for example.
Use Spring Profile to disable csrf locally
You can make use of Spring #Profile annotations to create a different configuration for local environment and the rest (test, acceptance, production). Csrf can simply be disabled for development. I do not prefer this option since I like to keep DEV and other environments the same as much as possible. It's also not a real answer to the problem statement.
Special thanks to the answer of user #dspies which helped me find the problem.
Related
I am working on setting up an application using Spring Boot and Auth0. We are refactoring from a legacy codebase to use Spring Boot. In the legacy code, the Auth0 URL is created manually by appending the URL parameters:
https://[removed].auth0.com/authorize?
response_type=code
&client_id=[removed]
&scope=openid email profile
&connection=[removed]
&state=[removed]
&redirect_uri=http://localhost:8081/login/oauth2/code/auth0
With the Spring Boot configuration (guide here: https://auth0.com/docs/quickstart/webapp/java-spring-boot/01-login), this is the URL that generates:
https://[removed].auth0.com/authorize?
response_type=code
&client_id=[removed]
&scope=openid email profile
&state=[removed]
&redirect_uri=http://localhost:8081/login/oauth2/code/auth0
The Spring Boot URL is giving me an error "[invalid_request] no connections enabled for the client".
I am missing the "connection" parameter with the Spring Boot setup. I have tested by manually copying the URL and adding the "connection" parameter and I get the login page. Without it, I get the error.
On Spring's configuration page (https://docs.spring.io/spring-security/reference/servlet/oauth2/login/core.html#oauth2login-boot-property-mappings), I don't see an option for Connection. I didn't see anything on the SecurityFilterChain that would allow me to change this either.
I see that Auth0.js has a function that allows a "connection" parameter (https://auth0.com/docs/libraries/auth0js). How do I add this using Spring Boot/Java?
EDIT
application.properties:
spring.security.oauth2.client.registration.auth0.client-id=[removed]
spring.security.oauth2.client.registration.auth0.client-secret=[removed]
spring.security.oauth2.client.registration.auth0.scope[0]=openid
spring.security.oauth2.client.registration.auth0.scope[1]=email
spring.security.oauth2.client.registration.auth0.scope[2]=profile
spring.security.oauth2.client.provider.auth0.issuer-uri=[removed]
EDIT 2
We were working in conjunction with Auth0 Support - they provided us the following information:
In case an Enterprise connection is the only enabled connection for an
application and the "connection" parameter is not specified on the
/authorize request, you need to enable the "show as a button" setting
on that enterprise connection, otherwise you will get "no connections
enabled for the client" error.
The "Display connection as a button" checkbox is on the "Login
Experience" tab of the connection setting page.
Weird configuration requirement - you can't go directly to the login page. You have to have a button to take you there. This did resolve the original issue; however, I marked #Codo answer below as accepted, as it did answer this question and appears it would work from initial testing.
You are looking for a way to add an additional parameter to the authorization URI. It's isn't as straightforward as one would like but doable.
Fortunately, it's described in Customizing Authorization and Token Requests with Spring Security 5.1 Client.
You probably want to implement the steps 2 and 4:
Add your own implementation of OAuth2AuthorizationRequestResolver, override both resolve() methods to call customizeAuthorizationRequest()
Implement customizeAuthorizationRequest() to add the additional connection parameter (OAuth2AuthorizationRequest already support additional parameters)
Implement a security configuration class to register CustomAuthorizationRequestResolver as the authorization request resolver
Several issues on GitHub ask for a simpler way. But the issues are still open (or closed as duplicates).
Update
Instead of clientRegistrationRepository() (at the end of step 2), you could declare clientRegistrationRepository as an injected dependency and the use it without parentheses:
#Autowired
private ClientRegistrationRepository clientRegistrationRepository;
Spring Security comes with with mostly preconfigured Auth0 module. Unless you're doing something specific, there's no need to construct URL yourself.
Have you done Spring configuration as said in the link you've posted: https://auth0.com/docs/quickstart/webapp/java-spring-boot/01-login#configure-spring-security ?
# src/main/resources/application.yml
spring:
security:
oauth2:
client:
registration:
auth0:
...
Here's another option (untested):
In application.properties, specify all URLs separately. So instead of:
spring.security.oauth2.client.provider.auth0.issuer-uri=xyz.us.auth0.com
Specify:
spring.security.oauth2.client.provider.auth0.authorization-uri=https://xyz.us.auth0.com/authorize?connection=azuread
spring.security.oauth2.client.provider.auth0.token-uri=https://xyz.us.auth0.com/oauth/token
spring.security.oauth2.client.provider.auth0.jwk-set-uri=https://xyz.us.auth0.com/=.well-known/jwks.json
spring.security.oauth2.client.provider.auth0.user-info-uri=https://xyz.us.auth0.com/userinfo
Note that the authorization URI already includes the connection parameters. All the other parameters should then be appended.
You can get all the URIs at https://xyz.us.auth0.com/.well-known/openid-configuration (just replace "xyz" and put the URL in your browser).
I'm created java web application using spring boot starter for azure active directory step by step like is described in:https://learn.microsoft.com/en-us/azure/developer/java/spring-framework/configure-spring-boot-starter-java-app-with-azure-active-directory
My application with my azure account work fine when i open localhost:8080 it redirects me to azure where I do the login and then I'm redirected back to my app.
Problem is when i try to configure this dummy app with azure AD account from my customer. Here also when i open my app host app redirects me to azure login and after login i got error like in screenshot
and here is my application.properties
azure.activedirectory.tenant-id=placeholder
azure.activedirectory.client-id=placeholder
azure.activedirectory.client-secret=placeholder
azure.activedirectory.object-id=placeholder
azure.activedirectory.user-group.allowed-groups=group1
azure.activedirectory.session-stateless=true
security.oauth2.authorization.token-key-access=permitAll()
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
application.baseurl.logout.redirect=https://mydomain:8081/
application.groups.for.displaying=
application.groups.for.filtering=
server.port=8081
server.ssl.enabled=true
server.ssl.trust-store=/apps/tomcat/conf/trusted.jks
server.ssl.trust-store-password=mykeys
server.ssl.key-store=/apps/tomcat/conf/.keystore
server.ssl.key-store-password=f213495a0be855c4ab190a1f84cc18cd
server.ssl.key-store-type=JKS
server.ssl.key-alias=key-dev-ui
server.ssl.key-password=f213495a0be855c4ab190a1f84cc18cd
my configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests().anyRequest().authenticated().and().oauth2Login().and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and().oauth2Client();
}
Please check below points:
The issue may arise when the issuer value obtained from token is from different endpoint (v2) than expected.Please make sure to use latest version of spring boot api and check the same to be in dependencies.
You need to set the redirect URL as http://localhost:8080/login/oauth2/code/azure or http://localhost:8080/login/oauth2/code/ in the portal.You can configure other value according to your app in place of localhost:8080 .This redirect uri must be configured in your application properties.
Make sure to expose api and make sure the permissions are also configured and granted admin consent.
. Give default scope (make sure to add the scope in code)or directly give the scopes present in the app (check in app code) such as User.read ,file.read or offline_access and provide delegated permsissions in portal like below(if those are present in code ).
(or)
and grand admin consent
Also see springboot starter dev guide | ms docs and please check references below.
You may provide other configuration details by editing the question if above are not the cases to investigate further.
References:
spring-rest-azure-ad-oauth
using-spring-security-with-azure-active-directory-mga
is literally my first time on AWS deployments , and doing it by myself is harder the task.
After having created my app in Elastic Beans and its respective RDS database with its instance , i created a Snapshot (jar) on my Springboot app which was also implemented in the deployment process of the AWS application.
Also several items were configured in its Software Category referring the RDS database endpoint, server ports, user-name of database , etc...
.
Then after all that process , got the app deployed with a url.
But when i apply that url with the endpoints my springboot controllers have , i receive as error a 404 Not Found; but if i decide to work on local requesting only the RDS database created by the application in Elastic Bean the endpoints shows data and the app works
Literally on my Spring Boot App i declared in the app. properties the connection to that database in AWS
spring.datasource.url=jdbc:mysql://aat54g98qswmf3.clnhc7kmwszz.us-west-2.rds.amazonaws.com:3306/ebdb?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=xxxx
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
logging.level.org.hibernate.SQL=debug
thus my controllers in the spring working in local don't have any problem
#RequestMapping(path = "/restaurant-booking/version1")
public class RestaurantController {
#Autowired
RestaurantService restaurantService;
#ResponseStatus(HttpStatus.OK)
#RequestMapping(value = "/restaurant/all", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
some code.........
};
then on my browser:
But if i switch to the url facilitated by the deployed EB app in AWS, and the use the same endpoint
Any help guys would be amazing , cause honestly can't find the problem or where to look at!
Thanks in advance!!!
The whitelabel error page is the default error page in spring when you have not defined your own. This means your application is deployed and running. You either messed up your request mapping or your url.
If we look at your mappings we see the url should be .../restaurant-booking/version1/restaurant/all
Request mappings get nested when they are on the class and method level.
You actually used the correct url locally but not on your deployed version.
The problem might be arising due to the connectivity with the RDS and the correct Environment configuration for your Spring Boot Application.
I would suggest that prior to continuing with the environment creation (or even after), make sure you connect the RDS properly. For the configuration part:-
you can make use of application-prod.properties file and specify an environment variable and value for software configuration labelled SPRING_PROFILES_ACTIVE and set its value to prod.
The application-prod properties consist of:-
server.port=5000
spring.datasource.url=jdbc:mysql://${RDS_HOSTNAME}:${RDS_PORT}/${RDS_DB_NAME}
spring.datasource.username=${RDS_USERNAME}
spring.datasource.password=${RDS_PASSWORD}
spring.jpa.hibernate.ddl-auto=create
Select a suitable RDS before or after creating the environment.
You need to configure the inbound and outbound rules for security group encompassing the RDS manually to allow access for creating, delete, update, etc.
And then finally upload the jar file in the environment. Worked for me.
I have an application that uses spring to the calling rest services. Specifically, the RestTemplate class. Right before I make the call to:
restTemplate.execute(
url,
method,
new JSONRequestCallback(headerParams),
new JSONResponseExtractor(containerType, cls));
method, I check the value url in the call. I quite literally can copy and paste the value of that url (lets call it http://myHost.net:8081) into a browser in the same network domain as my tomcat 8 (the web container I am using in eclipse), and the service response is successfully returned from the browser. However, the execute method throws an exception upon return:
org.springframework.web.client.ResourceAccessException: I/O error: myHost.net; nested exception is java.net.UnknownHostException: myHost.net
Now, the web service I am calling is on the internet while my dev environment is not. To address, we opened an appropriate port in our firewall and I can successfully access the service endpoint from a browser (as indicated above) by properly configuring the proxy settings on the browser. Similarly, from my Tomcat 8 configuration within eclipse, I have added the following launch configuration VM arguments:
-Dhttp.proxySet=true
-Dhttp.proxyHost=myHost.net
-Dhttp.proxyPort=8080
but as I have indicated above, I still get the unknown host exception.
By the way, I did see a similar issue here (Spring RestTemplate and Proxy Auth) but that required a coding solution in the spring code. I was hoping for a configuration solution as our system will not have the proxy issue in the operational environement.
I am hoping someone might have some ideas as to either what I might be doing wrong, and/or how I might be able to better diagnose my problem. Thank you!
I have a nearly finished web project with EJB3.1 deployed as WAR in Wildfly 8.2. It´s secured by a self-written JAAS-realm with form authentication (auth-method FORM).
Now I want to add a EJB-Webservice which is basically defined as
#Stateless
#WebService
public class ProductImportWebservice {
#Resource
WebServiceContext wsContext;
[...]
#WebMethod
public Product createOrFind(String name) {
[...]
}
}
The webservice works as expected but now I have the problem that I want to protect access to the Webservice by username/password.
So I annotated the webservice or the #WebMethod with
#RolesAllowed("ADMIN")
which is a valid role in the JAAS-authentication.
Effect: The SOAP-client gets the answere "reqeust not allowed / not authenticated" - but gets no authentication-request and attempts with BASIC-authentication are ignored.
If I add the URL of the webservice to the security-constraint in the web.xml I get an authentication request... but the problem now is that it´s the form based authentication - as a user with a browser would expect it. This is unusable for a "normal" SOAP-client :-(
The concrete question now is: Is there a way to have both - BASIC authentication for the webservice and FORM-based authentication for the rest of the application?
The only other way I would see is to split the WAR package up to an EAR-package and deploy the webservice in a different web-context.
The concrete question now is: Is there a way to have both - BASIC
authentication for the webservice and FORM-based authentication for
the rest of the application?
You can't use both FORM and BASIC authentication for the same web application.
If you need to do this, create two separate applications, with a shared codebase.
Reference:
https://docs.oracle.com/javaee/7/tutorial/security-webtier002.htm#JEETT01227
The auth-method subelement configures the authentication mechanism for
the web application. The element content must be either NONE, BASIC,
DIGEST, FORM, or CLIENT-CERT.