SSL / Proxy Issue using Spring Cloud OAuth2 - nginx

I adapted the following OAauth2 Spring Cloud samples:
Authserver / SSO
The only change I made, was using JPA on the Authserver side to check the credentials from a database. Everything works well, except deploying it behind an nginx proxy. As used in the sample apps above, Spring Boot and embedded Tomcat is used. I also properly configured proxy headers:
server.tomcat.protocol-header=X-Forwarded-Proto
server.tomcat.remote-ip-header=X-Real-IP
Proxying HTTP is working:
accessTokenUri: http://uaa.sample.com/oauth/token
userAuthorizationUri: http://uaa.sample.com/oauth/authorize
So far so good, but I need to use SSL (obviously):
accessTokenUri: https://uaa.sample.com/oauth/token
userAuthorizationUri: https://uaa.sample.com/oauth/authorize
If I switch to SSL, I get a 401 from my client application after the auth server is redirecting back from authorize. I captured the HTTP traffic and everything seems to work:
GET request to client application
Client app redirects to /login
/login redirects to https://uaa.sample.com/oauth/authorize?client_id=reprisk&redirect_uri=http://test.sample.com/login&response_type=code&state=9prwi2
Auth server redirects to https://uaa.sample.com/login
After login, authorize is called again and the server finally redirects to http://test.sample.com/login?code=212eRK&state=9prwi2
The HTTP traffic for HTTP and HTTPS is exactly the same, except that for HTTP a proper referer is set for the last request (AFAIK, the referer isn't checked during OAuth authentication, right?):
HTTP:
GET /login?code=212eRK&state=9prwi2 HTTP/1.1
Host: test.sample.com
...
Referer: http://uaa.sample.com/login
Cookie: JSESSIONID=401EB8D1D1F4297160D518EC253A0CB5; XSRF-TOKEN=95a00a0d-3362-4e9b-b7eb-45addf2d10b4
...
---
HTTP/1.1 302 Found
HTTPS:
GET /login?code=212eRK&state=9prwi2 HTTP/1.1
Host: test.sample.com
...
Cookie: JSESSIONID=401EB8D1D1F4297160D518EC253A0CB5; XSRF-TOKEN=95a00a0d-3362-4e9b-b7eb-45addf2d10b4
...
---
HTTP/1.1 401 Unauthorized
Corresponding log message from client application:
Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Could not obtain access token.
Any ideas why using a proxy and SSL isn't working? I'm happy to share more code and/or log output!
Thanks!!!

It looks to be failing where the SSO app tries to swap the auth code for a token.
All the steps prior to this were browser redirects, this is code on the SSO server trying to call the auth server.
What are you using for SSL certificates on the auth server? Are they signed by a trusted party with a CA in the Java trust store?
If not, that is probably why it's failing as the BadCredentialsException is the end result of the underlying HTTP request failing.
The other option is that there is no route directly from the SSO server to the Auth server address.
I believe it's ultimately the Apache Commons HttpClient code that will be handling the request, so you should try upping the debug for those classes (org.apache.http) and see what it reported.

It may be a little late but I ran into the exact same thing.
My Setup is a NGINX doing SSL proxying through to a running Spring Boot Application using Spring oAuth2.
To solve this in nginx config
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
And this in your spring application.yml
server.tomcat.remote_ip_header: X-Forwarded-For
server.tomcat.protocol_header: X-Forwarded-Proto
security.require_ssl: true
Source:
http://docs.spring.io/spring-boot/docs/current/reference/html/howto-security.html#howto-enable-https
And now Spring detects the right URL and also request.getRequestURL returns the right URL now including https://
#Controller
public class HomeController {
#RequestMapping("/")
#ResponseBody
public String rootLandingPage(HttpServletRequest request) throws Exception {
return "url: " + request.getRequestURL();
}
}

It may be worth taking a closer look at why the BadCredentialsException is bubbling up, and by this I mean stepping through the Spring Security OAuth2 code with your debugger.
The reason why I say this is because in my experience the BadCredentialsException may be due to an underlying InvalidRequestException with the following being the offending line:
throw new InvalidRequestException(
"Possible CSRF detected - state parameter was required but no state could be found");
I have raised a separate question related to the above here:
Why is AccessTokenRequest's PreservedState perpetually null with a resultant CSRF related InvalidRequestException?
So, in terms of your situation, with the newly introduced nginx proxy, I'm just wondering whether you might not be seeing a misleading exception. That is, misleading to the untrained in terms of oauth2 and spring security oauth 2 with CSRF as an additional complexity to deal with.

Related

Workfront API Auth request (Machine to Machine Application) is returning 404 error

I'm making an auth POST request to the https://example.my.workfront.com/integrations/oauth2/api/v1/jwt/exchange (note: actual request has 'example.my' subdomain replaced with the real Workfront subdomain for my org) endpoint with the required client_id, client_secret, and jwt_token values but receiving the following 404 response and error message in the Response:
{"statusCode":404,"error":true,"type":"Not Found","message":"Invalid Host"}
I searched the API docs and Workfront forum for any additional information on what could cause the error but had no luck. Does anyone know what is causing this error?
For context, this request seems to work fine from Postman on my local machine (i.e. auth token is returned) but fails on the server that is being used for this machine to machine application.
The issue was that the request to the Workfront API was including the port in the host Header value, i.e. example.my.workfront.com:443.
Excluding the port so the host Header value is in the format example.my.workfront.com resolved the issue and provided the expected 200 response with auth token.

KeyCloak ISS claim URL Authority incorrect in bearer token

I have setup an NGINX Reverse Proxy; using Keycloak 18 to validate users. Moving onto the customer network, I had to change the port mapping of the NGINX-RP to 9443 from 443. Keycloak provides a bearer token upon login. The bearer token will have the iss claim url: xx.xx.xx.xx:9443. This creates an authentication error, because the the expected value is xx.xx.xx.xx. Is there any way for me to change the url authority to contain the new port number?
In the nginx you could use the X-Forwarded headers to tweak the request and update the host and port for downstream calls.
But, you could also handle all your authentication related api calls under the same port (server{} block with location{} sub blocks) and that way you don't have to make this change plus you can also preserve the integrity of the request.

NGinx sends 307 instead of 401

I've used several revers proxies over time, but NGinx blew me away with its behavior. I recently had to use NGinx after years of using HAProxy (as K8s Ingress) and I'm stuck with no solution in sight.
Right from the beginning, the behaviour was different when the session was lost. An HTTP 401 would tell the client (single page JavaScript application) to inform the user that the session was lost and he/she has to login again. Instead of sending 401 to the browser, it sends a 307 to the login page, with the wrong verb (POST) since the request that fails with 401 was a POST request.
The best way to troubleshoot would be in isolation so I installed the version from the repository (Linux Mint 20) and registered a simple reverse proxy entry in /etc/nginx/conf.d
server {
listen 80;
location / {
proxy_pass http://localhost:8080;
}
}
Unfortunately (or fortunately) the issue manifested itself right away: as soon as I removed the cookies and the server sends 401 "Please log in", the browser shows a 307 on the request that failed with 401.
Expected behavior:
Browser sends "POST /ping"
Browser receives "401 on /ping"
Current behavior:
Browser sends "POST /ping"
Browser receives "307 /auth" and then executes /auth with POST (not sure how it knows /auth is the login page)
Any idea how to disable this behavior?

nginx unknown directive "auth_jwt"

I want set jwt auth on my nginx but I got this error:
nginx: [emerg] unknown directive "auth_jwt" in /usr/local/etc/nginx/nginx.conf
I have nginx version: nginx/1.15.8
OS: macOs Mojave v10.14.2
I couldn't find the answer on Google or other websites.
/nginx.cofnig:
location / {
auth_jwt "closed site" token=$cookie_token;
auth_jwt_key_file conf/api_secret.jwk;
try_files $uri /index.html =404;
}
The original JWT module is part of Nginx Plus, but then there are many opensource free modules that do similar thing:
https://github.com/tizpuppi/ngx_http_auth_jwt_module - supposed to be a drop-in replacement for ngx_http_auth_jwt_module from Nginx Plus (but currently became a bit outdated and lacks some features that the commercial module has), supports HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384 and ES512, can get token from Authorization header or from an Nginx variable, will respond with WWW-Authenticate header if JWT validation failed, depends on Jansson & libjwt
https://github.com/TeslaGov/ngx-http-auth-jwt-module - supports HS256 and RS256, can get token from Authorization header or from a cookie with configurable name, can be configured to do 302 redirect to a login page if JWT validation failed, exposes sub and emailAddress claims in X-UserId and X-Email headers respectively, easy to build a Docker image (based on centos 7), depends on Jansson & libjwt
https://github.com/max-lt/nginx-jwt-module - small Docker image (~16Mb) based on nginx-alpine, supports HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384 and ES512, can get token from Authorization header or from an Nginx variable, depends on Jansson & libjwt
https://github.com/ch1bo/nginx-jwt - supports HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384 and ES512, not only verifies the signature but can also issue signed JWT tokens, can get token from Authorization header with a fallback to cookie with same name (name of the cookie is not configurable), depends on Jansson & libjwt, doesn't have documentation
https://github.com/maxam18/nginx-ejwt-module - supports HS256 and RS256, can get token from Authorization header or from an Nginx variable, can extract and compare payload values with predefined values, will respond with WWW-Authenticate header if JWT validation failed, no external dependencies
https://github.com/andrei-markeev/ngx_http_jwted_module - supports the modern Ed25519, can get token from Authorization header or from an Nginx variable, exposes JWT claims json in $jwt_claims variable, no external dependencies
Despite a rather wide variety of open source modules, the commercial one from Nginx Plus has more features than any one of these modules, and is most likely much better battle-tested and also well maintained.
Disclaimer: I'm the author of the last module in this list.

Requests sequence in Nginx

I have a scenario when server needs to do authorization request before an actual request. so, one request are served by 2 different services.
Nginx's location has to be handled by Auth-Service, and if the response status is 200 OK, then the request should be forwarded to the Feature-Service. Otherwise, if the response status 401 then this status should be replied to front-end.
upstream auth_service {
server localhost:8180;
}
upstream feature_service {
server localhost:8080;
}
location /authAndDo {
# suggest here
}
Code snippet in nginscript will be also OK.
Specifically for this purpose, http://nginx.org/r/auth_request exists through http://nginx.org/docs/http/ngx_http_auth_request_module.html (not built by default).
It lets you put authentication, through a subrequest, into any location you want, effectively separating the authentication from the actual resource.
In general, such not possible from web server. 401 is a response at front end plus gives HTTP WWW-Authenticate response header. Develop web application according to need or edit 401 file. HTTP 401 has RFC specification. Users, browsers should understand the message. Nginx doc described how 401 will be handled.
Nginx community edition's auth_request will only process if the subrequest returns HTTP 200, else for 401 will not redirect more than to 401 by default, other headers will not be process the response to protect the application & the users. Nginx community edition not even support all features of HTTP/2. It can go worser.
Apache2 web server has full HTTP/2 support and custom 401 location in auth module and works only on few browsers. Few browsers allow Apache2 to do that perfectly. Others show fail to load page. On Stack Exchange networks's various subdomains peoples asked before for Apache2 to make it working for all the browsers.
Hardly you can redirect on Nginx :
error_page 401 /401.html;
location ~ (401.html)$ {
alias /usr/share/nginx/html/$1;
}
Another way may be using reverse proxy with another server like peoples talking here on Github. I can not give warranty of failure of loading page.

Resources