This issue is based on Spring Security OAuth GitHub issue, which @jgrandja asked me to explain and expand here.
I'm using spring stack (Spring Boot 2.0.0.RELEASE) for creating a site that delegues user authentication/registration to Google via OAuth2. It is implemented as a few Spring Cloud microservices with a Zuul gateway running on port 8080.
Google Auth Server
Zuul Gateway(:8080)
/ \
/ \
/ \
Other OAuth2 Client Microservice(:5000)
Microservices
I use Google as an OAuth2 server, and use spring-security-oauth2 as a client, which is implemented as a separate microservice. If all my cloud is deployed at localhost everything works fine. But if my microservices are deployed at different machines, e.g. virtual machines or AWS Elastic Beanstalk, OAuth login doesn't work.
The application uses Google for registering and authenticating users. Problem can be reproduced, if launch Zuul Gateway microservice on some separate virtual machine and other microservices should be launched at local machine ☝️
So Google should be called from the browser on VM. When the Google's callback hits the application the following exception appears (from the log of the Client Microservice):
org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_redirect_uri_parameter]
at org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider.authenticate(OAuth2LoginAuthenticationProvider.java:117) ~[spring-security-oauth2-client-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174) ~[spring-security-core-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
...
Taking a look at the sources shows that the issue seems to be in the following check in the org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider class:
if (!authorizationResponse.getRedirectUri().equals(authorizationRequest.getRedirectUri())) {
OAuth2Error oauth2Error = new OAuth2Error(INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
It happens, due to the following return values:
authorizationResponse.getRedirectUri()
http://192.168.1.2:5000/auth/login/oauth2/code/google
authorizationRequest.getRedirectUri()
http://localhost:8080/auth/login/oauth2/code/google
Here in redirect_uriof Response I have _IP address of the machine, where Client Microservice is running_, and that's why request's and response's redirect_uris are not matching, so Spring Security is rejecting the user authentication with an exception 😞
While running microservices at localhost everything is ok
Zuul Gateway Microservice config
server:
port: 8080
hystrix:
command:
default:
execution:
timeout:
enabled: false
ribbon:
ReadTimeout: 20000
ConnectTimeout: 20000
zuul:
ignoredServices: '*'
host:
connect-timeout-millis: 20000
socket-timeout-millis: 20000
routes:
auth:
path: /auth/**
url: ${AUTH_SERVICE_URI:http://localhost:5000}
stripPrefix: false
sensitiveHeaders:
OAuth2 Client Microservice config
server:
port: 5000
servlet:
contextPath: /auth
use-forward-headers: true
spring:
security:
oauth2:
resource:
filter-order: 3
client:
registration:
google:
client-id: [REMOVED]
client-secret: [REMOVED]
redirect-uri-template: ${AWS_CLOUD_URI:http://localhost:8080}/auth/login/oauth2/code/google
scope: profile,email
Spring Boot 2.0.0.RELEASE
Spring Security OAuth2 Client 5.0.3.RELEASE
@ayulit Looking at your OAuth2 Client config:
redirect-uri-template: ${AWS_CLOUD_URI:http://localhost:8080}/auth/login/oauth2/code/google
the AWS_CLOUD_URI env variable is not resolving so it's defaulting to http://localhost:8080, which explains why the Authorization Request is setup with:
authorizationRequest.getRedirectUri()
http://localhost:8080/auth/login/oauth2/code/google
For a quick test, set the redirect-uri-template to:
redirect-uri-template: http://192.168.1.2:5000/auth/login/oauth2/code/google
given that the OAuth2 Client application is running under http://192.168.1.2:5000/auth based on the Authorization Response you provided:
authorizationResponse.getRedirectUri()
http://192.168.1.2:5000/auth/login/oauth2/code/google
Let me know the results of your test.
Thanks.
I recommend using the default configuration, such as:
redirect-uri-template: {baseUrl}/{action}/oauth2/code/{registrationId}
The default configuration is much more convenient. Instead, configuring the redirect-uri-template makes the program more confused.
@ayulit Were you able to resolve your issue based on my comments? If so, can you please close this issue. Thanks.
@jgrandja As I mentioned before inside Expected Behavior section, if I work with microservices, deployed on the same IP and port everything works fine.
However it is requirement of my architecture to set redirect-uri-template to http://localhost:8080 or AWS_CLOUD_URI, which is the domain name of my production deployment server. It is assumed that all traffic from the 'outer world' will be go through gateway on port 8080. Also other microservices can be deployed on different IPs in production.
So is it possible to avoid this restriction in the check org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider.java:117 in my case?
@ayulit Did you see my previous comment?
Looking at your OAuth2 Client config:
redirect-uri-template: ${AWS_CLOUD_URI:http://localhost:8080}/auth/login/oauth2/code/google
the AWS_CLOUD_URI env variable is not resolving so it's defaulting to http://localhost:8080, which explains why the Authorization Request is...
I don't think your AWS_CLOUD_URI env is setup correctly in your production environment. This is why it's defaulting to http://localhost:8080.
@jgrandja AWS_CLOUD_URI env is setup 100% correctly. But it is different in any case with IP of microservice with which OAuth2 Client application is running.
Let me illustrate what is going on:
A: Zuul Gateway as Spring Boot Microservice is running on port 8080 at Machine One with IP address XX.
B: OAuth2 Client as Spring Boot Microservice is running on port 5000 at Machine Two with IP address YY.
When we come to check in org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider.java:117
authorizationRequest.getRedirectUri() gets us URI with IP XX, because we set it up inside the config.
BUT authorizationResponse.getRedirectUri() gets us URI with IP YY, besause, as far as I understood it is eventually formed dynamically and depends on the server on which we run corresponding microservice - see method getRequestURL() in org.apache.catalina.connector.Request.java:2377
In fact this is an issue that is not related to Spring Security OAuth. He needs to understand how zuul supports such features as X-Forwarded-For(server.tomcat.protocol-header and server.use-forward-headers).
This needs to rewrite the http header named "host" on zuul. The backend microservice does not know what domain name it exposes on the front end. This is required for redirect requests.
@ayulit Have you tried setting the redirect-uri-template from:
${AWS_CLOUD_URI:http://localhost:8080}/auth/login/oauth2/code/google
to
{baseUrl}/auth/login/oauth2/code/{registrationId}
This should work if the Proxy (Zuul) is configured to add the X-Forwarded-* headers and the OAuth2 Login application is set with server.use-forward-headers=true.
See these resources for further information:
I encountered the same error when I m setting up a Spring Boot application to authenticate users using Facebook OAuth2 implementation. Nginx (functions as reverse proxy) is configured to front the web app and also to offload the SSL cert.
The server.use-forward-headers=true works for me though. I removed the redirect-uri-template and let spring boot security use the default configuration.
@ayulit Were you able to resolve your issue based on the feedback? If so, please close this issue. Thanks.
@ayulit I'm closing this from lack of feedback. If you're still having issues we can re-open.
Based on the @jgrandja 's last comment, this looks meant to be closed.
Thanks @izeye. I forgot to close it :)
I am running into simmilar issue where I am using IIS proxy infornt of an API Gateway biuld using pring-boot-starter-webflux, spring-boot-starter-oauth2-client, spring-cloud-starter-gateway and spring-cloud-starter-security. In the configfuration for the application I have set the:
server.tomcat.protocol-header: X-Forwarded-Proto
server.use-forward-headers: true
spring.security.oauth2.client.registration.sgateway.redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
No matter what I do I keep getting either
[invalid_redirect_uri_parameter]org.springframework.security.oauth2.core.OAuth2AuthenticationException
or the that the in the location header the redirect_uri query parameter passed is incorrcect.
Not sure exactly what I am missing?
Thanks!
@GerryBennett-TexasMutual, thanks for getting in touch, but it feels like this is a question that would be better suited to Stack Overflow. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements. Feel free to update this issue with a link to the re-posted question (so that other people can find it) or open an issue with more detail if you feel this is a Spring Security bug.
Most helpful comment
I encountered the same error when I m setting up a Spring Boot application to authenticate users using Facebook OAuth2 implementation. Nginx (functions as reverse proxy) is configured to front the web app and also to offload the SSL cert.
The
server.use-forward-headers=trueworks for me though. I removed theredirect-uri-templateand let spring boot security use the default configuration.