Skip to content

Graph API driveItem invite fails with CODE_INTERNAL when sharing with an accepted OCM/federated user #3262

Open

Description

@patxixi

Summary

Creating a federated share via the Graph API driveItem/invite endpoint fails with a generic internal error, even though the OCM invitation handshake (generate-invite / accept-invite) between the two instances completed successfully and is verifiable via the sciencemesh API.

Environment

OpenCloud 7.4.0 (rolling). Two independent self-hosted instances, embedded IDM (no external LDAP) on either side. OC_ENABLE_OCM=true on both instances. Matching ocmproviders.json declaring mutual trust between both domains.

Steps to reproduce

Step 1: On instance A (a.example.com), authenticate as user A and call POST /sciencemesh/generate-invite. Returns a token and expiration.

Step 2: On instance B (b.example.com), authenticate as user B and call POST /sciencemesh/accept-invite with the token and providerDomain=a.example.com. Returns HTTP 200.

Step 3: Confirm the handshake on both instances via GET /sciencemesh/find-accepted-users. Both sides correctly list each other as an accepted federated user. This part works as documented.

Step 4: On instance A, attempt to share an existing personal folder with user B via the Graph API:

POST /graph/v1beta1/drives/{driveId}/items/{itemId}/invite
Content-Type: application/json

{
"recipients": [
{
"objectId": "@https://b.example.com",
"@libre.graph.recipient.type": "user"
}
],
"roles": ["b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5"]
}

Expected result

Per services/ocm/README.md: "we allow creating federated shares using the graph API. Clients can now discover the available sharing roles and invite federated users using the graph API." The share should be created as an OCM share.

Actual result

HTTP 400 with body:
error.code = invalidRequest
error.message = generalException: unexpected status code while getting accepted user: CODE_INTERNAL

Investigation

Traced through the source (main branch, commit-pinned dependency github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d).

DriveItemPermissionsService.Invite() in services/graph/pkg/service/v0/api_driveitem_permissions.go correctly detects that objectID is not a local user (identityCache.GetCS3User returns ErrNotFound), and since IncludeOCMSharees is enabled, falls back to identityCache.GetAcceptedCS3User(ctx, objectID) in services/graph/pkg/identity/cache/cache.go.

GetAcceptedCS3User parses the composite id via getIDAndMeshProvider, builds a cs3User.UserId{Idp, OpaqueId, Type: USER_TYPE_FEDERATED}, and calls revautils.GetAcceptedUserWithContext() (github.com/opencloud-eu/reva/v2/pkg/utils), which calls gwc.GetAcceptedUser(ctx, invitev1beta1.GetAcceptedUserRequest{RemoteUserId: userID}).

This gRPC call returns status CODE_INTERNAL, which checkStatusCode("getting accepted user", ...) turns into the generic error surfaced above.

So the failure is inside the gateway's GetAcceptedUser RPC handler, not in the Graph API request itself. The same federated user is correctly resolved by the sciencemesh HTTP API (find-accepted-users) moments earlier, so the underlying invitation data is intact; something in the gRPC path specifically breaks.

This appears to block the entire create-a-federated-share-via-the-Graph-API flow documented in services/ocm/README.md, since every code path for a federated recipient depends on this lookup succeeding.

Possibly related to the ongoing OCM compliance work in opencloud-eu/reva#383.

Additional context

objectId format tried: opaque-id@https://domain (as returned by /sciencemesh/find-accepted-users), also tried without the https:// prefix and with just the opaque id, same error in all variants.

Role ID used (b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5, Can view) is valid and works correctly for local-user invites on the same folder.

IncludeOCMSharees must be enabled on this deployment, since the code path reaches the OCM-specific lookup at all (with it disabled, the error would instead be the earlier generic failed-user-lookup error for a plain not-found user).

Activity

  1. micbar commented on Aug 14, 2026

    @micbar
    Member
  2. patxixi commented on Aug 14, 2026

    @patxixi
    Author

    @butonic @dragotin FYI

    Please also refer to link issue opencloud-eu/web#4039. Thanks !

  3. VicDeo commented on Aug 18, 2026

    @VicDeo
    Contributor

    @patxixi
    Expected objectId format
    The recipient must match how accepted users are stored after the invitation exchange. In practice, clients must build it from GET /sciencemesh/find-accepted-users:

    objectId = user_id + "@" + idp

    E.g.

    $ curl -k -u alan:demo https://cloud1.opencloud.test:9200/sciencemesh/find-accepted-users              
    [
      {
      "display_name":"Mary Kenneth Keller",
      "idp":"cloud2.opencloud.test:10200",
       "user_id":"056fc874-dd7f-11ef-ba84-af6fca4b7289@https://cloud2.opencloud.test:10200",
       "mail":"mary@example.org"
      }
    ]
    

    in this case the expected recipient is exactly

    ----------------------------------user_id-------------------------------@------idp----------
    056fc874-dd7f-11ef-ba84-af6fca4b7289@https://cloud2.opencloud.test:10200@cloud2.opencloud.test:10200
    

    It looks ugly and the most likely the invitation exchange process lacks proper remote user id normalization.

    So it doesn't work this way (056fc874-dd7f-11ef-ba84-af6fca4b7289@https://cloud2.opencloud.test:10200):

    curl -k -u alan:demo -X POST \
      "https://cloud1.opencloud.test:9200/graph/v1beta1/drives/$driveid/items/$itemid/invite" \
      -H 'Content-Type: application/json' \
      -d '{       
        "recipients": [{
          "objectId": "056fc874-dd7f-11ef-ba84-af6fca4b7289@https://cloud2.opencloud.test:10200",                            
          "@libre.graph.recipient.type": "user"
        }],
        "roles": ["b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5"]
      }'
    {"error":{"code":"invalidRequest","innererror":{"date":"2026-08-17T22:31:15Z","request-id":"reborn/F78MY7iLkz-001531"},"message":"generalException: unexpected status code while getting accepted user: CODE_INTERNAL"}}
    

    neither this way (056fc874-dd7f-11ef-ba84-af6fca4b7289@cloud2.opencloud.test:10200):

    curl -k -u alan:demo -X POST \
      "https://cloud1.opencloud.test:9200/graph/v1beta1/drives/$driveid/items/$itemid/invite" \
      -H 'Content-Type: application/json' \
      -d '{
        "recipients": [{
          "objectId": "056fc874-dd7f-11ef-ba84-af6fca4b7289@cloud2.opencloud.test:10200", 
          "@libre.graph.recipient.type": "user"
        }],
        "roles": ["b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5"]
      }'
    {"error":{"code":"invalidRequest","innererror":{"date":"2026-08-17T22:31:34Z","request-id":"reborn/F78MY7iLkz-001533"},"message":"generalException: unexpected status code while getting accepted user: CODE_INTERNAL"}}
    

    But this way it works (056fc874-dd7f-11ef-ba84-af6fca4b7289@https://cloud2.opencloud.test:10200@cloud2.opencloud.test:10200):

    curl -k -u alan:demo -X POST \                                                                                         
      "https://cloud1.opencloud.test:9200/graph/v1beta1/drives/$driveid/items/$itemid/invite" \
      -H 'Content-Type: application/json' \
      -d '{
        "recipients": [{
          "objectId": "056fc874-dd7f-11ef-ba84-af6fca4b7289@https://cloud2.opencloud.test:10200@cloud2.opencloud.test:10200",
          "@libre.graph.recipient.type": "user"
        }],
        "roles": ["b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5"]
      }'
    {"value":[{"createdDateTime":"2026-08-18T00:24:21.788197662+02:00","grantedToV2":{"user":{"@libre.graph.userType":"Federated","displayName":"Mary Kenneth Keller","id":"056fc874-dd7f-11ef-ba84-af6fca4b7289@https://cloud2.opencloud.test:10200"}},"id":"277186b3-8bfa-4210-8abc-e1941a852c65","invitation":{"invitedBy":{"user":{"@libre.graph.userType":"Member","displayName":"Alan Turing","id":"b1f74ec4-dd7e-11ef-a543-03775734d0f7"}}},"roles":["b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5"]}]}
    
  4. VicDeo commented on Aug 18, 2026

    @VicDeo
    Contributor

    According to OCM spec OCM address is identifier@host[:port] with no scheme.
    The invite-accepted userID should be the bare identifier; the provider host travels separately in recipientProvider.

    So the solution would be store the opaque_id correctly
    (and probably fallback to the old format for the backward compatibility)

    docker exec 740bin-2-cloud2-1  cat /var/lib/opencloud/storage/ocm/ocminvites.json | jq
    {
      "File": "/var/lib/opencloud/storage/ocm/ocminvites.json",
      "invites": {},
      "accepted_users": {
        "056fc874-dd7f-11ef-ba84-af6fca4b7289": [
          {
            "id": {
              "idp": "cloud1.opencloud.test:9200",
              "opaque_id": "b1f74ec4-dd7e-11ef-a543-03775734d0f7@https://cloud1.opencloud.test:9200",
              "type": 6
            },
            "mail": "alan@example.org",
            "display_name": "Alan Turing"
          }
        ]
      }
    }
    
          `"opaque_id": "b1f74ec4-dd7e-11ef-a543-03775734d0f7@https://cloud1.opencloud.test:9200",`
    

    should be stored as
    "opaque_id": "b1f74ec4-dd7e-11ef-a543-03775734d0f7",

    The same applies to the user-id sent on the invite accept.

  5. self-assigned this
    on Aug 18, 2026
  6. patxixi commented on Aug 18, 2026

    @patxixi
    Author

    Thanks @VicDeo, this workaround unblocked me completely.

    For context: I'm running 3 separate OpenCloud instances (personal family setup, one primary user per instance plus a couple of secondary accounts) and hit the exact same CODE_INTERNAL error on POST /graph/v1beta1/drives/{driveid}/items/{itemid}/invite when trying to share a folder with an already-accepted OCM/federated user.

    Confirmed your finding and workaround work in my real-world setup, not just the reproduction case:

    • Doubling the idp suffix in objectId (user_id@idp@idp instead of the user_id@idp returned by /sciencemesh/find-accepted-users) makes the invite succeed every time.
    • One extra detail I ran into: the items/{itemId} segment in the invite URL needs to be the full driveId!itemId form, not just the bare item id — using just the item id returns invalid driveID or itemID (422).

    After applying both, I built out a real bidirectional test: a shared folder on one instance, invited out to accepted federated users on the other two, each side uploading a file and confirming it shows up for everyone else. Full sync worked correctly in both directions once the shares were created this way.

    Separate observation while testing, possibly worth its own issue: I got a share notification for a local share (same instance, two accounts on the same host) as expected, but got no notification at all for the OCM/federated shares. Haven't dug further into whether that's specific to my setup or a general gap in the federated-share notification path.

    Would be great to see the remote-user-id normalization fixed upstream so this doesn't need a workaround — happy to help verify a patch against my setup if useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions