Navigation

    Kopano
    • Register
    • Login
    • Search
    • Categories
    • Get Official Kopano Support
    • Recent
    Statement regarding the closure of the Kopano community forum and the end of the community edition

    WebApp + Konnect with LDAP Backend

    Kopano WebApp
    3
    17
    1260
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • alindemann
      alindemann last edited by

      Hi,
      I have an issue with WebApp version 3.5.6 and the latest Konnect SSO service.
      The WebApp uses the OIDC sub as username for the authentication with the kopano-server. This creates problems, as the OIDC sub in this case is either the full LDAP DN or a KV-Pair list encoded into a string (like “uid=foo”). Both variants are not accepted by the kopano server as valid usernames.

      Is this behavior intended? If not, will this be fixed in a future version?

      Temporary, I created a tweaked version of konnect, which serves the correct username as sub, since I could not find the location in the webapp sourcecode to fix it there. If needed, I could provide this fix.

      1 Reply Last reply Reply Quote 0
      • longsleep
        longsleep Kopano last edited by

        Hi,

        i am not exactly sure how your setup is. It reads like you use Konnect with LDAP backend (thus the oidc sub is derived from the LDAP DN. What is not clear to me is how you use WebApp in this context. Normally to use WebApp one would use the Kopano backend of Konnect and then have the Kopano server authenticate using the access token.

        If you use some other way to authenticate web WebApp via a pass through username or similar, you could look at the preferred_username claim in the ID token.

        Maybe you can provide the details of this setup, so we can investigate what can be done to improve on it.

        Thanks!

        1 Reply Last reply Reply Quote 0
        • alindemann
          alindemann last edited by

          I’m using the LDAP-Backend to have the possibility to deploy this setup stripped from kopano-core, only as SSO solution for other services.

          I tested my patch and reconfigured the kopano server, so that now, I get the following Error code from Kopano, regardless of sub configuration:

          KCOIDC validate error 270

          Is there somewhere a Human Readable table what this means? The source of the libkcoidc library is non conclusive to me…

          longsleep 1 Reply Last reply Reply Quote 0
          • longsleep
            longsleep Kopano @alindemann last edited by longsleep

            @alindemann said in WebApp + Konnect with LDAP Backend:

            KCOIDC validate error 270

            Is there somewhere a Human Readable table what this means? The source of the libkcoidc library is non conclusive to me…

            Sorry about that - the error codes are generated. There is a unit test which can dump them in human readable form.

             ~/go/src/stash.kopano.io/kc/libkcoidc  go test -v
            === RUN   TestErrors
            --- PASS: TestErrors (0.00s)
                errors_test.go:24: 258: Invalid Issuer Identifier Value (:0x102)
                errors_test.go:24: 262: Unexpected Token Signing Method (:0x106)
                errors_test.go:24: 265: Unknown Token Key (:0x109)
                errors_test.go:24: 270: Missing required scope (:0x10e)
                errors_test.go:24: 259: Already Initialized (:0x103)
                errors_test.go:24: 261: Timeout (:0x105)
                errors_test.go:24: 264: Token Expired Or Not Valid Yet (:0x108)
                errors_test.go:24: 267: Token Validation Failed (:0x10b)
                errors_test.go:24: 268: Is Closed (:0x10c)
                errors_test.go:24: 263: Malformed Token (:0x107)
                errors_test.go:24: 266: Invalid Token Signature (:0x10a)
                errors_test.go:24: 257: Unknown (:0x101)
                errors_test.go:24: 260: Not Initialized (:0x104)
                errors_test.go:24: 269: Wrong Initialization (:0x10d)
            PASS
            ok      stash.kopano.io/kc/libkcoidc    0.002s
            

            So your 270: Missing required scope (:0x10e) error most likely means that you have not configured the kopano/gc scope which is required by kopano server. If Konnect uses the kc backend, this scope is automatically set. For other backends, it needs configuration in scopes.yaml since Konnect by default will ignore requests to unknown scopes.

            1 Reply Last reply Reply Quote 0
            • alindemann
              alindemann last edited by

              Ok, thanks for this,

              you are right, at this time the scope kopano/gc is ignored.

              Now I am digging into the scopes.yaml format and semantics. Is there any documentation on that? I have tried to remap an existing unused scope, but it still ignores it in the requests (as tested with kopano and an oidc debugger).

              Another thought:
              I could remove that scope from the webapp config, but I am unsure whether kopano core requires it to be there in the oidc response forwarded by the webapp.

              longsleep 1 Reply Last reply Reply Quote 0
              • longsleep
                longsleep Kopano @alindemann last edited by

                @alindemann said in WebApp + Konnect with LDAP Backend:

                Ok, thanks for this,

                you are right, at this time the scope kopano/gc is ignored.

                Now I am digging into the scopes.yaml format and semantics. Is there any documentation on that? I have tried to remap an existing unused scope, but it still ignores it in the requests (as tested with kopano and an oidc debugger).

                Remapping scopes is when a client requests a specific scope and the OIDC provider wishes to give another one.

                Another thought:
                I could remove that scope from the webapp config, but I am unsure whether kopano core requires it to be there in the oidc response forwarded by the webapp.

                Kopano core requires the kopano/gc scope to accept SSO with OIDC. When you remove it from webapp, this merely has the consequence that Webapp does no longer request it. To get it accepted by Konnect so that this scope gets granted by Konnect, Konnect needs to know about the scope. If a scope is not implicitly enabled already, they might be added to scopes.yaml. With our packaging of Konnect the following scopes.yaml is shipped.

                # This file contains additional scopes for Konnect. All of the scopes listed
                # here are made available to clients upon request if not limited by other means.
                
                ---
                scopes:
                  kopano/kwm:
                    description: "Access Kopano Meet"
                
                  kopano/kvs:
                    description: "Access Kopano Key Value Store"
                
                  kopano/pubs:
                    description: "Access Kopano Pub/Sub"
                

                If you use the LDAP backend and still want to have the kopano/gc scope, just add it accordingly. The source tree of Konnect includes a scopes.yaml.in file as example but i agree that this could need some docs.

                1 Reply Last reply Reply Quote 0
                • alindemann
                  alindemann last edited by

                  Ok,

                  I have this code in my scope.yaml which should enable the scope:

                  scopes:
                    kopano/gc:
                      description: "Read and write your Kopano Groupware data"
                  

                  but both kopano-core and my debuging service are not getting the scope in response. Is there additional configuration needed?

                  longsleep 1 Reply Last reply Reply Quote 0
                  • longsleep
                    longsleep Kopano @alindemann last edited by

                    @alindemann said in WebApp + Konnect with LDAP Backend:

                    but both kopano-core and my debuging service are not getting the scope in response. Is there additional configuration needed?

                    Not that i can think of.

                    When Konnect is started, it tells about the scopes getting loaded like this (debug logging enabled):

                    registered scope                              id=kopano/gc priority=0
                    

                    Make sure you see a line like that. If you see that, and if you include the kopano/gc scope in the authorization endpoint request, please share further details of the requests you are sending. In the access token, you will be finding the scopes which got granted by Konnect.

                    "kc.authorizedScopes": [
                        "openid",
                        "profile",
                        "email",
                        "kopano/gc"
                      ],
                    

                    alternatively if you send prompt=consent or use an untrusted client, you will also see the scope in the consent screen.

                    1 Reply Last reply Reply Quote 0
                    • alindemann
                      alindemann last edited by

                      Ok, I found a typo in the config (kopano/gc vs konnect/gc), fixed that and now the error message disappears.

                      But I still can’t log in, following log lines appear:

                      time="2019-07-16T13:30:10Z" level=debug msg="ldap identifier backend logon" id="uid=admin,<redacted>" username=admin
                      time="2019-07-16T13:30:10Z" level=debug msg="identifier client lookup" client_id=kopano-webapp known=true redirect_uri="https://webmail.<redacted>/#oidc-callback " trusted=true
                      time="2019-07-16T13:30:10Z" level=debug msg="identifier client lookup" client_id=kopano-webapp known=true redirect_uri="https://webmail.<redacted>/#oidc-callback " trusted=true
                      Jul 16 13:30:11 kopano-32-l4rbr kopano-server[32]: Authentication by plugin failed for user "uid=admin,<redacted>": Trying to authenticate failed: uid=admin,<redacted> not found in LDAP; username = uid=admin,<redacted>
                      Jul 16 13:30:11 kopano-32-l4rbr kopano-server[32]: Authentication by plugin failed for user "uid=admin,<redacted>": Trying to authenticate failed: uid=admin,<redacted> not found in LDAP; username = uid=admin,<redacted>
                      

                      Any thoughts about that ?

                      longsleep 1 Reply Last reply Reply Quote 0
                      • longsleep
                        longsleep Kopano @alindemann last edited by longsleep

                        @alindemann said in WebApp + Konnect with LDAP Backend:

                        Any thoughts about that ?

                        Well i am unsure how this is supposed to work at the moment. You are using konnect directly with LDAP and then expect the kopano server to somehow map the user from LDAP to a Kopano user. I think this is currently not possible since the kopano-server expects the subject to be something it can understand (an ABEID) so it can map the user using its users table to the external user.

                        Doing this the other way around might be possible, assuming some logic is added to the server’s access token validation/parsing which can allow the server to lookup the user using the external id directly. Needs work in provider/libserver/ECSession.cpp.

                        What is your reason to set up Konnect with the LDAP backend instead of the kc backend?

                        1 Reply Last reply Reply Quote 0
                        • alindemann
                          alindemann last edited by

                          Like said before, I want to have a solution I can use with other services, regardless whether there is a kopano server active in the specific setup. At the moment, it looks like I have to use the ldap backend if no kopano-server is present and the kc backend otherwise (with all implications to the other services).

                          If I understand correctly, the kopano-server needs a specific sub format. So if I use another OIDC provider with custom configuration it could work with that? Could you hint me to some documentation or the source lines where the ABEID is handled?

                          longsleep 1 Reply Last reply Reply Quote 0
                          • longsleep
                            longsleep Kopano @alindemann last edited by

                            @alindemann

                            Understood and i guess it makes sense to consider this as a supported scenario in a future release. The code where the kopano server finds the correct user from the result of the token validation is at

                            https://stash.kopano.io/projects/KC/repos/kopanocore/browse/provider/libserver/ECSession.cpp?until=f6d13f34fefe2181060b8a46c11aefaca6acfb58&untilPath=provider%2Flibserver%2FECSession.cpp#979-1007 - it should not be too hard to adapt. And figure out the extern ID from the access token.

                            1 Reply Last reply Reply Quote 0
                            • alindemann
                              alindemann last edited by

                              Ok, thank you very much for your help, I have a working setup now with Keycloak as OIDC provider.

                              fbartels 1 Reply Last reply Reply Quote 0
                              • fbartels
                                fbartels Kopano @alindemann last edited by

                                Hi @alindemann,

                                would you mind sharing how you achieved your setup?

                                Regards Felix

                                Resources:
                                https://kopano.com/blog/how-to-get-kopano/
                                https://documentation.kopano.io/
                                https://kb.kopano.io/

                                Support overview:
                                https://kopano.com/support/

                                1 Reply Last reply Reply Quote 0
                                • alindemann
                                  alindemann last edited by

                                  I used a oidc connector with one custom scope:

                                  {
                                      "name": "kopano/gc",
                                      "description": "Kopano Scope",
                                      "protocol": "openid-connect",
                                      "attributes": {
                                        "include.in.token.scope": "true",
                                        "display.on.consent.screen": "true"
                                      },
                                      "protocolMappers": [
                                        {
                                          "name": "kc.i.us",
                                          "protocol": "openid-connect",
                                          "protocolMapper": "oidc-script-based-protocol-mapper",
                                          "consentRequired": false,
                                          "config": {
                                            "userinfo.token.claim": "true",
                                            "id.token.claim": "true",
                                            "access.token.claim": "true",
                                            "claim.name": "kc\\.identity.kc\\.i\\.us",
                                            "jsonType.label": "String",
                                            "script": "/**\n * Available variables: \n * user - the current user\n * realm - the current realm\n * token - the current token\n * userSession - the current userSession\n * keycloakSession - the current userSession\n */\n\n\n//insert your code here...\n\nvar Base64 = Java.type(\"java.util.Base64\");\nvar encoder = Base64.getEncoder().withoutPadding();\n\nvar uid = user.getAttribute(\"LDAP_ID\")[0]\n\nexports = encoder.encodeToString(uid.getBytes())"
                                          }
                                        },
                                        {
                                          "name": "kc.provider",
                                          "protocol": "openid-connect",
                                          "protocolMapper": "oidc-hardcoded-claim-mapper",
                                          "consentRequired": false,
                                          "config": {
                                            "claim.value": "identifier-kc",
                                            "userinfo.token.claim": "true",
                                            "id.token.claim": "true",
                                            "access.token.claim": "true",
                                            "claim.name": "kc\\.provider",
                                            "jsonType.label": "String"
                                          }
                                        },
                                        {
                                          "name": "kc.authorizedScopes",
                                          "protocol": "openid-connect",
                                          "protocolMapper": "oidc-script-based-protocol-mapper",
                                          "consentRequired": false,
                                          "config": {
                                            "multivalued": "true",
                                            "userinfo.token.claim": "true",
                                            "id.token.claim": "true",
                                            "access.token.claim": "true",
                                            "claim.name": "kc\\.authorizedScopes",
                                            "script": "/**\n * Available variables: \n * user - the current user\n * realm - the current realm\n * token - the current token\n * userSession - the current userSession\n * keycloakSession - the current userSession\n */\n\n\n//insert your code here...\nvar ArrayList = Java.type(\"java.util.ArrayList\");\nvar scopes = new ArrayList();\n\nscopes.add(\"email\")\nscopes.add(\"openid\")\nscopes.add(\"profile\")\nscopes.add(\"kopano/gc\")\n\nexports = scopes"
                                          }
                                        },
                                        {
                                          "name": "kc.i.dn",
                                          "protocol": "openid-connect",
                                          "protocolMapper": "oidc-usermodel-attribute-mapper",
                                          "consentRequired": false,
                                          "config": {
                                            "userinfo.token.claim": "true",
                                            "user.attribute": "LDAP_ENTRY_NAME",
                                            "id.token.claim": "true",
                                            "access.token.claim": "true",
                                            "claim.name": "kc\\.identity.kc\\.i\\.dn",
                                            "jsonType.label": "String"
                                          }
                                        },
                                        {
                                          "name": "kc.isAccessToken",
                                          "protocol": "openid-connect",
                                          "protocolMapper": "oidc-hardcoded-claim-mapper",
                                          "consentRequired": false,
                                          "config": {
                                            "claim.value": "true",
                                            "userinfo.token.claim": "false",
                                            "id.token.claim": "false",
                                            "access.token.claim": "true",
                                            "claim.name": "kc\\.isAccessToken",
                                            "jsonType.label": "boolean"
                                          }
                                        },
                                        {
                                          "name": "kc.i.un",
                                          "protocol": "openid-connect",
                                          "protocolMapper": "oidc-usermodel-attribute-mapper",
                                          "consentRequired": false,
                                          "config": {
                                            "userinfo.token.claim": "true",
                                            "user.attribute": "LDAP_UID",
                                            "id.token.claim": "true",
                                            "access.token.claim": "true",
                                            "claim.name": "kc\\.identity.kc\\.i\\.un",
                                            "jsonType.label": "String"
                                          }
                                        },
                                        {
                                          "name": "kc.i.id",
                                          "protocol": "openid-connect",
                                          "protocolMapper": "oidc-script-based-protocol-mapper",
                                          "consentRequired": false,
                                          "config": {
                                            "userinfo.token.claim": "true",
                                            "id.token.claim": "true",
                                            "access.token.claim": "true",
                                            "claim.name": "kc\\.identity.kc\\.i\\.id",
                                            "jsonType.label": "String",
                                            "script": "/**\n * Available variables: \n * user - the current user\n * realm - the current realm\n * token - the current token\n * userSession - the current userSession\n * keycloakSession - the current userSession\n */\n\n\n//insert your code here...\nvar Base64 = Java.type(\"java.util.Base64\");\nvar encoder = Base64.getEncoder();\nvar uid = user.getAttribute(\"LDAP_ID\")[0]\n\nvar exid = encoder.encode(uid.getBytes())\n\nvar ByteOrder = Java.type(\"java.nio.ByteOrder\");\nvar ByteBuffer = Java.type(\"java.nio.ByteBuffer\");\n\nvar len = 32 + exid.length +4\n\nvar buffer = ByteBuffer.allocate(len).order(ByteOrder.LITTLE_ENDIAN)\n\n\n// abFlags[4]\nbuffer.putInt(0)\n\n//muidecsab\nbuffer.putInt(0x50a921ac)\nbuffer.putShort(0xd340)\nbuffer.putShort(0x48ee)\nbuffer.put(0xb3)\nbuffer.put(0x19)\nbuffer.put(0xfb)\nbuffer.put(0xa7)\nbuffer.put(0x53)\nbuffer.put(0x30)\nbuffer.put(0x44)\nbuffer.put(0x25)\n\n//ulVersion\nbuffer.putInt(0x1)\n//usType\nbuffer.putInt(0x6)\n//ulID (stub, uses extID)\nbuffer.putInt(0)\n\n//exID\nbuffer.put(exid)\n\nexports = encoder.encodeToString(buffer.array())"
                                          }
                                        }
                                      ]
                                    }
                                  

                                  the tricky attribute kc.i.id is being generated from a script (as seen above) which contains the following code in nice formatting:

                                  /**
                                   * Available variables: 
                                   * user - the current user
                                   * realm - the current realm
                                   * token - the current token
                                   * userSession - the current userSession
                                   * keycloakSession - the current userSession
                                   */
                                  
                                  
                                  //insert your code here...
                                  var Base64 = Java.type("java.util.Base64");
                                  var encoder = Base64.getEncoder();
                                  var uid = user.getAttribute("LDAP_ID")[0]
                                  
                                  var exid = encoder.encode(uid.getBytes())
                                  
                                  var ByteOrder = Java.type("java.nio.ByteOrder");
                                  var ByteBuffer = Java.type("java.nio.ByteBuffer");
                                  
                                  var len = 32 + exid.length +4
                                  
                                  var buffer = ByteBuffer.allocate(len).order(ByteOrder.LITTLE_ENDIAN)
                                  
                                  
                                  // abFlags[4]
                                  buffer.putInt(0)
                                  
                                  //muidecsab
                                  buffer.putInt(0x50a921ac)
                                  buffer.putShort(0xd340)
                                  buffer.putShort(0x48ee)
                                  buffer.put(0xb3)
                                  buffer.put(0x19)
                                  buffer.put(0xfb)
                                  buffer.put(0xa7)
                                  buffer.put(0x53)
                                  buffer.put(0x30)
                                  buffer.put(0x44)
                                  buffer.put(0x25)
                                  
                                  //ulVersion
                                  buffer.putInt(0x1)
                                  //usType
                                  buffer.putInt(0x6)
                                  //ulID (stub, uses extID)
                                  buffer.putInt(0)
                                  
                                  //exID
                                  buffer.put(exid)
                                  
                                  exports = encoder.encodeToString(buffer.array())
                                  
                                  fbartels 1 Reply Last reply Reply Quote 0
                                  • fbartels
                                    fbartels Kopano @alindemann last edited by

                                    @alindemann so you haven’t used Konnect at all?

                                    Regards Felix

                                    Resources:
                                    https://kopano.com/blog/how-to-get-kopano/
                                    https://documentation.kopano.io/
                                    https://kb.kopano.io/

                                    Support overview:
                                    https://kopano.com/support/

                                    1 Reply Last reply Reply Quote 0
                                    • alindemann
                                      alindemann last edited by

                                      This is correct.

                                      1 Reply Last reply Reply Quote 0
                                      • First post
                                        Last post