Reposoft / openidc-keycloak-test

zmartzone/mod_auth_openidc and keycloak combined - self hosted cloud auth
19 stars 6 forks source link

Support session revalidate without page reload (using iframes or AJAX) #12

Closed solsson closed 6 years ago

solsson commented 7 years ago

Both in production and in this repository we get 404 on both iframe srces when trying https://github.com/pingidentity/mod_auth_openidc/wiki/Session-Management.

Both as authenticated (in browser) and unauthenticated the url http://openidc/protected/redirect_uri?session=iframe_rp gets 404.

Apache logs say:

openidc_1         | [Wed Dec 14 07:00:51.250882 2016] [authz_core:debug] [pid 55:tid 140675462911744] mod_authz_core.c(809): [client 172.27.0.1:59654] AH01626: authorization result of Require valid-user : denied (no authenticated user yet)
openidc_1         | [Wed Dec 14 07:00:51.250922 2016] [authz_core:debug] [pid 55:tid 140675462911744] mod_authz_core.c(809): [client 172.27.0.1:59654] AH01626: authorization result of <RequireAny>: denied (no authenticated user yet)
openidc_1         | [Wed Dec 14 07:00:51.250934 2016] [auth_openidc:debug] [pid 55:tid 140675462911744] src/mod_auth_openidc.c(2981): [client 172.27.0.1:59654] oidc_check_user_id: incoming request: "/protected/redirect_uri?session=iframe_rp", ap_is_initial_req(r)=1
openidc_1         | [Wed Dec 14 07:00:51.250946 2016] [auth_openidc:debug] [pid 55:tid 140675462911744] src/util.c(853): [client 172.27.0.1:59654] oidc_util_get_cookie: returning "mod_auth_openidc_session" = <null>
openidc_1         | [Wed Dec 14 07:00:51.250981 2016] [auth_openidc:debug] [pid 55:tid 140675462911744] src/util.c(956): [client 172.27.0.1:59654] oidc_util_request_matches_url: comparing "/protected/redirect_uri"=="/protected/redirect_uri"
openidc_1         | [Wed Dec 14 07:00:51.251025 2016] [auth_openidc:debug] [pid 55:tid 140675462911744] src/mod_auth_openidc.c(2625): [client 172.27.0.1:59654] oidc_handle_session_management: iframe_rp command issued but no client ((null)) and/or no check_session_iframe ((null)) set
openidc_1         | 172.27.0.1 - - [14/Dec/2016:07:00:51 +0000] "HEAD /protected/redirect_uri?session=iframe_rp HTTP/1.1" 404 - "-" "curl/7.43.0"
zandbelt commented 7 years ago

This call will only succeed when authenticated in-browser.

Seems that the configuration of your provider doesn't have the check_session_iframe endpoint set. see https://github.com/pingidentity/mod_auth_openidc/blob/master/auth_openidc.conf#L98

solsson commented 7 years ago

@zandbelt Thank you.

Results were the same in browser and curl, but I should have pasted the log from the browser call instead.

I did check the endpoint

$ curl http://keycloak:8080/auth/realms/Testrealm/.well-known/openid-configuration | json_pp | grep check_session_iframe
   "check_session_iframe" : "http://keycloak:8080/auth/realms/Testrealm/protocol/openid-connect/login-status-iframe.html",

And that URL responds with a HTML containing javascript.

zandbelt commented 7 years ago

Can you paste a full debug log from startup through authentication to the RP iframe request?

solsson commented 7 years ago

Attaching log for a new apache start with a fresh browser. First unauthenticated start page, then auth, then I pasted the above iframe_rp URL in the address bar.

openidc-auth-iframe-rp.log.txt

endpoint-keycloak-240.json.txt

Apache conf is https://github.com/Reposoft/openidc-keycloak-test/blob/ajax-refresh/build-contracts/openidc/000-default.conf

zandbelt commented 7 years ago

The key is in:

openidc_1 | [Wed Dec 14 08:26:17.910215 2016] [auth_openidc:debug] [pid 11:tid 139850904758016] src/mod_auth_openidc.c(1554): [client 172.27.0.1:43872] oidc_save_in_session: session management disabled: session_state ((null)) and/or check_session_iframe (http://keycloak:8080/auth/realms/Testrealm/protocol/openid-connect/login-status-iframe.html) is not provided, referer: <>

Upon return from the Provider, the authorization response should have included a session_state parameter, see: http://openid.net/specs/openid-connect-session-1_0.html#CreatingUpdatingSessions, but that parameter is not there:

openidc_1 | [Wed Dec 14 08:26:17.846840 2016] [auth_openidc:debug] [pid 11:tid 139850904758016] src/mod_auth_openidc.c(2981): [client 172.27.0.1:43872] oidc_check_user_id: incoming request: "/protected/redirect_uri?state=wXLFssJv2Q5kBFaHOoA28infyxI&code=t3j_m_fA_WYX8CEGbySCnFgo0gVCLCAAfa6Ovw9wpk4.640680f4-c678-483c-896b-33235badc84d", ap_is_initial_req(r)=1, referer: <>

I guess you need to configure that in Keycloak perhaps for this specific Client.

solsson commented 7 years ago

Thank you again. We'll try to troubleshoot this before the end of this week.

solsson commented 7 years ago

Is it possible that https://github.com/pingidentity/mod_auth_openidc/issues/204 was a regression in 2.0? The reason why we wanted to try session management iframes now is that we're having issues with users having to reload their page after computers have been in sleep mode. In an older production environment that used 1.8.x we probably didn't have those.

zandbelt commented 7 years ago

Sort of, yes; there were a lot of changes around session management and this behaviour changed indeed though I assumed that was not a problem anywhere but a security improvement. As it turns out now that change seems to come with its own unforeseen problems.

solsson commented 7 years ago

Possibly related, we don't get a redirect back from keycloak to apache if an AJAX request with jQuery crossOrigin: true hits when auth needs to be revalidated.

First a request with X-Requested-With gets a 401 as expected (query string is there only for logs):

GET /protected/?crossDomain=false&t=1481975110940 HTTP/1.1
Host: openidc
Connection: keep-alive
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36
Referer: http://openidc/protected/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: sv-SE,sv;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: mod_auth_openidc_state_H2y2QOW69q06xVoqDmldpJV9ON4=eyJhbGciOiAiZGlyIiwgImVuYyI6ICJBMjU2R0NNIn0..UPY8Fx1YxbvvPSSV.Kjtpqfh6dwCq-DpQ6oBp9zbFiExg2FjsWcjHNHF2MYLPt3Rnyupyi1BXeiZpJvur8W7wKxVGUGHT5tPga0jwmbBi5N4KOWBfHfy-ORsOBtLHR34esImmToHo8Fkx1b87WgUYLOftpRShcDVOcuR4alGGTjVL1EWV252YF656lP4gwUdZVaKpfYYc84d2T7LM9R4q8j_9iaofSEoSrQuuHV_9tG6g82g_xzSKSY92Cmt6envMfuhLJt5vXTBbXrEfGF85oSRI7WOZUazBk_eqNfqKlIWqbqXudaabsEgedSfjcFyNZzKiz5Du8ridt4j7TCnk_11iVkyI2K1RpHyuXkg1dhPEscumEdv1LeCSmYVT5j0l6G8hfJGphcAki5_AXfas-V44if32bO3VO2TppPMzpwMrbnwJ2Xd92-9jB9E_EDJZa0emSdb6wdstGvjtQfpntSVEnQW-0ER2247TR5_qt8v5lEVUzbXOeCBIIs58sefqP-MwrqtyHTG2JzAPEq-MVtlZHmGfqssjCejHEgt8qpoO8iM.OSKIw1eBdzt_knDx_UdtpQ; mod_auth_openidc_state_q5bJCBlkhh9k1QXGxHlJWPvmuoo=eyJhbGciOiAiZGlyIiwgImVuYyI6ICJBMjU2R0NNIn0..bGc_6JYkxo9yuapF.Nli2d7F4yTJzT93_i7JP2cda2D8rzmO58V9_tdBS-OM7244yXQF6AuvhptKxKZe7V7lIZym09fHnePu3wUv_MUEWmuEuQVTUVWM9tqyOcuEM4809IGIYof7EUxxybnfaf3K13T6LSJ5M_lXvZg6t3SdB_SjWPrqsaOv-gdZsrPePcJ23eNeN5ECDolHUGf_2pt3pd48ROXenkF7b6lprJtyyDeGa1Iv_Mz68RZzqlMUH8_3Tf2jsQIlcdjDeMjL36d8D40Yo87cNXlzqLCi2CMm_QhRxDuehkdvVMafWGVFP_kwYrFYYSoxC1zh5INAsQmXOPfO1CDVnlJwBhP-KvEKEYQkRfZP6jQpZoWgH5n1wV1LpoZf4D-KBLBVdDk_n0pu5KfNzsjq7nV0KV0Y77TFy23ceBbJWpkJIRjUlk9rCFofECDy4odSyfSiotMBwwO_dvCCfmjbWZtc28ytXLSk6pVBh_fLyRfAMJ518QRPHRzYJCKXjp16YsxpyGis-9in9rf_Jn_5NbWIiMtUg7ojpsrIuD2Y.k1zxNBuFLhCbwisVltklkw

Then client sends the crossDomain request (which we hoped would re-validate the session):

GET /protected/?crossDomain=true&t=1481975128680 HTTP/1.1
Host: openidc
Connection: keep-alive
Accept: */*
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36
Referer: http://openidc/protected/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: sv-SE,sv;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: mod_auth_openidc_state_H2y2QOW69q06xVoqDmldpJV9ON4=eyJhbGciOiAiZGlyIiwgImVuYyI6ICJBMjU2R0NNIn0..UPY8Fx1YxbvvPSSV.Kjtpqfh6dwCq-DpQ6oBp9zbFiExg2FjsWcjHNHF2MYLPt3Rnyupyi1BXeiZpJvur8W7wKxVGUGHT5tPga0jwmbBi5N4KOWBfHfy-ORsOBtLHR34esImmToHo8Fkx1b87WgUYLOftpRShcDVOcuR4alGGTjVL1EWV252YF656lP4gwUdZVaKpfYYc84d2T7LM9R4q8j_9iaofSEoSrQuuHV_9tG6g82g_xzSKSY92Cmt6envMfuhLJt5vXTBbXrEfGF85oSRI7WOZUazBk_eqNfqKlIWqbqXudaabsEgedSfjcFyNZzKiz5Du8ridt4j7TCnk_11iVkyI2K1RpHyuXkg1dhPEscumEdv1LeCSmYVT5j0l6G8hfJGphcAki5_AXfas-V44if32bO3VO2TppPMzpwMrbnwJ2Xd92-9jB9E_EDJZa0emSdb6wdstGvjtQfpntSVEnQW-0ER2247TR5_qt8v5lEVUzbXOeCBIIs58sefqP-MwrqtyHTG2JzAPEq-MVtlZHmGfqssjCejHEgt8qpoO8iM.OSKIw1eBdzt_knDx_UdtpQ; mod_auth_openidc_state_q5bJCBlkhh9k1QXGxHlJWPvmuoo=eyJhbGciOiAiZGlyIiwgImVuYyI6ICJBMjU2R0NNIn0..bGc_6JYkxo9yuapF.Nli2d7F4yTJzT93_i7JP2cda2D8rzmO58V9_tdBS-OM7244yXQF6AuvhptKxKZe7V7lIZym09fHnePu3wUv_MUEWmuEuQVTUVWM9tqyOcuEM4809IGIYof7EUxxybnfaf3K13T6LSJ5M_lXvZg6t3SdB_SjWPrqsaOv-gdZsrPePcJ23eNeN5ECDolHUGf_2pt3pd48ROXenkF7b6lprJtyyDeGa1Iv_Mz68RZzqlMUH8_3Tf2jsQIlcdjDeMjL36d8D40Yo87cNXlzqLCi2CMm_QhRxDuehkdvVMafWGVFP_kwYrFYYSoxC1zh5INAsQmXOPfO1CDVnlJwBhP-KvEKEYQkRfZP6jQpZoWgH5n1wV1LpoZf4D-KBLBVdDk_n0pu5KfNzsjq7nV0KV0Y77TFy23ceBbJWpkJIRjUlk9rCFofECDy4odSyfSiotMBwwO_dvCCfmjbWZtc28ytXLSk6pVBh_fLyRfAMJ518QRPHRzYJCKXjp16YsxpyGis-9in9rf_Jn_5NbWIiMtUg7ojpsrIuD2Y.k1zxNBuFLhCbwisVltklkw

Openidc responds:

HTTP/1.1 302 Found
Date: Sat, 17 Dec 2016 11:42:18 GMT
Server: Apache/2.4.23 (Unix)
Set-Cookie: mod_auth_openidc_state_mG4U2tvs9uXhzNTOz4HuiSaXYIw=eyJhbGciOiAiZGlyIiwgImVuYyI6ICJBMjU2R0NNIn0..RZi8d8JkqPlnsxeU.vl41lRGjYWVpgRersDqcRolLLWXVKNBi-wjh-H6wlmrB6oOQNyCXSAPcnM-UAWrh5RrcewpijOBcJlxFDCi-Upr1tfi8RkdR5hEojejvug4BjH_5sFBHG3dfibFOmHHjV8b2sPFjo83xGm54OzWsHO_cVjxdyynV-cQcYbbUVTzrpyhsiKuoV5eoluSI5V8L9WCyZhu_9BJIfDIuu72NzKb3M46AiLW29FEcjoY1sOERY4vAeMXNGwqPb4mWcH_fx7xli5cV4BXoXQeM51JYaw8PJzihOiRWGrfRlixaaYox8HEjuRx8qJCre53TsDVZ9vEGPkIh4LE0DawhpVHO33HFXKvjHW3-vsL7WtbQ0UQ14nbdNRT9BqyKDsaa6GTZV15JNX8TIUSb6y3a_Hi9ldbk6NNS9KWwL2gEBtkiYYvPOeJQVfK0XhB6LTA0VZdvdvWgwHIfXmkxUQUMmqZ1O8j2DXU_DdKlddDNNI7onXkEKyFSmiaFC5cztSMN8IUY8Ll6frRWPIhS27Gw8yLyjbNspSt5kF0.ephrZ-JJDa-2q235PaDaAw;Path=/;HttpOnly
Location: http://keycloak:8080/auth/realms/Testrealm/protocol/openid-connect/auth?response_type=code&scope=openid%20email&client_id=testclient&state=mG4U2tvs9uXhzNTOz4HuiSaXYIw&redirect_uri=http%3A%2F%2Fopenidc%2Fprotected%2Fredirect_uri&nonce=nDCOOJug6X24dO7gwBdEr_AVeiFhHbS_cvE-D8X3SBw
Content-Length: 481
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=iso-8859-1

Client redirects accordingly:

GET /auth/realms/Testrealm/protocol/openid-connect/auth?response_type=code&scope=openid%20email&client_id=testclient&state=mG4U2tvs9uXhzNTOz4HuiSaXYIw&redirect_uri=http%3A%2F%2Fopenidc%2Fprotected%2Fredirect_uri&nonce=nDCOOJug6X24dO7gwBdEr_AVeiFhHbS_cvE-D8X3SBw HTTP/1.1
Host: keycloak:8080
Connection: keep-alive
Accept: */*
Origin: http://openidc
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36
Referer: http://openidc/protected/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: sv-SE,sv;q=0.8,en-US;q=0.6,en;q=0.4

Keycloak sends a 200, so openidc gets no chance to handle the result:

Update: The response here is simply the login page. That's odd because client has a session. If I visit the current URL in a separate tab the login gets re-validated automatically.

HTTP/1.1 200 OK
Cache-Control: no-store, must-revalidate, max-age=0
X-Powered-By: Undertow/1
Set-Cookie: KC_RESTART=eyJhbGciOiJSUzI1NiIsImtpZCIgOiAiSG4wOUU0ZGpHYkc4Z2hNenZVVnpZVzE4dkFFUXVyZXczT2hWSnNsQVExbyJ9.eyJjcyI6ImMyMzdmZjc3LTExM2QtNDhmMC1iZjZiLWUzYWZmOWM2Nzc5MyIsImNpZCI6InRlc3RjbGllbnQiLCJwdHkiOiJvcGVuaWQtY29ubmVjdCIsInJ1cmkiOiJodHRwOi8vb3BlbmlkYy9wcm90ZWN0ZWQvcmVkaXJlY3RfdXJpIiwiYWN0IjoiQVVUSEVOVElDQVRFIiwibm90ZXMiOnsiYXV0aF90eXBlIjoiY29kZSIsInNjb3BlIjoib3BlbmlkIGVtYWlsIiwiaXNzIjoiaHR0cDovL2tleWNsb2FrOjgwODAvYXV0aC9yZWFsbXMvVGVzdHJlYWxtIiwicmVzcG9uc2VfdHlwZSI6ImNvZGUiLCJyZWRpcmVjdF91cmkiOiJodHRwOi8vb3BlbmlkYy9wcm90ZWN0ZWQvcmVkaXJlY3RfdXJpIiwic3RhdGUiOiJtRzRVMnR2czl1WGh6TlRPejRIdWlTYVhZSXciLCJub25jZSI6Im5EQ09PSnVnNlgyNGRPN2d3QmRFcl9BVmVpRmhIYlNfY3ZFLUQ4WDNTQncifX0.Q-QLEpp9r9nSUbWfvzsbxVZ0CEzCtEBF1bgCQxd07sDvtAeNGNL3_z8omDrvJ2K-ZjJldAT6QfY1sWV7Ri4HeVHqOZYAcdHdQL_fGeOLh0-u9e62VxBgxVv_P_cUGOIA-Lyr-yMRKo1NZW8E51gKJB-MqTELfgSWZ51ef1dc3QdCgRIUQNEcZj_Pw7NbPqhNNHSOeQkuQ4MUlXerKWcR5PQmtwKQX9zKqYBUHE-_FUjYD_HW66g5y6W_4pwDUPT3L6DHBy0uCOT__qrweLGwidzn6d7VKL1SfmvX-xIj74CbN0aF-P2yUI7w8fsXt0reMH9HOAQFxTLtxnMWfgW8Jg; Version=1; Path=/auth/realms/Testrealm; HttpOnly
Server: WildFly/10
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-src 'self'
Date: Sat, 17 Dec 2016 11:42:18 GMT
Connection: keep-alive
X-Content-Type-Options: nosniff
Content-Type: text/html;charset=utf-8
Content-Length: 3584

This is reproduced by the poll script in the ajax-refresh branch.

solsson commented 7 years ago

@zandbelt Is it ruled out by OpenID specs to revalidate session using AJAX request? Hence the iframe recommendation? For us these iframes are impractical, even if we solve the 404, but maybe we should stop looking for a javascript-only solution? We figured that an AJAX request would follow the redirects just like a page reload does, and would therefore revalidate.

zandbelt commented 7 years ago

An AJAX request may follow redirects and may revalidate the session depending on the implementation at the OP. If the OP decides that the session is not/no-longer valid, then the AJAX request would break since there's no way to prompt the user for credentials.

But at this point I'm not sure what the issue is you're trying to solve or work around .

solsson commented 7 years ago

We're looking for a way to revalidate the session in an application that uses websockets and AJAX, where users may spend hours without a page reload. It looks like the following AJAX request using jQuery succeeds with revalidation:

$.ajax({
        url: '/some/protected/path',
        crossDomain: true,
        xhrFields: {
          withCredentials: true
        }
  });

Though I'm currently puzzled by finding websockets restored despite receving this CORS error in browser console: XMLHttpRequest cannot load https://[the-service]/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.

If I can make the above request work in production I will close this issue because we no longer see a need for the iframe based solution.

solsson commented 7 years ago

This describes our CORS problem, though I fail to reproduce exactly that with the test setup in this repo:

According to the second of those, this may be a blocker for AJAX+auth. It's the request that is caused by the redirect from the OP back to the OIDCRedirectURI that gets an Origin: null header.

solsson commented 7 years ago

Further proof-of-concept in this repository is blocked by Keycloak CORS being unavailable, despite proper configuration in the realm. Could be due to the use of http instead of https, or different port numbers.

solsson commented 6 years ago

Will be testing 2.3.3+ with some SPA configuration. Much of the above is pre-SPA.