Erudika / scoold

A Stack Overflow clone for teams (self-hosted or hosted)
https://scoold.com
Apache License 2.0
865 stars 238 forks source link

Incorrect redirect_uri in token request from Keycloak #276

Closed vljubovic closed 3 years ago

vljubovic commented 3 years ago

We are trying to setup OAuth2 against our corporate KeyCloak server. Currently user can login with KeyCloak login page, is redirected to /oauth2_auth endpoint which then redirects to /signin?code=3&error=true. The only relevant message in para.log is:

2021-10-22 09:32:54,163 [INFO ] c.e.p.s.filters.GenericOAuth2Filter - OAuth 2.0 token request failed with response {error=invalid_grant, error_description=Incorrect redirect_uri}

We have experimented with different settings for "Allowed redirect uri" and finally set it to * but the problem persists. We have no more ideas why token endpoint would return "Incorrect redirect_uri' while auth endpoint works fine. Below is oauth2 configuration in application.conf (classified bits redacted with ===). Scoold is behind nginx per instructions for "self-hosted para backend" in readme, para is running on port 8081 since port 8080 is used by something else.

para.oa2_app_id = "scoold"
para.oa2_secret = "==="
para.security.oauth.authz_url = "https://===/auth/realms/===/protocol/openid-connect/auth"
para.security.oauth.token_url = "https://===/auth/realms/===/protocol/openid-connect/token"
para.security.oauth.profile_url = "https://===/auth/realms/===/protocol/openid-connect/userinfo"
para.security.oauth.scope = "openid%20email%20profile"

para.security.oauth.parameters.picture = ""

para.security.redirect_uri = "http://scoold.mydomain.com"

Here is full para.log

2021-10-22 09:19:07,246 [INFO ] com.erudika.para.Para - --- Para.initialize() [embedded] ---
2021-10-22 09:19:07,265 [INFO ] c.erudika.para.core.utils.CoreUtils - Loaded new DAO, Search and Cache implementations - H2DAO, LuceneSearch and CaffeineCache.
2021-10-22 09:19:12,079 [INFO ] com.erudika.para.utils.HealthUtils - Server is healthy.
2021-10-22 09:19:12,317 [INFO ] c.erudika.para.metrics.MetricsUtils - Found root app 'para' and 1 existing child app(s).
2021-10-22 09:19:13,512 [INFO ] com.erudika.para.ParaServer - Starting ParaServer using Java 11.0.11 on c9 with PID 29222 (/opt/para/para-jar-1.41.3.jar started by root in /opt/para)
2021-10-22 09:19:13,514 [INFO ] com.erudika.para.ParaServer - The following profiles are active: embedded
2021-10-22 09:19:16,548 [INFO ] com.erudika.para.ParaServer - Instance #1 initalized and listening on http://localhost:8081
2021-10-22 09:19:23,208 [INFO ] com.erudika.para.ParaServer - Started ParaServer in 10.864 seconds (JVM running for 19.88)
2021-10-22 09:32:54,163 [INFO ] c.e.p.s.filters.GenericOAuth2Filter - OAuth 2.0 token request failed with response {error=invalid_grant, error_description=Incorrect redirect_uri}
2021-10-22 09:32:54,163 [INFO ] c.e.p.s.filters.GenericOAuth2Filter - OAuth 2.0 token request failed with response {error=invalid_grant, error_description=Incorrect redirect_uri}
albogdano commented 3 years ago

So, I think you are telling Keycloak to redirect back to Scoold, when instead, it should be redirecting back to Para. Either remove para.security.redirect_uri completely, or change it to para.security.redirect_uri = "http://para.mydomain.com".

The URL which needs to be whitelisted in Keycloak is http://para.mydomain.com/oauth2_auth The redirects go like this for each authentication request:

Scoold Login page --> Keycloak --> Para (handle auth request) --> Scoold (logged in)
vljubovic commented 3 years ago

Hello Alex, Thanks for your response! We have configured nginx to forward requests to http://scoold.mydomain.com/oauth2_auth to para server, as suggested in Scoold readme. Here is excerpt from our nginx configuration:

  location /oauth2_auth {
    proxy_pass http://127.0.0.1:8081;
    proxy_redirect http:// $scheme://;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Host $http_host;
  }

Nonetheless, I have now followed your suggestion and exposed the whole para server on domain http://para.mydomain.com. I have verified that it works by setting para.endpoint = "http://para.mydomain.com" in scoold config and it works. And also set para.security.redirect_uri = "http://para.mydomain.com" in both scoold and para application.conf. The problem persists, we are still getting error OAuth 2.0 token request failed with response {error=invalid_grant, error_description=Incorrect redirect_uri}. I can see in my browser developer tools that the sequence of events is exactly as you sketched in your reply.

albogdano commented 3 years ago

Since you are only exposing the /oauth2_auth endpoint from Para, I think your Scoold settings should look like this:

para.endpoint = "http://para.mydomain.com"
para.security.redirect_uri = "http://scoold.mydomain.com"

As you can see, the public URL where you handle the authentication request is http://scoold.mydomain.com so the redirect_uri should be set to that URL.

And in Keycloak, allow the redirect URL http://scoold.mydomain.com/oauth2_auth

vljubovic commented 3 years ago

Hello, We tried that too. It's the same.

albogdano commented 3 years ago

Well, definitely make sure that the URL that you get redirected to after visiting Keycloak matches that which is configured in Keycloak as a valid redirect.

albogdano commented 3 years ago

Also make sure you are only using the Authorization code flow of OAuth2

nthibaut33 commented 1 year ago

hello, I have the same problem and found a solution.

When Keycloak call para via the redirect_uri it works fine. But para is behind an ingress in kubernetes, and there is a port redirection.

Keycloak call para.mydomain.com/oauth2_auth but the port redirection add the port to the adress : para.mydomain.com:8080/oauth2_auth

The code in the file SecurityUtils.java in method getRedirectUrl reconstruct the redirect uri with a request.getRequestUrl() wich keep the port. The redirect url is incorrect with a port redirection.

To resolve this I get the para.endpoint with para.getConfigValue, if not emty this Url is used. There is the same issue in the method isValidSignature wich cause the port to be added to the path used to checl the signature. It fails because scoold construct is para token api with para.endpoint value :)

can I post this as feature request or propose a pull request ?

albogdano commented 1 year ago

@nthibaut33 have you tried setting scoold.security.redirect_uri to the correct redirect URL? Details here: https://github.com/Erudika/scoold#docker

albogdano commented 1 year ago

Related issues: #199 and #296