Jersey 1.12 with Oauth Support - oauth

I'm trying to secure my Jersey (Jax RS) ReSTFul API with OAuth and am not being very successful.
If I skip the Jersey OAuth jar files (client, server, core) in my deployment to Tomcat it works just fine, however, when I add the Jersey OAuth jars I get the following error when trying to call any of my endpoints:
SEVERE: Missing dependency for constructor public com.sun.jersey.oauth.server.api.OAuthServerFilter(com.sun.jersey.api.core.ResourceConfig,com.sun.jersey.oauth.server.spi.OAuthProvider) at parameter index 1
SEVERE: Missing dependency for field: private com.sun.jersey.oauth.server.spi.OAuthProvider com.sun.jersey.oauth.server.api.resources.AccessTokenRequest.provider
SEVERE: Missing dependency for field: private com.sun.jersey.oauth.server.spi.OAuthProvider com.sun.jersey.oauth.server.api.resources.RequestTokenRequest.provider
I've checked all over the Interwebs and cannot find anything that mentions this.
I don't know what else to add to this post to help answer the question of what I'm doing incorrectly.
Thanks.
EDIT:
Ok I see what's happening... I'm not gettnig a "provider" to be injected into my "request"
I am using the following init-param within my web.xml to add the DefaultOAuthProvider, but it doesn't seem to be helping:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.sun.jersey.oauth.server.api.resources;com.sun.jersey.oauth.server.api.providers</param-value>
</init-param>
The Debug console even says:
INFO: Provider classes found:
class com.sun.jersey.oauth.server.api.providers.DefaultOAuthProvider
I am FLUMUXED!!!!

Related

Configure the landing page after SSO authentication

I want to know how to setup the relay state with the new saml library. Basically once I am authenticated via the asserting party, I want to have a relay state url (a JSP in my application), where I should land. In the saml extensions library, as far as I know, the relay state url was set in the SAMLMessageContext object.
Actual Behaviour The mechanism of setting the page where I would be redirected to after SAML login has changed. I am not sure how to set the desired JSP where I want to land in the new library.
Expected behavior After the successful call to the assertionConsumerServiceLocation in my application, I should be taken to a Url configured by me (Relaystate). I need help in configuring this URL.
I tried to set up the relay state like this :-
Saml2AuthenticationRequestResolver authenticationRequestResolver(
RelyingPartyRegistrationResolver registrations) {
OpenSaml4AuthenticationRequestResolver authenticationRequests =
new OpenSaml4AuthenticationRequestResolver(registrations);
authenticationRequests.setRelayStateResolver(relayStateResolver);
return authenticationRequests;
}
I have defined the relayStateResolver like the following :-
private Converter<HttpServletRequest, String> relayStateResolver = (request) -> "my_desired_jsp_url_string";
Are my above configurations correct, and would they help me in landing on the desired JSP page after successful login?
Currently, below is the error I am facing with the above implementation. I am working to fix that (I need to get the Opensaml4 from Shibboleth's repository of artifacts), but wanted to know if the above configuration is correct before making that fixing effort.
Jan 03, 2023 5:54:28 AM org.apache.catalina.core.StandardWrapperValve
invoke SEVERE: Servlet.service() for servlet [dispatcher] in context
with path [/company] threw exception [Filter execution threw an
exception] with root cause java.lang.NoSuchMethodError:
org.opensaml.saml.saml2.core.AuthnRequest.setIssueInstant(Ljava/time/Instant;)V
at
org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver.lambda$resolve$1(OpenSaml4AuthenticationRequestResolver.java:60)
at
org.springframework.security.saml2.provider.service.web.authentication.OpenSamlAuthenticationRequestResolver.resolve(OpenSamlAuthenticationRequestResolver.java:133)
at
org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver.resolve(OpenSaml4AuthenticationRequestResolver.java:59)
at
org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter.doFilterInternal(Saml2WebSsoAuthenticationRequestFilter.java:184)
at
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
Because RelayState can be used for several things and is not always used to describe the post-login redirect URL (the spec says MAY), it is not defaulted to do this.
You can configure Spring Security to redirect to the RelayState parameter by configuring a SimpleUrlAuthenticationSuccessHandler like so:
#Bean
public SecurityFilterChain appEndpoints(HttpSecurity http) {
SimpleUrlAuthenticationSuccessHandler successHandler =
new SimpleUrlAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter(Saml2ParameterNames.RELAY_STATE);
http
.saml2Login((saml2) -> saml2
.authenticationSuccessHandler(successHandler)
// ...
)
// ...
return http.build();
}
You will also need to configure the relay state resolver, as you have already shown in your post.

Howto disable signature verification in Spring Security SAML 5.6.1?

I'm currently migrating from old deprecated Spring Security SAML Extension 1.0.10 to the SAML implementation in Spring Security 5.6.1.
In the old extension there was the possibility to disable the signature verification of the SAML response (property wantAssertionSigned in Spring Security SAML Extension documentation). This was very helpful for me during testing.
I wonder if this is also possible in Spring Security 5.6.1?
I searched in the source code and found the class OpenSamlMetadataResolver where it seems to me that this is hard-coded and cannot be changed:
private SPSSODescriptor buildSpSsoDescriptor(RelyingPartyRegistration registration) {
SPSSODescriptor spSsoDescriptor = build(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
(...)
spSsoDescriptor.setWantAssertionsSigned(true);
(...)
return spSsoDescriptor;
}
Also the code in OpenSaml4AuthenticationProvider doesn't seem to offer an easy way to configure private variable assertionSignatureValidator to override validation behaviour.
Any help is appreciated.
In Spring Security 5.7.0, which will be released this Monday, May 16, 2022, the hard-coded line is removed. Therefore no more signature verification by default.
You will also be able to customize the EntityDescriptor if you want, something like this:
openSamlMetadataResolver.setEntityDescriptorCustomizer(
(parameters) -> parameters.getEntityDescriptor().setEntityID("overriddenEntityId"));
You can always try the milestone releases before the GA.

Keycloak with Spring 'Invalid credentials' after login

I'm having no luck in setting up a simple Spring gateway + oauth2 client with Keycloak standalone. The keycloack part of it works fine. Wireshark shows the token correctly generated.
The gateway security config is as follows. I'm still not sure whether there is a need to permitAll() the login callback url. Some guides suggest that it should be the case, others dont. I suspect the oauth provider manages that part behind the scenes. Nonetheless, with or without permitAll for the "/login/*" path, the result remains the same.
#Configuration
#EnableWebFluxSecurity
public class SecurityConfig {
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange(e -> e.anyExchange().authenticated());
http.oauth2Login(Customizer.withDefaults());
http.csrf().disable();
return http.build();
}
}
After login the redirect to https://localhost:9000/login seems incorrect, it should retry the original url, say https://localhost:9000/test-service/v1/listall/
EDIT
In order to rule out any misconfigurations, even tried a simplest possible gateway and api resource (un-authenticated) and setup simplest possible relam in keyclock. The results haven't changed :( There are dozens of articles out there doing the exact same thing.
Any pointers, ideas?
Many Thanks
Even I have completed all configurations including "creating a client", "creating a scope" and creating a "user" with this scope. I have encountered this issue again.
The only solution worked for me is adding scope :openid to application.yaml.
You can refer application.yaml Security OAuth config from here:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://${keycloak.base.url}/auth/realms/${realm}
client:
registration:
gateway:
provider: keycloak
client-id: ${client id}
client-secret:${client secret}
scope: openid
provider:
keycloak:
user-name-attribute: preferred_username
issuer-uri: https://${keycloak.base.url}/auth/realms/${realm}
According to offical doc(found at https://www.keycloak.org/docs/latest/securing_apps/#_spring_security_adapter), you should use
#KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter
I figured it out, it was an incorrect user-name-attribute. The correct value is
user-name-attribute: preferred_username
For some reason, I had it set to preferred_name. It would save a lot of debug-time if only spring oauth writes the actual error instead of a generic invalid_grant.
First of all, for anyone troubleshooting Spring Security, I recommend enabling debug logging by setting the logging level in your application.properties or application.yml file.
application.properties format:
logging.level.org.springframework.security=DEBUG
application.yml format:
logging:
level:
org:
springframework:
security: DEBUG
I was having a similar issue when using OAuth2 along with Spring Session. Even after authenticating successfully with Keycloak, I would get this login error whenever my Spring Session had expired.
I do not condone messing around with authentication flows, but I was able to resolve this issue by setting my own authentication entry point on the ServerHttpSecurity:
http.exceptionHandling().authenticationEntryPoint(new RedirectServerAuthenticationEntryPoint("/oauth2/authorization/keycloak"));
I then had to handle any requests to the "/login" page in my Controller. For me, I just redirect to my default landing page for a root request:
#Controller
#RequestMapping("/")
public class RootController {
#GetMapping({"", "login"})
public Mono<String> index() {
return Mono.just("redirect:/myLandingPage");
}
}
Somehow adding the "scope=openid" in my applications.properties works fine for me
spring.security.oauth2.client.registration.spring-cloud-client.scope=openid

log4j2 monitorInterval using spring cloud config server

I'm trying to read the log4j2 configuration from config server during application startup.
bootstrap.yml
spring:
application:
name: loggingApp
cloud:
config:
uri: http://localhost:8888
logging:
config: http://localhost:8888/loggingApp/raw/master/loggingApp-log4j2-DEV.xml
The application seems to get the configuration properly during start up as I see the appropriate log levels. However, the automatic configuration doesn't seem to work. When I change the log level of the loggers, looks like it didn't read the updated config from config server after the monitorInterval has passed. I've set the monitorInterval to 10 seconds. As per the documentation the minimum interval should be 5 seconds. If I point to a file on local drive, instead of the config server url, it is working fine. I tried using -Dlog4j.configurationFile as jvm arg as well as spring configuration logging.config to see if one of those work, but none worked.
https://logging.apache.org/log4j/2.x/manual/configuration.html#AutomaticReconfiguration
https://logging.apache.org/log4j/2.x/log4j-spring-cloud-config/log4j-spring-cloud-config-client/index.html
I'm using spring-boot 2.2.5.RELEASE, log4j2 2.13.1 and spring-cloud Hoxton.SR3 versions.
This is how the git repo looks like where config files are being read from
I too tried the same and it did not work. It seems the <Configuration monitorInternal="10"> property would only have worked if we use log4j.configurationFile property to load the log4j2 config file as mentioned in Apache's Log4j Spring Cloud Configuration Guide .
I used the spring actuator refresh approach to accomplish this task. However this does not reload the configs on its own. So there is work around to meet the required ends. Create a scheduler in your spring application that hits the actuator refresh url on specified intervals as below
#Component
#EnableScheduling
public class RefreshScheduler {
#Autowired
private RestTemplate restTemplate;
#Scheduled(fixedRate = 60000)
private void postRefreshEndPoint() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(headers);
restTemplate.postForEntity("http://localhost:8080/actuator/refresh", entity, String.class);
}
}
This is surely a very dirty way of somehow achieving the required functionality. It works though :)
As a side note : Isn't it better to not have automatic refresh? My argument would be : It's possible developers by mistake ( or just for fun ) do changes to the configs in GIT. And this will lead to a change in the live application in prodcution.
However if we have the actuator refresh URL as mandatory for refreshing the spring context, this can be stopped. Only authorized memebers from the production/operations team would have access to this refresh url and they can decide whether to actually refresh the production spring beans when a commit on GIT has been done.

jasig cas too many redirects issue

I'm trying to secure a spring-boot web application using spring security and spring-security-cas (SSO with Jasig CAS).
I'm facing a too many redirects error when trying to access a protected resources. The project is available here
Do you see any error in my configuration?
Thanks in advance
redirect loop error screenshot
Finally found out the error:
In SpringSecurity 4.x, CasAuthenticationFilter's defaultFilterProcessesUrl path is changed. So Change '/j_spring_cas_security_check' to '/login/cas' in Configuration.
So in my application.properties file, i had to change
app.service.security=http://localhost:7777/j_spring_cas_security_check
to
app.service.security=http://localhost:7777/login/cas
So the ServiceProperties Bean would become
#Bean
public ServiceProperties serviceProperties() {
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService("http://localhost:7777/login/cas");
serviceProperties.setSendRenew(false);
return serviceProperties;
}
Hope it'll help someone else!

Resources