Oauth2 login with Apple ID returns request without CSRF token - spring-boot

I am trying to implement login with Apple ID in a Spring Boot application. I have it currently working so that I get the Apple login screen, and can authorize access. However, when Apple redirects back to https://example.com/myapp/login/oauth2/apple, I get the following error in my log :
2020-03-10 09:41:32.574 DEBUG 13644 --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /login/oauth2/code/apple at position 3 of 16 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-03-10 09:41:32.574 DEBUG 13644 --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /login/oauth2/code/apple at position 4 of 16 in additional filter chain; firing Filter: 'CsrfFilter'
2020-03-10 09:41:32.575 DEBUG 13644 --- [nio-8080-exec-5] o.s.security.web.csrf.CsrfFilter : Invalid CSRF token found for https://example.com/myapp/login/oauth2/code/apple
How can I make this work properly, without disabling CSRF facilities?

The problem is in the samesite=lax in 'session' cookie. Because of that, previous session related to login is being blocked by browser and new session is being created after Apple redirects to redirectUrl. This happens to all POST requests, since it is security issue and browsers drop cross-domain cookies. In order to resolve this issue, I disabled samesite in chrome and added the following config to my backend:
#Configuration
public class CookieConfiguration {
#Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setSameSite("none");
serializer.setUseSecureCookie(true);
return serializer;
}
}
And my csrf config is as following:
.csrf(c -> c
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringAntMatchers("/login/oauth2/code/apple")
)
At least, this gives me a chance to test my code. I will keep updating if I find how to resolve this in production.

Related

how to pass a parameter in the Header with multipartFormData

#PostMapping(value = "/uploadCV" , consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public ResponseEntity<?> uploadCV(#RequestHeader("token") String token,
#RequestParam("file") MultipartFile cvFile) throws Exception {
log.info(token + cvFile.getOriginalFilename()));
return curriculumService.addCurriculum(token, pdfFile);
}
If i use MediaType.MULTIPART_FORM_DATA_VALUE don't get the value "token" :
2022-11-11 16:13:28.040 WARN 7283 --- [nio-8080-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MissingRequestHeaderException: Required request header 'token' for method parameter type String is not present]
If i use MediaType.MULTIPART_MIXED_VALUE don't get file:
org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
how can i do?
The code that you have written with MediaType.MULTIPART_FORM_DATA_VALUE is absolutely correct. It is very clear from error message what is the issue.
2022-11-11 16:13:28.040 WARN 7283 --- [nio-8080-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MissingRequestHeaderException: Required request header 'token' for method parameter type String is not present]
Issue:
Code is expecting header name with key token should present in the request but it is not present.
Solution:
Please add header name with key token like this and try again.

Spring WebFlux - WebGraphQlInterceptor has empty request data

I would need to retrieve data from query headers for use in the GraphQl query controller. I searched around a bit and found the WebGraphQlInterceptor, but realized that when the WebGraphQlRequest argument is invoked it is completely devoid of information.
For example, this code:
#Component
class WebRequestInterceptor : WebGraphQlInterceptor {
val logger: Logger by lazy { LoggerFactory.getLogger(WebRequestInterceptor::class.java) }
override fun intercept(request: WebGraphQlRequest, chain: WebGraphQlInterceptor.Chain): Mono<WebGraphQlResponse> {
logger.info("URI", request.uri.toString())
logger.info("HEADERS", request.headers.toString())
logger.info("DOCUMENT", request.document)
return chain.next(request)
}
}
Returns:
2022-09-12 10:57:11.747 INFO 1 --- [or-http-epoll-1] b.s.b.gateway.auth.WebRequestInterceptor : URI *empty string*
2022-09-12 10:57:11.748 INFO 1 --- [or-http-epoll-1] b.s.b.gateway.auth.WebRequestInterceptor : HEADERS *empty string*
2022-09-12 10:57:11.748 INFO 1 --- [or-http-epoll-1] b.s.b.gateway.auth.WebRequestInterceptor : DOCUMENT *empty string*
P.S. The same thing happens if I try to log a single headers element using the request.headers.getFirst(..) function.

About Spring WebClient on external onTerminate event

I'm running a spring-boot v2.0.3 tomcat-embedded webserver 8.5.31, to Serve Spring Webflux REST services.
One of those REST services calls to another, external REST Webservice.
public Mono<ServerResponse> select(ServerRequest request) {
return request.principal().cast(Authentication.class)
.flatMap(principal ->
client.get().uri(f -> buildUri(request, principal, request.queryParams(), f))
.exchange())
.flatMap((ClientResponse mapper) ->
ServerResponse.status(mapper.statusCode())
.headers(c -> mapper.headers().asHttpHeaders().forEach(c::put))
.body(mapper.bodyToFlux(DataBuffer.class)
.delayElements(Duration.ofSeconds(10))
.doOnCancel(() -> log.error("Cancelled client"))
.doOnTerminate(() -> log.error("Terminated client")), DataBuffer.class))
.doOnTerminate(() -> log.error("Termination called"));
}
If a browser calls my REST-Service, and after a short while cancels the connection, I can see the outer "Termination called" event, and that the client was terminated also. But the client termination seems to trigger an error in tomcat:
2018-07-25 12:50:42.860 DEBUG 12084 --- [ elastic-3] org.example.search.security.UserManager : Authorizing org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken#809aec11: Principal: cn=dv dbsearch client, ou=dbsearch, o=example, l=eb, st=unknown, c=de; Credentials: [PROTECTED]; Authenticated: false; Details: null; Not granted any authorities
2018-07-25 12:50:42.864 DEBUG 12084 --- [ elastic-3] org.example.search.security.UserManager : Successfully authorized: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken#c03925ec: Principal: org.springframework.security.core.userdetails.User#809aec0e: Username: cn=dv dbsearch client, ou=dbsearch, o=example, l=eb, st=unknown, c=de; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_ADMIN
2018-07-25 12:50:45.470 ERROR 12084 --- [ctor-http-nio-4] c.d.s.s.h.SolrSelectRequestHandler : Termination called
2018-07-25 12:51:15.562 ERROR 12084 --- [ parallel-3] c.d.s.s.h.SolrSelectRequestHandler : Terminated client
2018-07-25 12:51:15.625 ERROR 12084 --- [nio-8443-exec-2] o.s.w.s.adapter.HttpWebHandlerAdapter : Unhandled failure: Eine bestehende Verbindung wurde softwaregesteuert durch den Hostcomputer abgebrochen, response already set (status=200)
2018-07-25 12:51:15.628 WARN 12084 --- [nio-8443-exec-2] o.s.h.s.r.ServletHttpHandlerAdapter : Handling completed with error: Eine bestehende Verbindung wurde softwaregesteuert durch den Hostcomputer abgebrochen
2018-07-25 12:51:15.652 ERROR 12084 --- [nio-8443-exec-2] o.a.catalina.connector.CoyoteAdapter : Exception while processing an asynchronous request
java.lang.IllegalStateException: Calling [asyncError()] is not valid for a request with Async state [DISPATCHING]
at org.apache.coyote.AsyncStateMachine.asyncError(AsyncStateMachine.java:424)
at org.apache.coyote.AbstractProcessor.action(AbstractProcessor.java:470)
at org.apache.coyote.Request.action(Request.java:431)
at org.apache.catalina.core.AsyncContextImpl.setErrorState(AsyncContextImpl.java:388)
at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:176)
at org.apache.coyote.AbstractProcessor.dispatch(AbstractProcessor.java:232)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:53)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Sorry for the german errormessages, it means "client abortet connection".
I don't really have a problem with this errormessage per se, it's just, that my buffers in spring's Webclient don't seem to be cleared up (the log I did not reproduce locally, so it has diferent timestamps):
2018-07-23 08:44:36.892 ERROR 22707 — [reactor-http-nio-5] io.netty.util.ResourceLeakDetector : LEAK: ByteBuf.release() was not called before it's garbage-collected. See http://netty.io/wiki/reference-counted-objects.html for more information.
Recent access records:
Created at:
io.netty.buffer.PooledByteBufAllocator.newDirectBuffer(PooledByteBufAllocator.java:331) io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:185)
So here the question: How can I cleanly end the WebClient connection, when the request to my REST-Service is cancelled?
I can't really say for sure about that exception message, but I know Tomcat improved this in the 8.5.x generation. Which version are you using? If you can provide a consistent way to reproduce this with a minimal application, you could create a new issue in jira.spring.io on Spring Framework, or Tomcat itself if you managed to reproduce it without Spring (although it should be a hard one to reproduce).
Now about releasing DataBuffer instances - DataBuffer instances can be pooled, depending on the implementation. Here the WebClient is using Netty, which is pooling buffers. So they need to be released when they're no longer used.
Looking at your implementation, I think those unreleased buffers come from this:
the WebClient is fetching data from the remote endpoint and creating DataBuffer instances
various Reactor operators along the way are buffering those using internal queues (depending on the prefetching and the operators used, the amount of queued buffers can vary)
when the subscriber fails or cancels, those buffers sitting in internal queues are not released as they should.
Currently Reactor does not offer a hook point to reach those objects in those error cases. But this is a brand new feature that's been added in Reactor core 3.2.0. This will be leveraged internally by Spring Framework with SPR-17025. Please follow this issue - your use case might be handy when it comes to testing the fix.

RetryWithHttpsEntryPoint

I'm having a problem with some of the links on my web application on netbeans. The web app has spring mvc and spring security implemented.
So basically, I have a link on one of the pages
<td colspan="2" align="center">
<h3 align="#center">You can login here.</h3>
</td>
Then when I click "here", I get an error: "Firefox has detected that the server is redirecting the request for this address in a way that will never complete."
The logs are as follows:
14:45:34,255 DEBUG FilterChainProxy:176 - Converted URL to lowercase, from: '/login.jsp'; to: '/login.jsp'
14:45:34,255 DEBUG FilterChainProxy:183 - Candidate is: '/login.jsp'; pattern is /**; matched=true
14:45:34,255 DEBUG FilterChainProxy:351 - /login.jsp at position 1 of 13 in additional filter chain; firing Filter: 'org.springframework.security.web.access.channel.ChannelProcessingFilter#a3d369'
14:45:34,255 DEBUG DefaultFilterInvocationSecurityMetadataSource:178 - Converted URL to lowercase, from: '/login.jsp'; to: '/login.jsp'
14:45:34,255 DEBUG DefaultFilterInvocationSecurityMetadataSource:196 - Candidate is: '/login.jsp'; pattern is /; matched=false
14:45:34,255 DEBUG DefaultFilterInvocationSecurityMetadataSource:196 - Candidate is: '/login.jsp'; pattern is /login.jsp*; matched=true
14:45:34,255 DEBUG ChannelProcessingFilter:101 - Request: FilterInvocation: URL: /login.jsp; ConfigAttributes: [REQUIRES_SECURE_CHANNEL]
14:45:34,255 DEBUG RetryWithHttpsEntryPoint:65 - Redirecting to:
14:45:34,271 DEBUG FilterChainProxy:176 - Converted URL to lowercase, from: '/'; to: '/'
If you notice, my app loses the url. It was able to match the url but then with the RetryWithHttpsEntryPoint, it's redirected to a blank url.
Any suggestions to fix this? Thanks!
this happens when you redirect to different url in your controller .
for eg:
return "redirect:/page";
where "page" is the direct name of page you want to navigate, you should use redirect in case you are redirecting to other mapping in your controller.
instead you should go for
return "/page";

How to debug parameters sent in Grails

Grails is so nice to append the parameters sent to console output in the case of an error:
2011-05-23 12:17:05,173 [http-8080-5] ERROR errors.GrailsExceptionResolver - Exception occurred when processing request: [POST] / - parameters:
maps: on
maps: on
maps: on
maps:
_isPublic:
description: test
name: set1
isPublic: on
Stacktrace follows:
...
But how can I tell it to always show me which parameters are sent (like in Rails, for example)?
My current log4j configuration looks as follows:
error 'org.codehaus.groovy.grails.web.servlet', // controllers
'org.codehaus.groovy.grails.web.pages', // GSP
'org.codehaus.groovy.grails.web.sitemesh', // layouts
'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping
'org.codehaus.groovy.grails.web.mapping', // URL mapping
'org.codehaus.groovy.grails.commons', // core / classloading
'org.codehaus.groovy.grails.plugins', // plugins
'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration
'org.springframework',
'org.hibernate',
'net.sf.ehcache.hibernate'
warn 'org.mortbay.log'
I don't believe Grails itself supports logging the request parameters for each request. However, you can easily implement this yourself in a filter:
package com.example
class MyFilters {
private static final log = org.apache.commons.logging.LogFactory.getLog(this)
def filters = {
paramLogger(controller:'*', action:'*') {
before = {
log.debug "request params: $params"
}
}
}
}
Remember to enable debug logging for this filter in `Config.groovy by adding
debug com.example
to the log4j closure
You probably want to get more output from org.codehaus.groovy.grails.web so set that one to a lower level (debug or trace)

Resources