zmartzone / lua-resty-openidc

OpenID Connect Relying Party and OAuth 2.0 Resource Server implementation in Lua for NGINX / OpenResty
Apache License 2.0
967 stars 247 forks source link

Authentication with Keycloak in Lua makes redirection loops #450

Open ThihaM2G opened 2 years ago

ThihaM2G commented 2 years ago

Hi,

First, I would like to explain my current setup. Nginx is stalled in both Server-1 and Server-2. Server-1 is the main reverse proxy. Server-2 is where the Lua script (authentication with Keycloak) exists. So we can say Server-1 is sitting in front of Server-2. When we access server-1.com/app/download/..., the request is passed to server-2.com/app/download/.... This is where the Lua script starts working. The idea is to authenticate the user with Keycloak. If authentication succeeds, Server-2 will authenticate with DigitalOcean Spaces to download the file. That's not too complex, right?

Expected behaviour
Actual behaviour
Environment

Before I provide my Nginx config file, please let me ask a few quick questions. Does lua-resty-openidc authentication with Keycloak work well for the above setup? From the comment, @bodewig mentioned having another reverse proxy sitting in front of the Nginx running the authentication proxy code can be the problem. It seems similar to my case. Please let me know your opinions. Thank you!

Here is the snippet of my Nginx config file located in Server-2
location ~* /app/download/(.*) {
  set $uri_full '$1';
  access_by_lua_block {
    local opts = {
      redirect_uri_path = "/app/download/" .. ngx.var.uri_full,
      -- I think complete URL becomes: server-1.com/app/download/test.pdf, file name is dynamic!
      discovery = "https://server-1.com/auth/realms/test/.well-known/openid-configuration",
      client_id = "...",
      client_secret = "...",
      redirect_uri_scheme = "https",
      session_contents = {id_token=true, user=true},
      ...
    }
    local res, err = require("resty.openidc").authenticate(opts)
  }
}
About redirection loop
KevinKeo commented 2 years ago

I exactly have the same problem with nearly the same setup. One nginx reverse proxy who serves keycloak and my other nginx who acts as a web server whose image is baased on openresty and have the lua code...

ThihaM2G commented 2 years ago

I exactly have the same problem with nearly the same setup. One nginx reverse proxy who serves keycloak and my other nginx who acts as a web server whose image is baased on openresty and have the lua code...

Oh, did you manage to solve it? I'm about to give up on this problem. I wanted Nginx to handle this authentication process. Now, I'm looking for an alternative solution.

KevinKeo commented 2 years ago

No idea To me it looks like the nginx webserver who does that authentification, doesn't authenticate so it loops and redo the auth to keycloak (keycloak behind the nginx reverse proxy).

But I don't know that much about oidc and oauth so I am kind of lost.

ThihaM2G commented 2 years ago

No idea To me it looks like the nginx webserver who does that authentification, doesn't authenticate so it loops and redo the auth to keycloak (keycloak behind the nginx reverse proxy).

But I don't know that much about oidc and oauth so I am kind of lost.

In my opinion, authentication works. That's why redirection occurs based on the redirect_uri_path parameter from the Lua script. redirect_uri_path is for redirection only after authentication succeeds. The problem seems related to our setup. If we have only one Nginx directly serving for both Lua script and Keycloak, we may not have that redirection loop. I don't have permission to modify the main Nginx. It's worth trying if you can. :)

bodewig commented 2 years ago

I have used a setup with two or even more proxies without problems in the past. It is possible. :-)

OpenID Connect uses a redirect based flow where - in your case - Keycloak redirects the user after successful login to a very specific URI in your application where code must be running that knows how to complete the OpenID Connect flow. redirect_uri is this very specific URI and it must be handled by lua-resty-openidc and can not be handeled by anything else. Once the flow has completed, lua-resty-openidc redirects to where the first request before the flow started wanted to go.

It looks as if your setup gets as far as the redirect from keycloak coming in to server2, so this part of the setu seems to be correct. Your redirect_uri should be something that does not correspond to any real URI you want to access, but to a URI that is only served by lua-resty-openidc. Something like /app/download/redirect_uri for example.

If you have set up things this way and it still fails, then you should have a look look at lua-resty-openidc's logs for hints (or post the logs here if you cannot see what's going wrong easily).

ThihaM2G commented 2 years ago

I have used a setup with two or even more proxies without problems in the past. It is possible. :-)

Thanks for your confirmation. Appreciate it. :)

OpenID Connect uses a redirect based flow where - in your case - Keycloak redirects the user after successful login to a very specific URI in your application where code must be running that knows how to complete the OpenID Connect flow. redirect_uri is this very specific URI and it must be handled by lua-resty-openidc and can not be handeled by anything else. Once the flow has completed, lua-resty-openidc redirects to where the first request before the flow started wanted to go.

It looks as if your setup gets as far as the redirect from keycloak coming in to server2, so this part of the setu seems to be correct. Your redirect_uri should be something that does not correspond to any real URI you want to access, but to a URI that is only served by lua-resty-openidc. Something like /app/download/redirect_uri for example.

If you have set up things this way and it still fails, then you should have a look look at lua-resty-openidc's logs for hints (or post the logs here if you cannot see what's going wrong easily).

So you mean redirect_uri_path shouldn't represent any actual URI? In my Lua script, it is pointing to an actual URI (depending on the requested file). Otherwise, I got 404. Am I missing something? (I edited my Nginx config file to include the location block.) Thanks for your help.

bodewig commented 2 years ago

redirect_uri must point to an URI inside a location with a Lua access block that invokes lua-resty-openidc. So it would be a constant URI that is inside of /app/download/ - lua-resty-openidc itself provides the implementation of it, so it is a real URI :-)

KevinKeo commented 2 years ago

redirect_uri must point to an URI inside a location with a Lua access block that invokes lua-resty-openidc. So it would be a constant URI that is inside of /app/download/ - lua-resty-openidc itself provides the implementation of it, so it is a real URI :-)

I do redirect to an not existing link.

My keycloak is behind the path /auth My app is behind /app/name-app

And the redirect uri is /app/name-app/redirect-uri

Sadly, I still get an infinite loop.

Do you know how to show or read the openidc log ?

When I removed the lua code from the nginx webserver (app) and put it in the reverse proxy, it was working correclty. But having the lua code behind a reverse proxy is still not working.

raythree commented 2 years ago

We also have a reverse proxy in front of our app, and the app has an nginx sidecar with lua-resty-openidc. The issue we have is that the redirect_uri needs to be:

https://host/app-name/callback 

The front reverse proxy uses the app-name to route, but the app is unaware of the "app-name" prefix, and only has URLs like /callback, /path1, /path2, etc.

What I want to do is tell lua-rest-openidc that the redirect_uri (that it passes to the authentication server) is https://host/app-name/callback, but that it should recognize the authorization callback when it gets a request on path /callback. It's like I want to provide it with:

opts.redirect_uri => The full redirection URI to give to the auth server opts.redirect_uri_path => The path to recognize as a authorization callback

It seems that if I put the "opts.redirect_uri_path" in FRONT of the "and" statement that this would work:

https://github.com/zmartzone/lua-resty-openidc/blob/master/lib/resty/openidc.lua#L1437

Is there another way to have an external redirect_uri, but to tell lua-resty-openidc to use a specific path as the redirection / callback path?

bodewig commented 2 years ago

@raythree I think you are correct and lua-resty-openidc needs a way to properly deal proxies that rewrite URIs - but from this issues's description I don't believe this is the case here. In either case this would be worth a separate issue.

If you happen to open a new one it would be good if you could check whether your reverse proxy sets X-Forwarded-Prefix which is yet another non-standard header that would contain /app-name in your case. This might help in the common case of prefix based routing but a more general solution (like allowing users to set both redirect_uri and a local path separately) is probably needed to deal with more complex rewrite rules.

KevinKeo commented 2 years ago

We also have a reverse proxy in front of our app, and the app has an nginx sidecar with lua-resty-openidc. The issue we have is that the redirect_uri needs to be:

https://host/app-name/callback 

The front reverse proxy uses the app-name to route, but the app is unaware of the "app-name" prefix, and only has URLs like /callback, /path1, /path2, etc.

What I want to do is tell lua-rest-openidc that the redirect_uri (that it passes to the authentication server) is https://host/app-name/callback, but that it should recognize the authorization callback when it gets a request on path /callback. It's like I want to provide it with:

opts.redirect_uri => The full redirection URI to give to the auth server opts.redirect_uri_path => The path to recognize as a authorization callback

It seems that if I put the "opts.redirect_uri_path" in FRONT of the "and" statement that this would work:

https://github.com/zmartzone/lua-resty-openidc/blob/master/lib/resty/openidc.lua#L1437

Is there another way to have an external redirect_uri, but to tell lua-resty-openidc to use a specific path as the redirection / callback path?

Indeed the problem seems to come from that. In my nginx web server, I put all the block in the same path as the reverse proxy use. And now I am using an alias to serve my static file under the request_uri path used in the reverse proxy and it seems to work.

bodewig commented 2 years ago

Indeed the problem seems to come from that.

So we should be able to cover that when #453 is addressed. I doubt it is the same case for @ThihaM2G, though.

ThihaM2G commented 2 years ago

So we should be able to cover that when #453 is addressed. I doubt it is the same case for @ThihaM2G, though.

Yes, it's the redirection loop for my case. I couldn't be able to solve it, unfortunately. I had to implement the same logic in my Java backend project. Thanks for the help. :)