spring /oauth/token, AuthorizationFailureEvent -> AuthenticationSuccessEvent - spring-boot

Spring Boot 2, OAuth2, and I use InMemoryTokenStore.
I have defined the following setting:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
...
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.anyRequest().permitAll();
;
}
...
}
To get a token I'm making the following request:
curl Standard:Login#localhost:8080/oauth/token -d grant_type=password -d username=user -d password=passUser
Next, the following event chain occurs:
1) The first event raised is AuthorizationFailureEvent:
principal = anonymousUser
configAttributes = [fullyAuthenticated]
details = org.springframework.security.web.authentication.WebAuthenticationDetails#957e: RemoteIpAddress: 127.0.0.1; SessionId: null
exceptionClass = AccessDeniedException
source = FilterInvocation: URL: /oauth/token?username=user&password=passUser&grant_type=password
authorities = [ROLE_ANONYMOUS]
exceptionMessage = Access is denied
timestamp = 2017-12-01 14:45:34
2) Then AuthenticationSuccessEvent:
details = remoteAddress=127.0.0.1, tokenType=BearertokenValue=<TOKEN>
source = org.springframework.security.oauth2.provider.OAuth2Authentication#eb5a2d91: Principal: {
"id":" user",
"password": "****",
"authorities": ["USER"],
"firstName": "",
"lastName": "",
"accountNonExpired": "true",
"credentialsNonExpired": "true",
"accountNonLocked": "true",
"enabled": "true"
}; Credentials: [PROTECTED]; Authenticated: true; Details: remoteAddress=127.0.0.1, tokenType=BearertokenValue=<TOKEN>; Granted Authorities: "USER"
timestamp = 2017-12-01 14:45:36
3) And finally the server successfully returns a token:
{
"access_token" : "2d37dd06-0f35-441f-a851-96d145836eed",
"token_type" : "bearer",
"expires_in" : 863999,
"scope" : "all"
}
Please help me understand:
What settings should I make so that event AuthorizationFailureEvent does not occur?

Related

Timeouts are not correct for servlet in spring boot

This is remote server properties:
server.servlet.session.timeout=3m
SAme for my local.properties
also we have a config like this:
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl("/login?invalidSession")//dokunma
.maximumSessions(1)//
.maxSessionsPreventsLogin(true)//
.expiredUrl("/login?expired")
.sessionRegistry(sessionRegistry());
We have a class like this:
#Bean // Http Listener
public HttpSessionListener httpSessionListener() {
return new HttpSessionListener() {
#Override
public void sessionCreated(HttpSessionEvent se) {
HttpSession session = se.getSession();
if (session != null) {
LoggerService.logger.info("sessionCreated sessionid: {}, setMaxInactiveInterval: {}, ipaddress: {}",
session.getId(), session.getMaxInactiveInterval(), SecurityUtil.getIpAddress());
I did this to see internal times.
But on server, i see this log:
sessionCreated sessionid: 342E6139B2FE108D26537C9D684FBFF3, setMaxInactiveInterval: 1800, ipaddress: null
It must be 180, not 1800. Why does it multiply?
We dont have any other codes to set this. For example:
request.getSession(false).setMaxInactiveInterval(11);
We dont have this. But i will use this if i cant find any solution.
For example, for remote, i changed to this:
server.servlet.session.timeout=44s
But what i see is:
sessionCreated sessionid: 7C3573FE7B5FB6C8939DF8BF60B1B550, setMaxInactiveInterval: 1800, ipaddress: null
Tomcat9 is doing this?
On my local, i use that properties to test.
So
server.servlet.session.timeout=44s
for my local and remote server database configurations for my local.
But this time:
sessionCreated sessionid: 747E6BF3DCD061DFF306325FE4FD76B6, getMaxInactiveInterval: 60, ipaddress: 0:0:0:0:0:0:0:1
747E6BF3DCD061DFF306325FE4FD76B6 0:0:0:0:0:0:0:1 Session Created
What am i doing wrong?
FOr last test, i added this to success handler for my local but with remote properties:
LoggerService.logger.info("onAuthenticationSuccess sessionid: {}, getMaxInactiveInterval: {}, ipaddress: {}",
session.getId(), session.getMaxInactiveInterval(), SecurityUtil.getIpAddress());
request.getSession(false).setMaxInactiveInterval(55);
LoggerService.logger.info("onAuthenticationSuccess sessionid: {}, getMaxInactiveInterval: {}, ipaddress: {}",
session.getId(), session.getMaxInactiveInterval(), SecurityUtil.getIpAddress());
If i put my username password, i can see this:
: onAuthenticationSuccess sessionid: F796EA6C54D8BCA239A36E02C4A7A030, getMaxInactiveInterval: 60, ipaddress: 0:0:0:0:0:0:0:1
: onAuthenticationSuccess sessionid: F796EA6C54D8BCA239A36E02C4A7A030, getMaxInactiveInterval: 55, ipaddress: 0:0:0:0:0:0:0:1
I also did this:
#Bean // Http Listener
public HttpSessionListener httpSessionListener() {
return new HttpSessionListener() {
#Override
public void sessionCreated(HttpSessionEvent se) {
HttpSession session = se.getSession();
if (session != null) {
LoggerService.logger.info("sessionCreated sessionid: {}, setMaxInactiveInterval: {}, ipaddress: {}",
session.getId(), session.getMaxInactiveInterval(), SecurityUtil.getIpAddress());
session.setMaxInactiveInterval(55);
LoggerService.logger.info("sessionCreated sessionid: {}, setMaxInactiveInterval: {}, ipaddress: {}",
session.getId(), session.getMaxInactiveInterval(), SecurityUtil.getIpAddress());
It is again same:
sessionCreated sessionid: FFA7DC9A6558951F1CB790AD9D804F88, getMaxInactiveInterval: 60, ipaddress: null
sessionCreated sessionid: FFA7DC9A6558951F1CB790AD9D804F88, getMaxInactiveInterval: 55, ipaddress: null
FFA7DC9A6558951F1CB790AD9D804F88 0:0:0:0:0:0:0:1 Session Created
For remote, i tested with same code and also it worked but i dont want to set programatically
sessionCreated before sessionid: 38EC29F7C9C45B34D1FDF05B1F90DC3A, getMaxInactiveInterval: 1800, ipaddress: 192.ss
sessionCreated after sessionid: 38EC29F7C9C45B34D1FDF05B1F90DC3A, getMaxInactiveInterval: 180, ipaddress: 192.ss
So, there are two problems:
Why is application-remote-properties timeout value not working for local?
Why is remote timeout multiplied by 10 (properties has 3m but log shows 1800s)
The server.* properties are used to control the embedded container used by Spring Boot. Spring Boot will create an instance of the servlet container using one of the ServletWebServerFactory instances. These classes use the server.* properties to configure the controlled servlet container (tomcat, jetty etc).
However when you are deploying the application as a war file to a Tomcat instance the server.* properties don't apply. They don't apply because a pre-configured servlet container is available (as it is a remotely running service). So deploying to a remote Tomcat will make the server.* properties useless.
Regarding the session timeout being in minutes. Spring Boot will convert the session.servlet.session.timeout property to minutes, so 44s or 55s will be automatically converted to 1 minute. Setting it to something less then a minute also will not make much sense as Tomcat invalidates session with a thread running each minute.

CXF 3.2.2 CORS failed with "No 'Access-Control-Allow-Origin' header is present"

I was developing a Restful service using CXF + spring boot. and had problems with CORS setting
I set CORS on service definition interface (GateRs)
...
#Service
#CrossOriginResourceSharing(
allowAllOrigins = true,
allowHeaders = {
"Accept", "Accept-Charset", "Accept-Encoding", "Accept-Datetime",
"Accept-Language", "Authorization", "Content-Language", "Content-Length",
"Content-Type", "Origin", "User-Agent"},
exposeHeaders = {
"Accept", "Accept-Charset", "Accept-Encoding", "Accept-Datetime",
"Accept-Language", "Authorization", "Content-Language", "Content-Length",
"Content-Type", "Origin", "User-Agent"},
allowCredentials = true,
maxAge = 1209600 )
public interface GateRs {
#POST
#Path("/require")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#VoluntarySecure
public Res require(Req req);
...
implement interface with a class
#Component
public class Gate implements GateRs {
#Override
public Res require(Req req) {
...
attach the 'CrossOriginResourceSharingFilter' to the service bean
#Bean
public CrossOriginResourceSharingFilter cors() {
return new CrossOriginResourceSharingFilter();
}
#Bean(destroyMethod = "destroy") #DependsOn(Bus.DEFAULT_BUS_ID)
public Server jaxRsServer() {
final JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
factory.setServiceBean(new Gate());
factory.setProviders(Arrays.asList(jackson(), cors()));
factory.setBus(springBus());
factory.setAddress("/Gate");
return factory.create();
}
...
the client is a mobile application based on Ionic, and emulated by Chrome browser.
the '/Gate/require' returned '404' becasue CORS failed with no "
Access-Control-Allow-Origin" header.
I looked into the detail; and found out that the preflight ('OPTIONS') actually succeed; the "POST" failed, triggered the above mentioned messages
the preflight(OPTIONS)
the POST failed; it appears that the server side had not presented 'Access-Control-Allow-Origin' header in the response message
does anyone has any idea or suggestion?
thanks
I finally solved the problem by changing the '#CrossOriginResourceSharing' as follows:
...
#CrossOriginResourceSharing(
allowAllOrigins = true,
allowCredentials = true,
maxAge = 1209600 )
public interface GateRs {
...

Spring boot Authorization server redirection issue to client after successful authentication via Facebook

I am trying to setup a spring boot Authorizaiton server which will have the internal user login and OAuth2 with facebook. I am facing the below issues -
If I make my Authorization server SessionCreationPolicy.STATELESS then after successful authentication from facebook control get stuck in Authorization server itself (Its not returning to the my client application while if SessionCreationPolicy.IF_REQUIRED then control returns to my client app).
When I am using SessionCreationPolicy.IF_REQUIRED then control returns and I can do a authorization_code flow but the jwt token generated by spring-security-jwt gives me only user_name information in token which is facebook user's id (not even name).
My local user authentication code works fine with code flow and I can customize my token with custom token enhancer and add other properties also but when I try to customize facebook principal object to my custom user I get error that string can not be casted to custom user object.
Please refer to the repo for details - dev repo
I am using below code for setup/jwt generation -
#EnableOAuth2Client // for Oauth setup
// jwt enhancer which gives me error when principal is converted to custom user
class CustomTokenEnhancer implements TokenEnhancer {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
Map<String, Object> additionalInfo = new HashMap<>();
Authentication auth = authentication.getUserAuthentication();
/* additionalInfo.put("email", ((CustomPrincipal)auth.getPrincipal()).getEmail());
additionalInfo.put("roles", ((CustomPrincipal)auth.getPrincipal()).getRoles());
additionalInfo.put("id", ((CustomPrincipal)auth.getPrincipal()).getId());*/
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
//SSO filter i am using -
private Filter ssoFilter(ClientResources client, String path) {
OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(path);
OAuth2RestTemplate template = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext);
filter.setRestTemplate(template);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(
client.getResource().getUserInfoUri(), client.getClient().getClientId());
tokenServices.setRestTemplate(template);
filter.setTokenServices(tokenServices);
// filter.setAuthenticationSuccessHandler(authenticationHandler);
return filter;
}
Any help is appreciated.
Thanks!
I was able to get an explanation for 2nd and 3rd point-
Since after the authentication is successful from Facebook; Spring boot authorization server stores authentication object as below format -
{
"authorities": [
{
"authority": "ROLE_USER"
}
],
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": "xyzxyzxyzxyzxyz",
"tokenValue": "xyzxyzxyzxyzxyz",
"tokenType": "bearer",
"decodedDetails": null
},
"authenticated": true,
"userAuthentication": {
"authorities": [
{
"authority": "ROLE_USER"
}
],
"details": {
"id": "xyzxyzxyzxyzxyz",
"name": "xyzxyzxyzxyzxyz",
"email": "xyzxyzxyzxyzxyz"
},
"authenticated": true,
"principal": "xyzxyzxyzxyzxyz",
"credentials": "N/A",
"name": "xyzxyzxyzxyzxyz"
},
"principal": "xyzxyzxyzxyzxyz",
"oauth2Request": {
"clientId": "xyzxyzxyzxyzxyz",
"scope": [],
"requestParameters": {},
"resourceIds": [],
"authorities": [],
"approved": true,
"refresh": false,
"redirectUri": null,
"responseTypes": [],
"extensions": {},
"grantType": null,
"refreshTokenRequest": null
},
so when I was casting my principal to custom principal I was getting the error since in above model principal is just a string.
Note - I still have no idea how i can customize the above authentication object to my customuser object.

Spring Websocket url hits RequestMapping but not MessageMapping

I'm having trouble setting up a websocket configuration in an existing web application.
#Configuration
#EnableWebSocketMessageBroker
public class WebsocketConfig extends AbstractWebSocketMessageBrokerConfigurer{
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/mobile");
config.setApplicationDestinationPrefixes("/mobile-server");
config.setUserDestinationPrefix("/mobile-user");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/mobile-socket")
.withSockJS()
.setInterceptors(new HttpSessionHandshakeInterceptor());
}
}
Controller
#Controller
public class WebSocketInboxController{
#MessageMapping("/inbox")
#SendToUser("/mobile")
public Map<String,Object> inbox(
){
Map<String,Object> res = new HashMap<>();
res.put("hello", "hello");
return res;
}
client
const webstomp = require('webstomp-client');
const socket = webstomp.client('ws://www.dev.server.com/mobile-socket',{
debug:true
});
socket.connect('marc#gmail.com', '123456', (client) => {
console.log('connected');
socket.send("/mobile-server/inbox",)
socket.subscribe("/mobile/inbox");
}, (client, err) => {
console.log(err);
});
What I see when the client tries to connect is spring trying to match the /mobile-socket against the RequestMappings of the existing web application, finally hitting one that matches it by way of a #RequestMapping("/{somevar}").
I'm new to WebSockets, but I would expect the endpoint registration be a catchall for these kinds of connects?
Even after removing the erronous RequestMapping being hit, I can't seem to get the MessageMapping to be hit. I see this in my log
AntPathRequestMatcher.matches(150) | Request '/mobile-socket' matched by universal pattern '/**'
[MSA] DEBUG [2016-06-03T11:16:21,025] FilterSecurityInterceptor.beforeInvocation(219) | Secure object: FilterInvocation: URL: /mobile-socket; Attributes: [permitAll]
[MSA] DEBUG [2016-06-03T11:16:21,025] FilterSecurityInterceptor.authenticateIfRequired(348) | Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken#9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
[MSA] DEBUG [2016-06-03T11:16:21,025] AffirmativeBased.decide(66) | Voter: org.springframework.security.web.access.expression.WebExpressionVoter#444af45, returned: 1
[MSA] DEBUG [2016-06-03T11:16:21,025] FilterSecurityInterceptor.beforeInvocation(243) | Authorization successful
[MSA] DEBUG [2016-06-03T11:16:21,026] FilterSecurityInterceptor.beforeInvocation(256) | RunAsManager did not change Authentication object
[MSA] DEBUG [2016-06-03T11:16:21,026] FilterChainProxy.doFilter(325) | /mobile-socket at position 16 of 16 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
[MSA] DEBUG [2016-06-03T11:16:21,026] FilterChainProxy.doFilter(310) | /mobile-socket reached end of additional filter chain; proceeding with original chain
[MSA] DEBUG [2016-06-03T11:16:21,027] ExceptionTranslationFilter.doFilter(117) | Chain processed normally
[MSA] DEBUG [2016-06-03T11:16:21,027] HstsHeaderWriter.writeHeaders(130) | Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher#53cc2afb
[MSA] DEBUG [2016-06-03T11:16:21,027] HttpSessionSecurityContextRepository.saveContext(352) | SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
[MSA] DEBUG [2016-06-03T11:16:21,028] SecurityContextPersistenceFilter.doFilter(120) | SecurityContextHolder now cleared, as request processing completed
The Spring tries to match the "/mobile-socket" against the RequestMappings because all the requests go to HandlerMapping beans in the web application context to map incoming web requests to appropriate handlers. With the introduction of annotated controllers, RequestMappingHandlerMapping automatically looks for #RequestMapping annotations on all #Controller beans including the controller which has #MessageMapping.
Since the #MessageMapping can only be defined under #Controller annotation, Spring would try to match other RequestMappings as well.
One possible solution could be to introduce interceptor to handle the websocket request url to specifically map to a particular controller. You can give it a try!

Spring Security pre-authentication - gives me a new session even though principal is unchanged

I have implemented a spring security preathentication filter in my Grails application in order to integrate with Tivoli Access Manager.
The filter getting called for every request in my web application - yet even though the filter returns the same principal as a previous request, it seems to create a new session. I have created a ApplicationListener to listen for authentication events and I can see a new AuthenticationSuccessEvent, with a new session id for each request.
This means all my session variables get cleared each request- which wouldn't be a big deal but it breaks the uploadr plugin.
When I turn debug logging on for my preauthentication filter I see that it thinks the principal has changed, even though it has not:
2015-03-04 11:34:57.769 foobar.TamAuthenticationFilter Checking secure context token: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken#f0666480: Principal: grails.plugin.springsecurity.userdetails.GrailsUser#3125618: Username: 66734; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_APPROVER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#fffde5d4: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 87928D9E25D98DD3CCFAC5D67689E609; Granted Authorities: ROLE_ADMIN, ROLE_APPROVER
2015-03-04 11:34:57.770 foobar.TamAuthenticationFilter Pre-authenticated principal has changed to 66734 and will be reauthenticated
2015-03-04 11:34:57.770 foobar.TamAuthenticationFilter Invalidating existing session
2015-03-04 11:34:57.771 foobar.TamAuthenticationFilter preAuthenticatedPrincipal = 66734, trying to authenticate
How can I make spring security use the same session for each principal returned by the pre authentication filter, rather than create a new one for every request?
here is my filter:
package foobar
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter
import grails.util.Environment
import grails.util.Holders
import groovy.util.logging.Log4j
#Log4j
class TamAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {
java.lang.Object getPreAuthenticatedCredentials(javax.servlet.http.HttpServletRequest request)
{
"N/A"
}
java.lang.Object getPreAuthenticatedPrincipal(javax.servlet.http.HttpServletRequest request)
{
Long staffId = getStaffIdFromTamHeader(request)
if(!staffId)
log.error "iv-user header not found"
return staffId
}
/**
* Get Staff ID from the ivUser Tamheader.
* #param request
* #return
*/
static public Long getStaffIdFromTamHeader(request) {
return request.getHeader("iv-user")
}
}
LoggingSecurityEventListener:
package foobar
import groovy.util.logging.Log4j
import org.springframework.context.ApplicationListener
import org.springframework.security.authentication.event.AbstractAuthenticationEvent
#Log4j
class LoggingSecurityEventListener implements
ApplicationListener<AbstractAuthenticationEvent> {
void onApplicationEvent(AbstractAuthenticationEvent event) {
def username = event.authentication.principal
def address = event.authentication.details.remoteAddress
def sessionId = event.authentication.details.sessionId
log.info "event=${event.class.simpleName} username=${username} remoteAddress=${address} sessionId=${sessionId}"
}
}
resources.groovy:
beans = {
//
// grabs the user id from the tam headers
//
tamAuthenticationFilter(TamAuthenticationFilter) {
authenticationManager = ref('authenticationManager')
checkForPrincipalChanges = true
}
tamAuthenticationProvider(PreAuthenticatedAuthenticationProvider) {
preAuthenticatedUserDetailsService = ref('authenticationUserDetailsService')
}
//
// we do not want to redirect to the auth/login page since we are using tam
//
authenticationEntryPoint(Http403ForbiddenEntryPoint)
securityEventListener(LoggingSecurityEventListener)
}
config.groovy:
grails.plugin.springsecurity.useSecurityEventListener = true
grails.plugin.springsecurity.providerNames = ['tamAuthenticationProvider']
grails.plugin.springsecurity.userLookup.userDomainClassName = 'strobe.auth.User'
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'strobe.auth.UserRole'
grails.plugin.springsecurity.authority.className = 'strobe.auth.Role'
grails.plugin.springsecurity.securityConfigType="InterceptUrlMap"
grails.plugin.springsecurity.interceptUrlMap = [
'/foobar/**': ['ROLE_USER']]
bootstrap.groovy:
def init = { servletContext -> SpringSecurityUtils.clientRegisterFilter('tamAuthenticationFilter',
SecurityFilterPosition.PRE_AUTH_FILTER.order + 10)
}
I have found the solution to my problem- I was returning a Long from getPreauthenticatedPrincipal() which confuses spring security, as the method requiresAuthentication() in AbstractPreauthenticatedProcessingFilter has this line of code:
if ((principal instanceof String) && currentUser.getName().equals(principal)) {
return false;
}
It expects the principal to be a String. Every time I returned a long it would reauthenticate and give me a new session.
I can't believe the solution was that simple and it took me 2 days to figure out!!!
Part of the problem I think is the scant documentation on preauthentication in spring and also the method signature of:
java.lang.Object getPreAuthenticatedPrincipal(javax.servlet.http.HttpServletRequest request)
which doesn't clearly suggest a return type.

Resources