Spring Security Azure AD redirect url issue - spring-security

When I try running in localhost, it works fine. But when I try running the same behind a load balancer, it gives the following error:
AADSTS50011: The reply URL specified in the request does not match the reply URLs configured for the application: '<clien-id>'.
I have registered the application at AzureAD with the load balancer URL. But when I send my request, the redirect URL is still localhost as shown below.
https://login.microsoftonline.com/common/oauth2/authorize?response_type=code&client_id=XXX&...**redirect_uri=localhost:8080/login/oauth2/code/azure**&nonce=...
I want my application to insert the load balancer URL as the value of redirect_url (instead of localhost).
I tried the solutions suggested below and still not successful:
Redirect URL for Spring OAuth2 app on Azure with Active Directory: Invalid Redirect URI Parameter
Spring Boot using Azure OAuth2 - reply URL does not match error
Thanks in advance.

When you use a load balancer/proxy, you need to add some extra configuration to make it possible to resolve the redirect URL correctly.
A load balancer usually applies the standard RFC7239 "Forwarded Headers" like X-Forwarded-Proto and X-Forwarded-Host. In that case, the redirect url should be correctly computed after applying the following two configurations. (Example for the Tomcat scenario)
server.forward-headers-strategy=NATIVE
"If the proxy adds the commonly used X-Forwarded-For and
X-Forwarded-Proto headers, setting server.forward-headers-strategy to
NATIVE is enough to support those."
server.tomcat.redirect-context-root=false
If you are using Tomcat and terminating SSL at the proxy,
server.tomcat.redirect-context-root should be set to false. This
allows the X-Forwarded-Proto header to be honored before any redirects
are performed.
The above configuration works if you use a placeholder for the base URL in your client configuration in Spring Security, for example {baseUrl}/login/oauth2/code/{registrationId}. In this way, the {baseUrl} placeholder is dynamically resolved by Spring Security differently depending on whether it's behind a load balancer or not (https://your-lb-url.com vs http://localhost:8080).
More info in the official documentation:
Spring Boot - Running Behind a Front-end Proxy Server
Spring Security - Proxy Server Configuration

Related

Proper way to set up Rails 6 on AWS ECS

I have Rails 6 (running with Puma) deployed on AWS ECS. There's an ECS Service and an ECS Task, and the Task spins up EC2 instances that host my app. I also created an Application Load Balancer with my ECS Service. I added an HTTPS listener to the load balancer. My listener points to a Target Group that accepts traffic specifically via the HTTP protocol.
My understanding of the request flow:
HTTPS request from internet:
--> hits AWS load balancer
--> hits HTTPS listener
--> passes traffic using HTTP to Target Group
--> request finally reaches Rails app on Target Group EC2 targets *over HTTP*
Is this a valid setup? I read this StackOverflow answer and my interpretation is that that we only need HTTPS for our load balancer and not for Puma and thus not for the Target Group.
I also set up a health check over HTTP for my Target Group that expects a 301 status code response as a healthy target (because I have config.force_ssl on in my Rails config). The thing is, why does traffic from the load balancer not get redirected? Why does traffic from the health check get redirected? Aren't they both hitting the same Target Group? Why does one request result in a 200 while the other results in a 301?
I've made a drawing to try to capture my question/confusion/current understanding:
Here's my load balancer and target group setup:
Not sure if this is an AWS issue or if it's a Puma issue or something else. Taking all ideas! Thank you!!
Answering your 2 questions.
Why does traffic from the load balancer not get redirected?
This answer explains it as well, but I'll dig a little bit deeper: when the load balancer receives a HTTPS connection and forwards it via HTTP to your Rails server, it will set a X-FORWARDED-PROTO=https header to the request, and Rails understands that as enough for the force_ssl config.
The code path is that Rails
https://github.com/rails/rails/blob/main/actionpack/lib/action_dispatch/middleware/ssl.rb (which, as per the comments in that file, is included in the request chain when config.force_ssl is true) will call request.ssl?
request in that context is an instance of https://github.com/rails/rails/blob/main/actionpack/lib/action_dispatch/http/request.rb, but you won't find the ssl? method defined there; however, that class includes include Rack::Request::Helpers.
If you go to Rack's source code, you'll find the ssl? method defined in https://github.com/rack/rack/blob/master/lib/rack/request.rb, where it calls scheme method, and the X-Forwarded-Proto header will be checked in the elsif forwarded_scheme part. And there you go :) Even tough it's a HTTP request, the presence of that header will make the request.ssl? return true.
Why does traffic from the health check get redirected?
In this case, it's a HTTP request from the load balancer to your application that doesn't carry that X-Forwarded-For or X-Forwarded-Proto header, so Rail's config.force_ssl is doing what it's supposed to do and redirecting.
Lastly, check out the documentation on config.force_ssl. You actually can set it to a hash of options, and exclude your healthcheck urls from the force_ssl behavior!
config.ssl_options = { redirect: { exclude: -> request { request.path =~ /healthcheck/ } } }

Grails spring security redirects to wrong port

In my Grails app (2.3.11), my login page sends an Ajax request to:
https://myurl/my-app/j_spring_security_check
but spring-security redirects to:
https://myurl:80/my-app/login/ajaxSuccess
This results in a timeout error (because port 80 is added on the URL).
This problem only occurs when my client accesses the application through their traffic manager(Big-IP); if they access the application directly through server IP, it works correctly.
Is there any configuration I can do in Grails to fix this problem? I'm not sure if this problem is related to the application or Big-IP.
These are my configs (Config.groovy) related to spring-security plugin:
grails.plugins.springsecurity.successHandler.defaultTargetUrl = '/login/authSucccessExtJs'
grails.plugins.springsecurity.successHandler.alwaysUseDefault = true
grails.plugins.springsecurity.failureHandler.defaultFailureUrl = '/login/authFailExtJs?login_error=1'
grails.plugins.springsecurity.password.algorithm = 'MD5'
The problem is your application is receiving http traffic because you are offloading ssl at the BIG-IP, so it returns http links to your client. There are a few potential solutions.
Configure grails to set all URLs to https, even though requests are http
Insert the header X-Forwarded-Proto: https (if grails honors this) at the BIG-IP via a local traffic policy or an iRule (you can test this from curl by inserting the header there to see if that helps)
Rewrite https to http URLs on BIG-IP in response traffic via a stream profile or an iRule. This can be very problematic with AJAX but otherwise will work, however, option 1 or 2 would be far more efficient and less maintenance.

Swagger proxied by haproxy can't execute requests

I have a swagger working with a haproxy. I use built in swagger in Websphere Liberty Profile (apiDiscovery feature):
Browser -swagger.mydomain.com-> haproxy -swagger.intranet-> IBM Liberty server with Swagger
The first swagger page is generated and shown correctly in the browser, but as Liberty server gets the request from haproxy, not my browser, and gets them to the intranet name/ip (swagger.intranet), Swagger code to execute GETs, POSTs, etc. is generated with that intranet IP name (swagger.intranet), so when I try any of the methods they won't work as reference this internal ip name from in a browser outside that zone.
Can I configure haproxy with some header to inform haproxy that he should generate code with the original server name (swagger.mydomain.com) request used in the request? (That is the one to be used in the generated HTML/Javascript code)
Thanks.
Liberty trusts the Host: header and uses it to assemble self-referential links.
Where you define the backend, try setting http-request set-header Host swagger.mydomain.com to what the client will be using or removing a similar stanza if you are setting it to some swagger.intranet already.
(sorry, I'm not an HAProxy user. This is based on searching for 'HAProxy equivalent of ProxyPreserveHost')

Elastic Load Balancer on port 443 works for forced SSL Ruby On Rails application, but why?

My ruby on Rails application is configured with the following:
config.force_ssl = true
And I set up the following elastic load balancer:
With this configuration everything works, but I do not understand why? with the code above, my application instance will return a 301 redirect in response to HTTP request. When the HTTP request is handled by the load balancer, it is forwarded on to to the instance as a HTTP request. Shouldn't this result in another 301, and therefore an endless loop?
EDIT
I thought a bit about my answer and decided to get in to some more detail with it.
Network communication is usually composed of several layers, among which are the physical layer, which is the cable/radio channel where information travels through, the transport layer which is often TCP/IP, the protocol layer which in our case is usually HTTP or HTTPS and finally the application layer which is what our rails app handles.
Rails usually never gets in touch with the actual HTTPS data stream, as this is handled by your webserver. So how does force_ssl work at all?
The protocol layer is handled by the webserver (nginx, mongrel...) and this is who could care first about forcing ssl. When the webserver hands over a request to the application layer (hence, the rails app), it also provides a lot of meta data, which includes requester IP, request path, request format, a lot of header variables and also information about the used protocol.
When a request arrives at your webserver on port 443 (and uses HTTPS protocol), the webserver sets the header flag SERVER_PROTOCOL to https.
If a proxy server (like load balancer is) receives a request on 443 and forwards it to 80, it adds the X-FORWARDED-PROTO=https header to the request, which is made available for your rails app by the webserver.
Now, long story short: config.force_ssl requires SERVER_PROTOCOL OR X-FORWARDED-PROTO to denote https.
ORIGINAL ANSWER
The rails force_ssl method does not really force a request to arrive on port 443 on your server, it is satisfied when the original (client) request was sent over ssl through the internet. The load balancer (as a proxy) sets the header X-FORWARDED-PROTO to "https". rails trusts that information and that is why this is working.
More info on that can be found in the elastic load balancer docs: http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/TerminologyandKeyConcepts.html#x-forwarded-for

https URL redirecting to external site

Hi I have a website that I will be developing in the future.
Upon looking at the current website I noticed something weird that I have never seen before and also Google'd and found nothing.
If you go to: http://www.smartrainer.com.au you get the normal site
But, if you go to: https://www.smartrainer.com.au you get redirected to another website and are also given an SSL warning beforehand (in Chrome)
The site is hosted on a UNIX / PHP server and the .htaccess file currently has nothing that would suggest that it's redirecting to this other website.
Any help or insight would be appreciated with this, because I've never heard of this or seen this before.. The client also has no idea why it would be directing to that company that we've never heard of
Thanks!
It sounds like you're using a shared hosting server.
In plain HTTP, the server can know which host the client is requesting using the Host header in the request (this is based on the URL). Apache Httpd supports this with what it calls Name-based virtual hosts.
The HTTPS configuration is separate from the HTTP configuration in Apache Httpd (and presumably a number of other servers). Having virtual hosts (typically on a shared host) for the HTTP configuration doesn't mean that the same configuration is replicated for HTTPS.
HTTPS presents another problem: choosing which certificate to send before being able to see the Host header. Indeed, the server needs to send the client a certificate with the correct name during the SSL/TLS handshake, which happens before any HTTP traffic is sent (so before the Host header can be read). To overcome this problem, some hosts will set up a certificate valid for multiple host names (typically multiple Subject Alternative Names, or sometimes wilcards), others will use Server Name Indication (which isn't supported by all clients).
To get your server to host your site for HTTPS, you'd need:
To make sure the certificate it serves is valid for your host name (otherwise, there will be a warning message).
That the virtual hosts (or equivalent) it serves are configured for your host too.
In your case it seems that (a) your server is serving a single certificate that is not valid for your host and (b) your host isn't configured for HTTPS anyway, since you're falling back to what's probably the default host.
You may solve this issue by redirecting HTTPS URL to HTTP URL from your .htaccess. This error might because of shared hosting. If you cannot solve this issue from your .htaccess than you may also contact your hosting provider on this issue.

Resources