From 1bb065ade078978086e170feacf12c65681b5b90 Mon Sep 17 00:00:00 2001 From: Harivansh Rathi Date: Wed, 1 Apr 2026 17:24:47 -0400 Subject: [PATCH] Shorten mount credential username token to fix macOS WebDAV mount macOS WebDAVFS truncates HTTP Basic Auth passwords at 255 bytes. The mount credential password (base64 payload + HMAC signature) was 260 bytes because the full username token (32 random bytes / 43 base64url chars) is embedded in the signed payload. Reduce the username token from 32 bytes to 24 bytes (32 base64url chars), bringing the total password to ~246 chars with margin for longer node IDs and mount paths. --- .../cmd/control-plane/mount_credentials.go | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/apps/control-plane/cmd/control-plane/mount_credentials.go b/apps/control-plane/cmd/control-plane/mount_credentials.go index 0ad9a28..c7b79ee 100644 --- a/apps/control-plane/cmd/control-plane/mount_credentials.go +++ b/apps/control-plane/cmd/control-plane/mount_credentials.go @@ -2,6 +2,7 @@ package main import ( "crypto/hmac" + "crypto/rand" "crypto/sha256" "encoding/base64" "encoding/json" @@ -11,6 +12,15 @@ import ( const mountCredentialModeBasicAuth = "basic-auth" +// mountCredentialUsernameTokenBytes controls the random token size in mount +// credential usernames (e.g. "mount-"). The username is also embedded +// inside the signed password payload, so longer tokens produce longer +// passwords. macOS WebDAVFS truncates Basic Auth passwords at 255 bytes, +// which corrupts the HMAC signature and causes auth failures. 24 bytes +// (32 base64url chars) keeps the total password under 250 characters with +// margin for longer node IDs and mount paths. +const mountCredentialUsernameTokenBytes = 24 + type signedMountCredentialClaims struct { Version int `json:"v"` NodeID string `json:"nodeId"` @@ -26,7 +36,7 @@ func issueMountCredential(secret string, nodeID string, mountPath string, readon return "", mountCredential{}, err } - usernameToken, err := newOpaqueToken() + usernameToken, err := newMountCredentialUsernameToken() if err != nil { return "", mountCredential{}, err } @@ -53,6 +63,14 @@ func issueMountCredential(secret string, nodeID string, mountPath string, readon }, nil } +func newMountCredentialUsernameToken() (string, error) { + raw := make([]byte, mountCredentialUsernameTokenBytes) + if _, err := rand.Read(raw); err != nil { + return "", fmt.Errorf("generate mount credential username token: %w", err) + } + return base64.RawURLEncoding.EncodeToString(raw), nil +} + func signMountCredentialClaims(secret string, claims signedMountCredentialClaims) (string, error) { payload, err := json.Marshal(claims) if err != nil {