Status update 2026-10

2026-10-26 #oauth #pimsync #status-update

I’ve completed development work on OAuth support in pimsync. The implementation has been tested primary with NextCloud (both its built-in OAuth provider and the OIDC plugin), but should be usable with any CalDAV/CardDAV server using OAuth for authentication. It should also work with Google’s flavour of CalDAV, although this has yet to be tested.

At this point, mostly the documentation is missing — the reference-level documentation is there, but a friendlier “how to” guide covering all the details is still pending. It’s both definitely planned and quite necessary.

Implementation details

I wrote a sans-io OAuth library in Rust, liboauth, which is essentially a state-machine for the whole authentication process. This is usable with any Rust HTTP client, including hyper, which is the HTTP implementation of choice for pimsync.

liboauth v0.1.0 has been published to crates.io, and its integration tests use NextCloud as a server. It’s possible that it might need extra tweaking as its used with more OAuth implementations in the wild, but it’s perfectly usable to any other kind of software.

I also wrote a tower/hyper middleware using liboauth. This is currently in the pimsync repository: it’s not suitable for upstream since upstream is in a state of flux, where the older client API is deprecated and won’t receive further changes, but the newer client API does not exist. I hope to upstream this when the situation evolves along with the digest auth middleware..

The authentication settings are separate from the regular storage configuration, in a separate credential block. This is to (a) avoid overwhelming the storage configurations with so many directives, and (b) make it easy to re-use the same credentials for two storages (e.g.: CalDAV and CardDAV).

A credentials block looks something like the following:

    credentials nextcloud {
        auth_method oauth
        registration_url https://dav.example.com/oidc/registration
        token_url https://dav.example.com/oidc/token
        scope openid
        scope offline_access
        tokens_file ~/.local/state/pimsync/oauth/dav
        encryption_key {
            cmd hiq -dFpassword proto=pimsync-tokens
        }
    }

When a server doesn’t support dynamic client registration a few more settings are required:

credentials nextcloud {
    auth_method oauth
    authorization_url https://nextcloud.example.com/authorize
    token_url https://nextcloud.example.com/token
    client_id my-client
    client_secret {
        cmd hiq -dFpassword proto=carddavs oauth-client
    }
    scope calendar
    scope contacts
    pkce off
    tokens_file ~/.local/state/pimsync/oauth/nextcloud
    encryption_key {
        cmd hiq -dFpassword proto=pimsync-tokens
    }
}

The implementation also enables using OAuth + JMAP, although this path has not yet been tested — feedback for it is most welcome.

Token storage

OAuth tokens expire, and need to be automatically renewed, This can happen at any time, and pimsync needs to able to store this. The obvious approach would seem to be writing into the local of secret storage. Writing into such a storage would happen at seemingly random times during the day, while the computer is likely being being used for unrelated tasks. An unexpected pop-up to overwrite values in a secret store at times would be extremely annoying.

Some “YOLO-style” secret storage services allow any process to overwrite (and read!) any secret without any user confirmation. This kind of setup would not suffer from the same issue, but supporting only those would be irresponsible.

As a solution, the tokens are stored encrypted (using ChaCha20-Poly1305) on disk using the secret provided by encryption_key. This key is read only once at start-up, and would typically be in a secret storage (but can be in a plain-text file if desirable).

Missing details

Using a custom TLS configuration for OAuth is not implemented. pimsync could also have better integration tests for OAuth discovery. A tutorial-style documentation, as mentioned above.

Improved logging

Pimsync now logs (at TRACE level) all HTTP requests made to all storage types (including JMAP). This was 80% there already, and finishing the last remaining bit should greatly help debugging failures.

Have comments or want to discuss this topic?
Send an email to my public inbox: ~whynothugo/public-inbox@lists.sr.ht.
Reply privately by email: hugo@whynothugo.nl.

— § —