Skip to content

auth-basic with Active Directory produces byte-swapped objectGUID as opaque_id, causing GetUserGroups to fail #2872

@jordivandijk

Description

@jordivandijk

Bug: auth-basic with Active Directory produces byte-swapped objectGUID as opaque_id, causing GetUserGroups to fail

Describe the bug

When authenticating via WebDAV using Basic Auth against an Active Directory LDAP backend, the auth-basic service successfully finds and binds the user by sAMAccountName, but then fails at the GetUserGroups step with grpc getting user groups failed: 'error getting user groups', resulting in error code 15 (UNAUTHENTICATED).

The root cause is a byte order mismatch in how auth-basic constructs the user's opaque_id from the raw AD objectGUID bytes after a successful LDAP bind. The endianness fix introduced in #1901 (for the OIDC/web login path) does not appear to be applied in the auth-basic code path.

Steps to reproduce

  1. Configure OpenCloud with an external Active Directory as LDAP backend and external Keycloak as OIDC IDP, using objectGUID as the user ID attribute (OC_LDAP_USER_SCHEMA_ID=objectGUID, OC_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING=true).
  2. Verify that web UI login via Keycloak OIDC works correctly.
  3. Attempt to authenticate a WebDAV connection from an external client (e.g. Windows) using the AD username and password via Basic Auth.

Expected behavior

Basic Auth with a valid AD username and password should authenticate successfully, allowing WebDAV access from external clients.

Actual behavior

auth-basic finds the user in AD by sAMAccountName and the LDAP bind succeeds, but the subsequent GetUserGroups call fails with not found. Authentication is rejected with code 15.

The root cause is that auth-basic constructs the user's opaque_id from the raw AD objectGUID bytes without applying the little-endian to UUID string conversion that AD requires for the first three UUID segments. This produces a byte-swapped opaque_id.

Example: A user whose objectGUID is correctly expressed as ba94e17e-da1a-44ba-a365-2c02a825d886 gets assigned the opaque_id 7ee194ba-1ada-ba44-a365-2c02a825d886 (byte-swapped). The GetUserGroups call then searches LDAP using this wrong ID and returns not found.

Relevant log sequence:

# auth-basic finds and binds the user successfully
{"service":"auth-basic","filter":"(&(objectclass=person)(|(sAMAccountName=<username>)))","message":"LDAP Search"}

# GetUserGroups is called with the byte-swapped opaque_id
{"service":"users","filter":"(&(objectclass=person)(objectGUID=\\ba\\94\\e1\\7e\\da\\1a\\44\\ba\\a3\\65\\2c\\02\\a8\\25\\d8\\86))","message":"LDAP Search"}

# Lookup fails because the opaque_id used is byte-swapped
{"service":"users","error":"error: not found: (&(objectclass=person)(objectGUID=\\ba\\94\\e1\\7e...))","userid":{"opaque_id":"7ee194ba-1ada-ba44-a365-2c02a825d886"},"message":"Failed to lookup user"}

# Final failure
{"service":"auth-basic","error":"ldap: grpc getting user groups failed: 'error getting user groups'","message":"authsvc: error in Authenticate"}
{"service":"proxy","error":"could not authenticate with username and password user: <username>, got code: 15","message":"failed to authenticate request"}

Setup

OpenCloud 7.0.0 rolling (2026-05-21), deployed via Docker Compose with external Active Directory (Windows Server) and Keycloak 26.6.0 as OIDC IDP.

Details

OC_LDAP_URI=ldap://ad.example.com:389
OC_LDAP_INSECURE=true
OC_LDAP_BIND_DN=serviceaccount@example.com
OC_LDAP_USER_BASE_DN=OU=Users,DC=example,DC=com
OC_LDAP_USER_SCHEMA_ID=objectGUID
OC_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING=true
OC_LDAP_USER_OBJECTCLASS=person
OC_LDAP_USER_SCHEMA_USERNAME=sAMAccountName
OC_LDAP_GROUP_BASE_DN=OU=OpenCloud,OU=Groups,DC=example,DC=com
OC_LDAP_GROUP_SCHEMA_ID=cn
OC_LDAP_GROUP_SCHEMA_ID_IS_OCTETSTRING=false
OC_LDAP_GROUP_OBJECTCLASS=group
OC_LDAP_SERVER_WRITE_ENABLED=false
OC_EXCLUDE_RUN_SERVICES=idm,idp,search
PROXY_USER_OIDC_CLAIM=uuid
PROXY_USER_CS3_CLAIM=userid
PROXY_AUTOPROVISION_ACCOUNTS=false
USERS_LDAP_URI=ldap://ad.example.com:389
USERS_LDAP_USER_SCHEMA_ID=objectGUID
USERS_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING=true
USERS_LDAP_USER_SCHEMA_USERNAME=sAMAccountName
AUTH_APP_CS3_CLAIM=userid
OC_OIDC_ISSUER=https://keycloak.example.com/realms/openCloud

Additional context

  • Web UI login via Keycloak OIDC works correctly — the endianness fix in handle objectguid endianess #1901 applies to this path.
  • WebDAV Basic Auth fails — the same endianness fix is not applied when auth-basic constructs the opaque_id from the raw objectGUID bytes after a successful LDAP bind.
  • The Keycloak token contains the correct UUID string (e.g. ba94e17e-da1a-44ba-a365-2c02a825d886), while auth-basic produces a byte-swapped opaque_id (e.g. 7ee194ba-1ada-ba44-a365-2c02a825d886). These are the same objectGUID with the first three segments' bytes reversed, which is the classic Windows AD little-endian encoding issue.
  • No workaround has been found that does not require either patching reva or restructuring the entire user ID scheme away from objectGUID.
  • A secondary issue exists where auth-app always calls GetUserByClaim(claim="username") regardless of the AUTH_APP_CS3_CLAIM=userid setting, causing app token authentication to also fail — but that is a separate bug.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Qualification

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions