Authenticating jsreport with Keycloak as the authorization server



  • Hello,

    I'm working on authenticating jsreport 3.1.1 with Keycloak as the authorization server. After executing the OAuth flow and being redirected back to jsreport, jsreport records the error below.

    Error: Failed to serialize user into session
        at pass (/app/node_modules/passport/lib/authenticator.js:281:19)
        at serialized (/app/node_modules/passport/lib/authenticator.js:286:7)
        at /app/node_modules/@jsreport/jsreport-authentication/lib/main.js:163:42
        at pass (/app/node_modules/passport/lib/authenticator.js:294:9)
        at Authenticator.serializeUser (/app/node_modules/passport/lib/authenticator.js:299:5)
        at SessionManager.logIn (/app/node_modules/passport/lib/sessionmanager.js:14:8)
        at IncomingMessage.req.login.req.logIn (/app/node_modules/passport/lib/http/request.js:50:33)
        at /app/node_modules/@jsreport/jsreport-authentication/lib/main.js:352:13
        at allFailed (/app/node_modules/passport/lib/middleware/authenticate.js:107:18)
        at attempt (/app/node_modules/passport/lib/middleware/authenticate.js:180:28)
    

    Any thoughts on what might be causing this?


  • administrators

    hi!

    hmm that looks weird, it looks that this error happens when there is no passport.serializeUser or passport.deserializeUser configured but we always do it here. are you using the standard jsreport 3.1.1? or are you integrating jsreport into an existing nodejs app + express?



  • I'm using a Docker image derived from the standard 3.1.1 image with a custom jsreport.json.config.

    FROM jsreport/jsreport:3.1.1
    COPY --chown=jsreport:jsreport jsreport.config.json /app
    
    {
      "httpPort": 5488,
      "store": {
        "provider": "fs"
      },
      "logger": {
        "console": {
          "transport": "console",
          "level": "debug"
        },
        "file": {
          "transport": "file",
          "level": "info",
          "filename": "logs/reporter.log"
        },
        "error": {
          "transport": "file",
          "level": "error",
          "filename": "logs/error.log"
        }
      },
      "allowLocalFilesAccess": false,
      "reportTimeout": 60000,
      "workers": {
        "numberOfWorkers": 2
      },
      "extensions": {
        "authentication": {
          "enabled": true,
          "cookieSession": {
            "secret": "******"
          },
          "admin": {
            "username": "admin",
            "password": "******"
          },
          "authorizationServer": {
            "name": "Health Dynamics",
            "issuer": "https://accounts.healthdynamics.test/auth/realms/master",
            "endpoints": {
              "jwks": "https://accounts.healthdynamics.test/auth/realms/master/.well-known/openid-configuration/certs",
              "authorization": "https://accounts.healthdynamics.test/auth/realms/master/protocol/openid-connect/auth",
              "token": "https://accounts.healthdynamics.test/auth/realms/master/protocol/openid-connect/token",
              "introspection": "https://accounts.healthdynamics.test/auth/realms/master/protocol/openid-connect/token/introspect",
              "userinfo": "http://accounts.healthdynamics.test/auth/realms/master/protocol/openid-connect/userinfo"
            },
            "studioClient": {
              "clientId": "jsreport-studio",
              "clientSecret": "******"
            },
            "apiResource": {
              "clientId": "jsreport-api",
              "clientSecret": "******"
            },
            "usernameField": "username",
            "timeout": 6000,
            "authorizationRequest": {
              "scope": [
                "openid",
                "profile"
              ],
              "introspectionRequest": {
                "tokenValidScopes": [
                  "jsreport"
                ],
                "extraBody": {}
              }
            }
          }
        },
        "authorization": {
          "enabled": true
        }
      }
    }
    

    I've tried this config with both the array and string versions of scopes.


  • administrators

    i was able to reproduce the problem with an openid authorization server based on oidc-provider.

    there are two problems:

    1.- first, in some specific case when the authentication against the authorization server (in your case Keycloak) fails, the validation response contains different shape which i was not aware of, so we were not able to get useful information about what happened, and as default you get the weird Failed to serialize user into session
    2.- second, as mentioned on the previous problem, we are not able to get the error details, but in my test (against my authorization server) the case that trigger that Failed to serialize user into session is when the jwt response of the authorization server contains a different issuer or iss metadata to what is declared in authorizationServer.issuer options. for example, i was running my authorization server at http://localhost:5000 and i used the same value in my authorizationServer.issuer config, however, i wanted to make the server public to the world, so i used a tool (ngrok) to proxy my server with a different public url, in this case that tool gave me the url at http://7f50-2001-1388-144f-741b-18b4-88d-f9ec-e55e.ngrok.io, when I tried to authenticate in studio and after doing some changes to be able to get more detailed error i found this:

    Error: Authentication with authorization server failed. unexpected iss value, expected http://7f50-2001-1388-144f-741b-18b4-88d-f9ec-e55e.ngrok.io, got: http://localhost:5000 (jwt: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtleXN0b3JlLUNIQU5HRS1NRSJ9.eyJzdWIiOiJhZG1pbiIsImF0X2hhc2giOiJ6MWo3bVpkdEdXVUxWTW9ybC1pX193IiwiYXVkIjoianNyZXBvcnQtc3R1ZGlvIiwiZXhwIjoxNjM5Nzg3MDMzLCJpYXQiOjE2Mzk3ODM0MzMsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTAwMCJ9.qcF6Uyjx65Bdo-b4ikBP3lHyfG9MyX2HegttVhJSr46s8ri-rfNDc6LBrAICiIEpI0fAJT_7nV2ewkHWpeV-Gh0CDlnjvBIlOeG0VSNgGVRmnvZ52XAyfXk6BMgbuLMaTb_vxamp__0vNySY4_ElFFKhyHVRVkGdemR74LoD5jzjZhLzeVfG2wACd57fQBWMMs8oVEm19VG3fvBKw0eiBBtz_fGSRc86eqlo0smYdnT3RGkKHhLSaUGgMr9U9So3MtL-S-Rxfapa79PcBgKbg2-fbvo0neRuDHrdCce0FXrjJAb8MG3VnDqhAWQwbig7LsO-o07WL7eOGrD4qEo91g")
        at module.exports (/Volumes/DATA/workspace/jsreport3.2.0/node_modules/@jsreport/jsreport-core/lib/shared/createError.js:10:13)
        at MainReporter.createError (/Volumes/DATA/workspace/jsreport3.2.0/node_modules/@jsreport/jsreport-core/lib/shared/reporter.js:47:12)
        at /Volumes/DATA/workspace/jsreport3.2.0/node_modules/@jsreport/jsreport-authentication/lib/main.js:428:32
        at allFailed (/Volumes/DATA/workspace/jsreport3.2.0/node_modules/passport/lib/middleware/authenticate.js:107:18)
        at attempt (/Volumes/DATA/workspace/jsreport3.2.0/node_modules/passport/lib/middleware/authenticate.js:180:28)
        at OpenIDConnectStrategy.strategy.fail (/Volumes/DATA/workspace/jsreport3.2.0/node_modules/passport/lib/middleware/authenticate.js:297:9)
        at /Volumes/DATA/workspace/jsreport3.2.0/node_modules/openid-client/lib/passport_strategy.js:181:12
        at processTicksAndRejections (node:internal/process/task_queues:96:5)
    caused by: RPError: unexpected iss value, expected http://7f50-2001-1388-144f-741b-18b4-88d-f9ec-e55e.ngrok.io, got: http://localhost:5000
        at Client.validateJWT (/Volumes/DATA/workspace/jsreport3.2.0/node_modules/openid-client/lib/client.js:834:15)
        at Client.validateIdToken (/Volumes/DATA/workspace/jsreport3.2.0/node_modules/openid-client/lib/client.js:693:60)
        at Client.callback (/Volumes/DATA/workspace/jsreport3.2.0/node_modules/openid-client/lib/client.js:469:18)
        at processTicksAndRejections (node:internal/process/task_queues:96:5)
        at async /Volumes/DATA/workspace/jsreport3.2.0/node_modules/openid-client/lib/passport_strategy.js:152:22
    

    as you can see the error says that the authentication validation was expecting the issuer (or iss) metadata of the token to be http://7f50-2001-1388-144f-741b-18b4-88d-f9ec-e55e.ngrok.io (my public url) but instead it was http://localhost:5000, this was caused because i forgot to update my authorization server issuer value to be the public url instead, after i configured my authorization server and also make my authorizationServer.issuer config to be the same url, the problems were gone.

    I think you have the same case, that your authorization server (Keycloak) is generating tokens with iss values different to what you defined in authorizationServer.issuer

    if you want to try the changes that I did to get a better error response (which will also verify that i am right about that you are having the same problem that I am mentioning) you can do this:

    • download the file i am attaching and save it as main.js to the directory in which you use as your docker context when building your image
    • update your dockerfile to this
    FROM jsreport/jsreport:3.1.1
    COPY --chown=jsreport:jsreport main.js /app/node_modules/@jsreport/jsreport-authentication/lib/main.js
    COPY --chown=jsreport:jsreport jsreport.config.json /app
    

    after that try to login, and now you should get a better error which will telll you what was the problem.


  • administrators



  • Thanks. I updated the image with the main.js you provided and tried again. This is the result.

    0_1640103799542_jsreport1.png

    Any thoughts?


  • administrators

    hmm it seems the GET to the authorizationServer.endpoints.jwks (which according to your config points to https://accounts.healthdynamics.test/auth/realms/master/.well-known/openid-configuration/certs) is returning 404, are you sure it is the right URL? it should be the endpoint which your authorization server (Keycloak) returns the public keys used to verify any JSON Web Token issued by it. i tried to open that page but it seems your auth server is not public and it is only available to your internal network, so no way for me to test it.



  • Thanks for pointing that out. I did have a typo in the jwks endpoint which I corrected. This fixed the 404 error. I get this error now.

    0_1640122801566_Screen Shot 2021-12-21 at 3.39.18 PM.png

    The OpenID configuration is below. I'm only able to upload images. Hopefully you can read it.

    0_1640123407422_openid-config.png


  • administrators

    hmm now according to the error your authorizationServer.endpoints.userinfo endpoint is returning a response with status code 302, which probably means a redirect, which does not seem to be valid according to the openid spec (i don't see an expected 302 response in the spec), I am not sure why, perhaps Keycloak is expecting some additional information to be present in the request to the userinfo endpoint, but i don't know what could be, perhaps there is a mention to that in its docs.

    if you want i can try to debug a bit and see if i can get more information, for that i will need that you share with me the following in order to setup my local jsreport instance to authenticate against your Keycloak server:

    • the real studio client clientSecret you are using
    • the real api resource clientSecret you are using
    • the credentials for a test user that you are using to login in Keycloak

    if this is just your test environment you can share those details here, or if you want you can share it privately at bjrmatos@gmail.com



  • I don't have a publicly available environment. I should hopefully have time to set one up later in the week.


Log in to reply
 

Looks like your connection to jsreport forum was lost, please wait while we try to reconnect.