Getting OpenID/OAuth in docker container



  • Hi,

    I'm trying to get the docker image working using Keycloak as the authorization server as described in this article. According to the configuration article, I should be able to provide the comple JSON object to configure the authentication extension using environment variables.
    I have the following configuration in my docker-compose file (I don't mind posting the client secrets as they work only on my local dev env anyway):

      jsreport:
        image: jsreport/jsreport:3.1.1-full
        volumes:
          - .dev/jsreport_data1:/app/data
        environment:
          extensions_authentication_authorizationServer_name: keycloak
          extensions_authentication_authorizationServer_issuer: http://localhost:8301/auth/realms/MyRealm
          extensions_authentication_authorizationServer_endpoints_jwks: http://localhost:8301/auth/realms/MyRealm/protocol/openid-connect/certs
          extensions_authentication_authorizationServer_endpoints_authorization: http://localhost:8301/auth/realms/MyRealm/protocol/openid-connect/auth
          extensions_authentication_authorizationServer_endpoints_token: http://localhost:8301/auth/realms/MyRealm/protocol/openid-connect/token
          extensions_authentication_authorizationServer_endpoints_introspection: http://localhost:8301/auth/realms/MyRealm/protocol/openid-connect/token/introspect
          extensions_authentication_authorizationServer_endpoints_userinfo: http://localhost:8301/auth/realms/MyRealm/protocol/openid-connect/userinfo
          extensions_authentication_authorizationServer_studioClient_clientId: js-report-studio
          extensions_authentication_authorizationServer_studioClient_clientSecret: e8721322-eea5-48ec-a5d7-53a8c80b6e4f
          extensions_authentication_authorizationServer_apiResource_clientId: js-report-api
          extensions_authentication_authorizationServer_apiResource_clientSecret: 1ede2612-3e4d-4624-a833-05002e03e199
          extensions_authentication_authorizationServer_authorizationRequest_scope: "openid profile"
          extensions_authentication_authorizationServer_introspectionRequest_tokenValidScopes: "jsreport"
        ports:
          - "8318:5488"
    

    This does not seem to affect JSreport at all, I can still access the studio without needing to provide any authentication.
    In the logs I don't see anything related to authentication being enabled.
    It's probably something obvious :) But could anyone point me in the right direction?


  • administrators

    hi @AlexGoris-KasparSolutions

    it is probably not obvious, but in docker by default the authentication extension is disabled until you configure some basic options for the authentication to work, a sample and a mention of that is available here. so you need to pass these options too:

    extensions_authentication_admin_username: admin
    extensions_authentication_admin_password: xxxx
    extensions_authentication_cookieSession_secret: yyylong
    

    after that you should see in the logs this: Authentication against custom authorization server is enabled

    i did a quick test locally with the options you are using and with the extra options i am mentioned and the server ends configured appropriately.

    also worth mentioning and just FYI we have an example of this OpenID integration available here which we recently updated to the jsreport v3



  • Sorry for the late reply, but I was finally able to pick this up again yesterday, and it works perfectly with the 3 added lines you mentioned! Thanks!



  • Hi @AlexGoris-KasparSolutions can you please share the config file of jsreports that are working for you with Keycloak authorization? Also did you have to make any additions in Keycloak to make it work?
    I am working on a similar thing and I am not getting any leads that work successfully. Please can you help me out?



  • @Mrinalini-Pal The config is pretty much what I pasted in my initial post in this topic, with the 3 lines @bjrmatos suggested added below it, and one more line to map keycloak usernames to js-report usernames, so in total:

      jsreport:
        image: jsreport/jsreport:3.1.1-full
        volumes:
          - .dev/jsreport_data1:/app/data
        environment:
          extensions_authentication_authorizationServer_name: keycloak
          extensions_authentication_authorizationServer_issuer: http://localhost:8301/auth/realms/MyRealm
          extensions_authentication_authorizationServer_endpoints_jwks: http://localhost:8301/auth/realms/MyRealm/protocol/openid-connect/certs
          extensions_authentication_authorizationServer_endpoints_authorization: http://localhost:8301/auth/realms/MyRealm/protocol/openid-connect/auth
          extensions_authentication_authorizationServer_endpoints_token: http://localhost:8301/auth/realms/MyRealm/protocol/openid-connect/token
          extensions_authentication_authorizationServer_endpoints_introspection: http://localhost:8301/auth/realms/MyRealm/protocol/openid-connect/token/introspect
          extensions_authentication_authorizationServer_endpoints_userinfo: http://localhost:8301/auth/realms/MyRealm/protocol/openid-connect/userinfo
          extensions_authentication_authorizationServer_studioClient_clientId: js-report-studio
          extensions_authentication_authorizationServer_studioClient_clientSecret: e8721322-eea5-48ec-a5d7-53a8c80b6e4f
          extensions_authentication_authorizationServer_apiResource_clientId: js-report-api
          extensions_authentication_authorizationServer_apiResource_clientSecret: 1ede2612-3e4d-4624-a833-05002e03e199
          extensions_authentication_authorizationServer_authorizationRequest_scope: "openid profile"
          extensions_authentication_authorizationServer_introspectionRequest_tokenValidScopes: "jsreport"
          extensions_authentication_admin_username: admin
          extensions_authentication_admin_password: super-secret-password
          extensions_authentication_cookieSession_secret: yyylong #(This needs to be some random string that you keep secret)
          extensions_authentication_authorizationServer_usernameField: username
        ports:
          - "8318:5488"
    

    The above config assumes you've got Keycloak running on your localhost (in my case through a docker container) on port 8301.

    First of all there's a reference to the keycloak realm in this config: 'MyRealm', either create this realm, or change the name accordingly to match your realm name in the above config. Note that using the default 'master' realm in Keycloak is not advisable, as per Keycloak docs this realm should be reserved for administrative tasks pertaining to Keycloak itself

    Then you'll need some scope which JSReport will accept, I've set this scope name to 'jsreport' (extensions_authentication_authorizationServer_introspectionRequest_tokenValidScopes parameter) in the config above, so go to 'Client scopes' in the main Keycloak menu, and add the scope with whatever name you want, just make sure it matches the name given in that mentioned parameter.

    As you see this config references 2 client-id's and accompanying secrets, these you will need to configure in Keycloak, just add 2 clients with the corresponding ids and set the 'Access type' to 'Confidential', once you save this you'll notice a 'Credentials' tab popping up under the client in Keycloak, you can go to that tab to copy the client secret.

    The config I pasted above uses this config line:

          extensions_authentication_authorizationServer_usernameField: username
    

    To tell js-report to map the 'username' property in the JWT claims to the js-report username, so we need to configure keycloak to pass the keycloak username in that property to the claims. To do this go to Clients in the main keycloak menu, then edit the client you're using for js-report-studio (the id whose name is in the extensions_authentication_authorizationServer_studioClient_clientId: parameter), go the the 'Mappers' tab, click 'Add builtin', check the 'username' mapper and click 'Add selected'

    This should be all, if you navigate to js-report studio, you should see a 'Login with keycloak' (the name 'keycloak' in the button is configured through the extensions_authentication_authorizationServer_name: parameter), clicking this should take you to keycloak, if you login with a username which also exists on js-report, you should be able to login to js-report.

    Hope this helps!



  • Hi @AlexGoris-KasparSolutions I have followed the steps that you mentioned above. I just have a couple of doubts -

    • Should the 'username' property be added to extensions_authentication_authorizationServer_studioClient_clientId: only and not to extensions_authentication_authorizationServer_apiResource_clientId: too because I need to use jsreports using both the studio and the API.

    • How to find out the username that exists in jsreports that need to be provided to keycloak during login? I have added a user with permission to read and edit all entities in jsreport that exists in keycloak, does anything more need to be done?

    • When I try to login using the username and password of the user mentioned in the previous point, I am getting Error: Authentication with authorization server failed. Information returned by the authorization server does not contain "username" field. Authentication cancelled message. My keycloak is running on a global IP through a docker container while the jsreport code is running on localhost. Is it not working for this reason?



  • Should the 'username' property be added to extensions_authentication_authorizationServer_studioClient_clientId: only and not to extensions_authentication_authorizationServer_apiResource_clientId: too because I need to use jsreports using both the studio and the API.

    I don't understand this question, both the extensions_authentication_authorizationServer_studioClient_clientId and the extensions_authentication_authorizationServer_apiResource_clientId properties must be passed a valid client id, not a user name. The value you fill in here must be a valid client in your keycloak config, meaning it must be listed when you go to the Configure > Clients menu in keycloak

    How to find out the username that exists in jsreports that need to be provided to keycloak during login? I have added a user with permission to read and edit all entities in jsreport that exists in keycloak, does anything more need to be done?

    See the docs for details on this, specifically:

    jsreport needs to know how to associate the users from your authorization server to users/groups in jsreport, this association is needed to correctly identify which jsreport entities and permissions the user of your authorization server is able to work with in the context of jsreport. to do this you will have to do any of the following:

    • associate the jsreport username of an existing jsreport user with a claim (authorizationServer.usernameField) linked to the user handled by your authorization server.
    • associate the jsreport group name of an existing jsreport group with a claim (authorizationServer.groupField) linked to the user handled by your authorization server. this association has the extra advantage of not requiring to duplicate the user of your authorization server in jsreport, you can handle all the permissions in jsreport in groups and let your authorization server handle the user, you just need to associate them so after login the jsreport is able to identify which entities it has access to. When doing SSO to the jsreport studio you can customize the username shown there by setting another claim (authorizationServer.usernameField) to the data linked to the user handled by your authorization server.

    In the config I have provided at the beginning of this topic, the authorizationServer.usernameField has been set to username, this means that JSReport expects to find a claim with the name username in the accesstoken it gets from Keycloak, and will use whatever value is in there to try to find a matching jsreport user. So if the JSreport user you have is called johndoe, your access token should like so:

    {
      "sub": "1234567890",
      "name": "John Doe",
      "iat": 1516239022,
      "username": "johndoe" // This will tell keycloak to authenticate you as its own 'johndoe' user
    }
    

    When I try to login using the username and password of the user mentioned in the previous point, I am getting Error: Authentication with authorization server failed. Information returned by the authorization server does not contain "username" field. Authentication cancelled message. My keycloak is running on a global IP through a docker container while the jsreport code is running on localhost. Is it not working for this reason?

    This is related to the previous point, if you copied the config in my original post, and left the extensions_authentication_authorizationServer_usernameField set to the username value, you probably didn't configure the js-report-studio client mappers as mentioned in my previous post.



  • I have configured the js-report-studio client mappers as mentioned in the previous post but still I am getting Error: Authentication with authorization server failed. Information returned by the authorization server does not contain "username" field. Authentication cancelled message. I cannot even see the username in the access token. Can you show me the headers called to generate the access token that you showed in the sample?



  • Sorry I can't share the headers of my project. But I can say they contain nothing special, it's a standard OAuth2 authorization code flow which requests the access token using the calls defined in the Oauth2 protocol. It is up to the keycloak to decide which claims to add to the access token so you must have missed something. Can you post your complete JSreport docker-compose config and some screenshots of your 2 keycloak clients?



  • I have downloaded the Github Jsreports project and updated the jsreports.config.json as follows :

    {
      "httpPort": 5488,
      "trustUserCode": true,
      "store": {
        "provider": "fs"
      },
      "blobStorage": {
        "provider": "fs"
      },
      "reportTimeout": 60000,
      "extensions": {
        "docker-workers": {
          "enabled": false,
          "container": {
            "image": "worker",
            "memory": "520m",
            "memorySwap": "720m",
            "cpus": "0.8"
          },
          "numberOfWorkers": 3
        },
        "authentication": {
          "cookieSession": {
            "secret": "secret_cookie"
          },
          "admin": {
            "username": "admin",
            "password": "password"
          },
          "authorizationServer": {
            "name": "keycloak",
            "issuer": "https://ip:port/auth/realms/apple",
            "endpoints": {
              "jwks": "https://ip:port/auth/realms/apple/protocol/openid-connect/certs",
              "authorization": "https://ip:port/auth/realms/apple/protocol/openid-connect/auth",
              "token": "https://ip:port/auth/realms/apple/protocol/openid-connect/token",
              "introspection": "https://ip:port/auth/realms/apple/protocol/openid-connect/token/introspect",
              "userinfo": "https://ip:port/auth/realms/apple/protocol/openid-connect/userinfo"
            },
            "studioClient": {
              "clientId": "jsreport-studio",
              "clientSecret": "exty7397YpjdwaobSVqVqQzgL5a2CMwB"
            },
            "apiResource": {
              "clientId": "jsreport-api",
              "clientSecret": "rkn3PP5pDxLa3LFvdGbiwjuaIhmTVUKK"
            },
            "usernameField": "username",
            "authorizationRequest": {
              "scope": ["openid", "profile"]
            },
            "introspectionRequest": {
              "tokenValidScopes": ["jsreport"]
            }
          },
          "enabled": true
        },
        "sample-template": {
          "createSamples": true
        }
      },
      "extensionsList": [
        "assets",
        "authentication",
        "authorization",
        "base",
        "browser-client",
        "child-templates",
        "chrome-pdf",
        "cli",
        "components",
        "data",
        "docx",
        "express",
        "freeze",
        "fs-store",
        "handlebars",
        "html-to-xlsx",
        "import-export",
        "jsrender",
        "licensing",
        "localization",
        "npm",
        "pdf-utils",
        "pptx",
        "public-templates",
        "reports",
        "sample-template",
        "scheduling",
        "scripts",
        "static-pdf",
        "studio",
        "studio-theme-dark",
        "tags",
        "text",
        "version-control",
        "xlsx"
      ]
    }
    

    The images of the 2 keycloak clients are as follows :
    0_1655116171758_upload-e1d0f1e2-c309-4e0e-800e-2da31a84a39e

    0_1655116219736_upload-6cbbcd9c-a8b9-4e30-ba5a-301cc9fb1f30

    0_1655116259219_upload-db24e1e7-e7f0-4569-bdc8-24d62df98336



  • Looks all good as far as I can see.
    What are the jsreport logs saying when you try to login?
    I would try intercepting the access token returned by keycloak so you can inspect it for the username claim


Log in to reply
 

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