Captain-P-Goldfish / scim-for-keycloak

a third party module that extends keycloak by SCIM functionality
BSD 3-Clause "New" or "Revised" License
187 stars 48 forks source link

SCIM remote user push incorrectly patches externalId in the remote SCIM provider. #126

Open dmitry-mightydevops opened 2 weeks ago

dmitry-mightydevops commented 2 weeks ago

I'm using latest version of this plugin https://scim-for-keycloak.de/ (production build kc-26-2.3.3) and keycloak 26.0.5 I'm getting externalId in remote SCIM provider incorrectly updated (to wrong value)

i.e. in remote SCIM after user sync changes the following correct data

{ 
    "id": "VXNlcjo1ODEwMTYx",
    "externalId": "bca594cc-1004-4010-a58d-1fd011d39241", (this is correct keycloak user id)
}

would become

{ 
    "id": "VXNlcjo1ODEwMTYx",
    "externalId": "VXNlcjo1ODEwMTYx", (here it becomes replaced with twingate id)
}

which then breaks the authentication via keycloak iDP on the twingate side and I have to manually patch the externalId to make it replaced with bca594cc-1004-4010-a58d-1fd011d39241

see it in action:

keycloak SCIM endpoint data

➜ curl -s -X GET https://keycloak.example.cloud/realms/example/scim/v2/Users -H "Authorization: Bearer ${MY_TOKEN}" -H "Content-Type: application/scim+json" | jq
{
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:ListResponse"
  ],
  "totalResults": 1,
  "itemsPerPage": 1,
  "startIndex": 1,
  "Resources": [
    {
      "schemas": [
        "urn:ietf:params:scim:schemas:core:2.0:User",
        "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
      ],
      "groups": [
        {
          "value": "facecfea-e1ff-4f82-8452-7b2fd654e17d",
          "display": "example/devops",
          "type": "direct"
        }
      ],
      "name": {
        "familyName": "User",
        "givenName": "Test"
      },
      "emails": [
        {
          "value": "Test@example.com",
          "primary": true
        }
      ],
      "id": "bca594cc-1004-4010-a58d-1fd011d39241",
      "externalId": "VXNlcjo1ODEwMTYx",
      "userName": "test-user",
      "title": "test user",
      "locale": "en_us",
      "timezone": "America/Chicago",
      "active": true,
      "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
        "organization": "example, LLC",
        "division": "executive",
        "department": "management"
      },
      "meta": {
        "resourceType": "User",
        "created": "2024-10-29T22:32:49.623Z",
        "lastModified": "2024-10-29T22:32:49.623Z",
        "location": "https://keycloak.example.cloud/realms/example/scim/v2/Users/bca594cc-1004-4010-a58d-1fd011d39241"
      }
    }
  ]
}

twingate (remote SCIM) provider

➜ curl -s -X GET "https://example.twingate.com/api/scim/v2/Users?count=10" -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/scim+json" | jq
{
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:ListResponse"
  ],
  "totalResults": 1,
  "startIndex": 1,
  "itemsPerPage": 10,
  "Resources": [
    {
      "schemas": [
        "urn:ietf:params:scim:schemas:core:2.0:User"
      ],
      "id": "VXNlcjo1ODEwMTYx",
      "externalId": "bca594cc-1004-4010-a58d-1fd011d39241",
      "userName": "test-user",
      "name": {
        "givenName": "Test",
        "familyName": "User",
        "formatted": "Test User"
      },
      "displayName": "Test User",
      "active": true,
      "meta": {
        "resourceType": "User",
        "created": "2024-11-08T21:03:07.607491+00:00",
        "lastModified": "2024-11-08T22:23:34.486784+00:00",
        "location": "https://example.twingate.com/api/scim/v2/Users/VXNlcjo1ODEwMTYx"
      },
      "emails": [
        {
          "value": "Test@example.com",
          "primary": true,
          "type": "work"
        }
      ]
    }
  ]
}

perfom the sync

image

query twingate (remote SCIM) provider

➜ curl -s -X GET "https://example.twingate.com/api/scim/v2/Users?count=10" -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/scim+json" | jq
{
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:ListResponse"
  ],
  "totalResults": 1,
  "startIndex": 1,
  "itemsPerPage": 10,
  "Resources": [
    {
      "schemas": [
        "urn:ietf:params:scim:schemas:core:2.0:User"
      ],
      "id": "VXNlcjo1ODEwMTYx",
      "externalId": "VXNlcjo1ODEwMTYx",
      "userName": "test-user",
      "name": {
        "givenName": "Test",
        "familyName": "User",
        "formatted": "Test User"
      },
      "displayName": "Test User",
      "active": true,
      "meta": {
        "resourceType": "User",
        "created": "2024-11-08T21:03:07.607491+00:00",
        "lastModified": "2024-11-08T22:23:34.486784+00:00",
        "location": "https://example.twingate.com/api/scim/v2/Users/VXNlcjo1ODEwMTYx"
      },
      "emails": [
        {
          "value": "Test@example.com",
          "primary": true,
          "type": "work"
        }
      ]
    }
  ]
}

as you see the id and externalId is the same now. at this time login into twingate via keycloak idP fails.

fix by manually patching via CURL

➜ curl -X PATCH -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" -d '{
    "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
    "Operations": [
      {
        "op": "replace",
        "path": "externalId",
        "value": "bca594cc-1004-4010-a58d-1fd011d39241"
      }
    ]
  }' https://example.twingate.com/api/scim/v2/Users/VXNlcjo1ODEwMTYx

now I can login, but this manual op bothers me.

Am I doing something wrong in my setup?

I tried to change these 2 (all possible combinations) but it had no effect and I still get externalId incorrectly set on each sync. image

dmitry-mightydevops commented 2 weeks ago

these are logs

Logs ``` 2024-11-09 00:48:35,563 DEBUG [org.apache.http.conn.ssl.SSLConnectionSocketFactory] (ForkJoinPool.commonPool-worker-1) Starting handshake 2024-11-09 00:48:35,583 DEBUG [org.apache.http.conn.ssl.SSLConnectionSocketFactory] (ForkJoinPool.commonPool-worker-1) Secure session established 2024-11-09 00:48:35,584 DEBUG [org.apache.http.conn.ssl.SSLConnectionSocketFactory] (ForkJoinPool.commonPool-worker-1) negotiated protocol: TLSv1.3 2024-11-09 00:48:35,584 DEBUG [org.apache.http.conn.ssl.SSLConnectionSocketFactory] (ForkJoinPool.commonPool-worker-1) negotiated cipher suite: TLS_AES_256_GCM_SHA384 2024-11-09 00:48:35,584 DEBUG [org.apache.http.conn.ssl.SSLConnectionSocketFactory] (ForkJoinPool.commonPool-worker-1) peer principal: CN=*.twingate.com 2024-11-09 00:48:35,584 DEBUG [org.apache.http.conn.ssl.SSLConnectionSocketFactory] (ForkJoinPool.commonPool-worker-1) peer alternative names: [*.twingate.com] 2024-11-09 00:48:35,584 DEBUG [org.apache.http.conn.ssl.SSLConnectionSocketFactory] (ForkJoinPool.commonPool-worker-1) issuer principal: CN=R10, O=Let's Encrypt, C=US 2024-11-09 00:48:35,584 DEBUG [org.apache.http.impl.conn.DefaultHttpClientConnectionOperator] (ForkJoinPool.commonPool-worker-1) Connection established 10.120.3.24:42676<->34.111.220.252:443 2024-11-09 00:48:35,584 DEBUG [org.apache.http.impl.execchain.MainClientExec] (ForkJoinPool.commonPool-worker-1) Executing request PUT /api/scim/v2/Users/VXNlcjo1ODEwMTYx HTTP/1.1 2024-11-09 00:48:35,584 DEBUG [org.apache.http.impl.execchain.MainClientExec] (ForkJoinPool.commonPool-worker-1) Proxy auth state: UNCHALLENGED 2024-11-09 00:48:35,585 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> PUT /api/scim/v2/Users/VXNlcjo1ODEwMTYx HTTP/1.1 2024-11-09 00:48:35,585 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> Content-Type: application/scim+json 2024-11-09 00:48:35,585 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> Authorization: Bearer hidden 2024-11-09 00:48:35,585 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> Content-Length: 446 2024-11-09 00:48:35,585 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> Host: example.twingate.com 2024-11-09 00:48:35,585 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> Connection: Keep-Alive 2024-11-09 00:48:35,586 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> User-Agent: Apache-HttpClient/4.5.14 (Java/21.0.5) 2024-11-09 00:48:35,586 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> Accept-Encoding: gzip,deflate 2024-11-09 00:48:35,586 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> "PUT /api/scim/v2/Users/VXNlcjo1ODEwMTYx HTTP/1.1[\r][\n]" 2024-11-09 00:48:35,587 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> "Content-Type: application/scim+json[\r][\n]" 2024-11-09 00:48:35,587 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> "Authorization: Bearer hidden[\r][\n]" 2024-11-09 00:48:35,587 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> "Content-Length: 446[\r][\n]" 2024-11-09 00:48:35,587 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> "Host: example.twingate.com[\r][\n]" 2024-11-09 00:48:35,587 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> "Connection: Keep-Alive[\r][\n]" 2024-11-09 00:48:35,587 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> "User-Agent: Apache-HttpClient/4.5.14 (Java/21.0.5)[\r][\n]" 2024-11-09 00:48:35,587 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> "Accept-Encoding: gzip,deflate[\r][\n]" 2024-11-09 00:48:35,587 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> "[\r][\n]" 2024-11-09 00:48:35,588 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 >> "{"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"],"locale":"en_us","emails":[{"primary":true,"value":"test@example.com"}],"name":{"givenName":"test","familyName":"user"},"timezone":"America/Chicago","urn:ietf:params:scim:schemas:extension:enterprise:2.0:User":{"division":"executive","organization":"example, LLC","department":"management"},"title":"CTO","externalId":"VXNlcjo1ODEwMTYx","userName":"test-user","active":true}" 2024-11-09 00:48:36,379 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "HTTP/1.1 200 OK[\r][\n]" 2024-11-09 00:48:36,379 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "server: twingate[\r][\n]" 2024-11-09 00:48:36,379 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "date: Sat, 09 Nov 2024 00:48:36 GMT[\r][\n]" 2024-11-09 00:48:36,379 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "content-type: application/scim+json[\r][\n]" 2024-11-09 00:48:36,379 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "location: https://example.twingate.com/api/scim/v2/Users/VXNlcjo1ODEwMTYx[\r][\n]" 2024-11-09 00:48:36,379 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "vary: Accept, Accept-Encoding[\r][\n]" 2024-11-09 00:48:36,379 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "allow: GET, PUT, PATCH, DELETE[\r][\n]" 2024-11-09 00:48:36,380 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "x-frame-options: SAMEORIGIN[\r][\n]" 2024-11-09 00:48:36,380 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "strict-transport-security: max-age=31536000; includeSubDomains[\r][\n]" 2024-11-09 00:48:36,380 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "x-content-type-options: nosniff[\r][\n]" 2024-11-09 00:48:36,380 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "referrer-policy: same-origin[\r][\n]" 2024-11-09 00:48:36,380 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "cross-origin-opener-policy: same-origin[\r][\n]" 2024-11-09 00:48:36,380 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "content-encoding: gzip[\r][\n]" 2024-11-09 00:48:36,380 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "via: 1.1 google[\r][\n]" 2024-11-09 00:48:36,380 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000[\r][\n]" 2024-11-09 00:48:36,380 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "Transfer-Encoding: chunked[\r][\n]" 2024-11-09 00:48:36,380 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "[\r][\n]" 2024-11-09 00:48:36,380 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << HTTP/1.1 200 OK 2024-11-09 00:48:36,380 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << server: twingate 2024-11-09 00:48:36,380 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << date: Sat, 09 Nov 2024 00:48:36 GMT 2024-11-09 00:48:36,380 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << content-type: application/scim+json 2024-11-09 00:48:36,380 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << location: https://example.twingate.com/api/scim/v2/Users/VXNlcjo1ODEwMTYx 2024-11-09 00:48:36,380 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << vary: Accept, Accept-Encoding 2024-11-09 00:48:36,380 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << allow: GET, PUT, PATCH, DELETE 2024-11-09 00:48:36,380 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << x-frame-options: SAMEORIGIN 2024-11-09 00:48:36,380 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << strict-transport-security: max-age=31536000; includeSubDomains 2024-11-09 00:48:36,380 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << x-content-type-options: nosniff 2024-11-09 00:48:36,380 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << referrer-policy: same-origin 2024-11-09 00:48:36,380 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << cross-origin-opener-policy: same-origin 2024-11-09 00:48:36,380 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << content-encoding: gzip 2024-11-09 00:48:36,380 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << via: 1.1 google 2024-11-09 00:48:36,380 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 2024-11-09 00:48:36,380 DEBUG [org.apache.http.headers] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << Transfer-Encoding: chunked 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "[0x1f][\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "[0x8b][\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "[0x8][\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "[0x8][\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "[0x0][\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "[0x0][\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "[0x0][\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "[0x0][\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "[0x0][\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "[0x3][\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "a[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "a[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "a[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "a[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "a[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,381 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "a[\r][\n]" 2024-11-09 00:48:36,382 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,382 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "a[\r][\n]" 2024-11-09 00:48:36,382 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,382 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "a[\r][\n]" 2024-11-09 00:48:36,382 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "00000001[\r][\n]" 2024-11-09 00:48:36,382 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "a[\r][\n]" 2024-11-09 00:48:36,382 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "001[\r][\n]" 2024-11-09 00:48:36,382 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "a[\r][\n]" 2024-11-09 00:48:36,382 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "188[\r][\n]" 2024-11-09 00:48:36,382 DEBUG [org.apache.http.wire] (ForkJoinPool.commonPool-worker-1) http-outgoing-4 << "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[0x0]uQ[0xc1]N[0xc2]@[0x10][0xfd][0x15]3WK[0xbb]-[0x15][0xec][0x9e]https://example.twingate.com:443][total available: 0; route allocated: 0 of 2; total allocated: 0 of 20] 2024-11-09 00:48:36,389 DEBUG [de.captaingoldfish.scim.sdk.keycloak.scim_client.fullsynchronize.push.AbstractPushRealmSynchronizer] (ForkJoinPool.commonPool-worker-1) Successfully updated USERS 'test-user' 2024-11-09 00:48:36,390 TRACE [org.keycloak.models.cache.infinispan.UserCacheSession] (executor-thread-1) getuserById bca594cc-1004-4010-a58d-7fd011d39247 2024-11-09 00:48:36,391 TRACE [org.keycloak.models.cache.infinispan.UserCacheSession] (executor-thread-1) not cached 2024-11-09 00:48:36,409 TRACE [org.keycloak.connections.jpa.DefaultJpaConnectionProvider] (executor-thread-1) DefaultJpaConnectionProvider close() 2024-11-09 00:48:38,159 TRACE [org.keycloak.models.cache.infinispan.RealmCacheSession] (executor-thread-1) realm by name cache hit: master 2024-11-09 00:48:38,159 TRACE [org.keycloak.models.cache.infinispan.RealmCacheSession] (executor-thread-1) by id cache hit: master 2024-11-09 00:48:38,161 TRACE [org.keycloak.services.cors.DefaultCors] (executor-thread-1) No Origin header, ignoring 2024-11-09 00:48:38,162 TRACE [org.keycloak.keys.DefaultKeyManager] (executor-thread-1) Active key found: realm=master kid=W-dZ0i9GBGSsBObf0fVZ0kgY3A9hduwV7u7b3TuLpcY algorithm=RS256 use=SIG 2024-11-09 00:48:48,159 TRACE [org.keycloak.models.cache.infinispan.RealmCacheSession] (executor-thread-1) realm by name cache hit: master 2024-11-09 00:48:48,159 TRACE [org.keycloak.models.cache.infinispan.RealmCacheSession] (executor-thread-1) by id cache hit: master 2024-11-09 00:48:48,160 TRACE [org.keycloak.services.cors.DefaultCors] (executor-thread-1) No Origin header, ignoring 2024-11-09 00:48:48,160 TRACE [org.keycloak.keys.DefaultKeyManager] (executor-thread-1) Active key found: realm=master kid=W-dZ0i9GBGSsBObf0fVZ0kgY3A9hduwV7u7b3TuLpcY algorithm=RS256 use=SIG ```

as you can see

PUT /api/scim/v2/Users/VXNlcjo1ODEwMTYx HTTP/1.1
Host: example.twingate.com
{
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User"
  ],
  "locale": "en_us",
  "emails": [
    {
      "primary": true,
      "value": "test@example.com"
    }
  ],
  "name": {
    "givenName": "test",
    "familyName": "user"
  },
  "timezone": "America/Chicago",
  "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
    "division": "executive",
    "organization": "example, LLC",
    "department": "management"
  },
  "title": "CTO",
  "externalId": "VXNlcjo1ODEwMTYx",
  "userName": "test-user",
  "active": true
}

so it sends wrong externalId (it should have been bca594cc-1004-4010-a58d-1fd011d39241)

dmitry-mightydevops commented 2 weeks ago

so combination 1

image

payload in keycloak logs:

PUT /api/scim/v2/Users/VXNlcjo6ODQwMTYx HTTP/1.1
{
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User"
  ],
  "locale": "en_us",
  "emails": [
    {
      "primary": true,
      "value": "test@example.com"
    }
  ],
  "name": {
    "givenName": "test",
    "familyName": "user"
  },
  "timezone": "America/Chicago",
  "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
    "division": "executive",
    "organization": "example, LLC",
    "department": "management"
  },
  "title": "CTO",
  "externalId": "VXNlcjo6ODQwMTYx",
  "userName": "test-user",
  "active": true
}

this overrides externalId on twingate side and makes it impossible to login

combination 2

image

payload in keycloak logs:

{
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User"
  ],
  "locale": "en_us",
  "emails": [
    {
      "primary": true,
      "value": "test@example.com"
    }
  ],
  "name": {
    "givenName": "test",
    "familyName": "user"
  },
  "timezone": "America/Chicago",
  "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
    "division": "executive",
    "organization": "example, LLC",
    "department": "management"
  },
  "title": "CTO",
  "externalId": "VXNlcjo6ODQwMTYx",
  "id": "bca594cc-1004-4010-a58d-7fd011d39247",
  "userName": "test-user",
  "active": true
}

this overrides externalId on twingate side and makes it impossible to login. It would be nice to not include externalId here in the payload.

combination 3

image

payload in keycloak logs:

{
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User"
  ],
  "locale": "en_us",
  "emails": [
    {
      "primary": true,
      "value": "test@example.com"
    }
  ],
  "name": {
    "givenName": "test",
    "familyName": "user"
  },
  "timezone": "America/Chicago",
  "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
    "division": "executive",
    "organization": "example, LLC",
    "department": "management"
  },
  "title": "CTO",
  "externalId": "VXNlcjo6ODQwMTYx",
  "id": "VXNlcjo6ODQwMTYx",
  "userName": "test-user",
  "active": true
}

combination 4

image

{
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User"
  ],
  "locale": "en_us",
  "emails": [
    {
      "primary": true,
      "value": "test@exampl.com"
    }
  ],
  "name": {
    "givenName": "test",
    "familyName": "user"
  },
  "timezone": "America/Chicago",
  "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
    "division": "executive",
    "organization": "exampl, LLC",
    "department": "management"
  },
  "title": "CTO",
  "externalId": "VXNlcjo6ODQwMTYx",
  "userName": "test-user",
  "active": true,
  "id": "VXNlcjo6ODQwMTYx"
}

So either this is a bug or I did something wrong or we need a new setting like send externalId as externalId field and if not set - don't include it in the PUT /Users payload.

Captain-P-Goldfish commented 2 weeks ago

Hi Dmitry, thanks for reporting this so thoroughly. I know where the problem is but I need to fix it. You haven't done anything wrong here. I need to prevent the synchronization of the externalId-attribute. This will require some time. I will try to to fix this until the end of next week.

dmitry-mightydevops commented 1 week ago

@Captain-P-Goldfish thank you! Pls also check this one I found: https://github.com/Captain-P-Goldfish/scim-for-keycloak/issues/127 as it could be related.

In the meantime I will go and buy an enterprise license. Thanks for your work!