Files
nocal/include/nocal/sync/microsoft_graph.hpp
Bernardo Magri 74c798e7aa feat(sync): add Microsoft account connect CLI
nocal account connect microsoft runs the PKCE browser flow, reads /me,
upserts the deterministic account row, and persists tokens to Secret
Service as the final connected-state gate. Account IDs are hashed from
the remote subject so reconnect is idempotent, and a secret-store
failure leaves the same coherent state as a disconnect. Ships a
placeholder client ID with NOCAL_MICROSOFT_CLIENT_ID override until a
real Entra registration exists.
2026-07-21 18:44:47 +01:00

78 lines
3.0 KiB
C++

#pragma once
#include "nocal/storage/sync_cache.hpp"
#include "nocal/sync/http.hpp"
#include <cstdint>
#include <stdexcept>
#include <string>
#include <string_view>
namespace nocal::sync {
// Per-operation credentials for Microsoft Graph. The adapter never retains the
// access token after the public operation returns.
struct MicrosoftGraphCredentials {
std::string account_id;
std::string access_token;
};
// A half-open UTC calendar window. Values are Unix epoch microseconds and are
// serialized canonically before they become part of a durable checkpoint.
struct MicrosoftGraphWindow {
std::int64_t start_epoch_microseconds{0};
std::int64_t end_epoch_microseconds{0};
};
class MicrosoftGraphError : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
};
// Validated /me identity. Fetched without persisting anything so account
// connect can derive the deterministic local account id before any cache write.
struct MicrosoftGraphIdentity {
std::string remote_subject;
std::string display_name;
};
// Read-only Microsoft Graph v1.0 synchronization. Calendar discovery covers
// every calendar returned by /me/calendars. Stable v1.0 event delta is used for
// the primary calendar and complete calendarView reconciliation for secondary
// calendars; this class does not call beta or undocumented delta routes.
class MicrosoftGraphSync {
public:
MicrosoftGraphSync(HttpTransport& transport, storage::SyncCache& cache);
// Requires delegated User.Read. Reads /me and returns the validated
// identity without touching the cache; the access token is not retained.
[[nodiscard]] MicrosoftGraphIdentity fetch_identity(std::string_view access_token) const;
// Requires delegated User.Read. A complete and validated /me response is
// persisted atomically; failures leave the cached account unchanged.
storage::CachedAccount refresh_account(const MicrosoftGraphCredentials& credentials);
// Requires delegated Calendars.Read. Pagination is completed before the
// cache listing is replaced, so a partial listing never deactivates data.
void refresh_calendars(const MicrosoftGraphCredentials& credentials);
// Requires delegated Calendars.Read. Each validated page and its opaque
// next/delta URL are committed in one cache transaction. A later-page
// failure therefore resumes from the last committed cursor. Only HTTPS
// continuations on graph.microsoft.com are accepted.
void sync_primary_calendar_window(const MicrosoftGraphCredentials& credentials,
const MicrosoftGraphWindow& window);
// Reads every page of the documented Graph v1.0 calendarView for one
// active, non-primary calendar. The cache window is replaced only after the
// listing completes, so a failed or malformed page leaves it unchanged.
void sync_secondary_calendar_window(const MicrosoftGraphCredentials& credentials,
std::string calendar_id, const MicrosoftGraphWindow& window);
private:
HttpTransport& transport_;
storage::SyncCache& cache_;
};
} // namespace nocal::sync