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.
This commit is contained in:
Harivansh Rathi 2026-04-01 17:24:47 -04:00
parent a6c74c2a39
commit 1bb065ade0

View file

@ -2,6 +2,7 @@ package main
import ( import (
"crypto/hmac" "crypto/hmac"
"crypto/rand"
"crypto/sha256" "crypto/sha256"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
@ -11,6 +12,15 @@ import (
const mountCredentialModeBasicAuth = "basic-auth" const mountCredentialModeBasicAuth = "basic-auth"
// mountCredentialUsernameTokenBytes controls the random token size in mount
// credential usernames (e.g. "mount-<token>"). 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 { type signedMountCredentialClaims struct {
Version int `json:"v"` Version int `json:"v"`
NodeID string `json:"nodeId"` NodeID string `json:"nodeId"`
@ -26,7 +36,7 @@ func issueMountCredential(secret string, nodeID string, mountPath string, readon
return "", mountCredential{}, err return "", mountCredential{}, err
} }
usernameToken, err := newOpaqueToken() usernameToken, err := newMountCredentialUsernameToken()
if err != nil { if err != nil {
return "", mountCredential{}, err return "", mountCredential{}, err
} }
@ -53,6 +63,14 @@ func issueMountCredential(secret string, nodeID string, mountPath string, readon
}, nil }, 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) { func signMountCredentialClaims(secret string, claims signedMountCredentialClaims) (string, error) {
payload, err := json.Marshal(claims) payload, err := json.Marshal(claims)
if err != nil { if err != nil {