AuthServer
Calendar Integration
Google Calendar and Microsoft 365 calendar connections with connection-only, read-pull, and two-way modes.
SqlOS stores calendar OAuth tokens encrypted at rest, refreshes them automatically, and optionally imports normalized events — per user or per organization. Provider credentials are reused from your seeded Google / Microsoft social connections; sign-in flows never request calendar scopes.
For a walkthrough, see the calendar integration guide.
public enum SqlOSCalendarIntegrationMode
{
ConnectionOnly, // SqlOS stores tokens; your app calls Google/Graph directly
ReadPull, // SqlOS imports events/busy blocks on a schedule
TwoWay // SqlOS pushes app-created events and pulls changes
}ConnectionOnly never persists event copies. ReadPull and TwoWay require a provider refresh token; the connect flow fails fast when one is not granted.
| Provider | Read | Two-way writes |
|---|---|---|
https://www.googleapis.com/auth/calendar.readonly | + https://www.googleapis.com/auth/calendar.events | |
| Microsoft | offline_access, Calendars.Read | offline_access, Calendars.ReadWrite |
SqlOS adds openid email so the granting account is recorded, sends access_type=offline&prompt=consent to Google, and accepts a custom scope list via SqlOSStartCalendarConnectRequest.Scopes.
SqlOSCalendarService.StartConnectAsync(...) with the OIDC connection id, mode, owner (UserId or OrganizationId), and an absolute ReturnUri.AuthorizationUrl (PKCE + single-use state, Calendar.ConnectSessionLifetime window).{AuthServer.BasePath}/calendar/callback.ReturnUri with ?calendarConnectionId=... or ?error=....Add the callback URI to the provider app registration:
https://your-app.example.com/sqlos/auth/calendar/callbackSqlOSCalendarService (scoped, registered by AddSqlOS):
StartConnectAsync / HandleConnectCallbackAsync / CompleteConnectAsync — connect flowListConnectionsAsync, GetConnectionAsync — summaries (never token material)GetAccessTokenAsync — short-lived provider token, transparently refreshed within Calendar.AccessTokenRefreshSkewListProviderCalendarsAsync — live calendar list from the providerEnableCalendarSyncAsync — choose which calendars a connection syncsListEventsAsync — normalized events (read-pull/two-way only)DisconnectAsync — revokes and clears stored tokensForceRefreshAsync — admin force refreshSqlOSCalendarSyncService:
SyncConnectionAsync — pull one connection now (auto-enrolls the primary calendar on first sync)CreateEventAsync — two-way event creation with a local push copySyncDueConnectionsAsync — used by the background schedulerAll owner-scoped reads accept forUserId / forOrganizationId and fail closed when the connection belongs to someone else.
builder.AddSqlOS<AppDbContext>(db => db.UseSqlServer(cs), options =>
{
options.ConfigureCalendar(calendar =>
{
calendar.SyncWindowPastDays = 30; // read-pull import window
calendar.SyncWindowFutureDays = 90;
calendar.AccessTokenRefreshSkew = TimeSpan.FromMinutes(5);
calendar.ConfigureSyncScheduler(scheduler =>
{
scheduler.Enabled = true; // background sync worker
scheduler.Interval = TimeSpan.FromMinutes(5);
scheduler.SyncEvery = TimeSpan.FromMinutes(15);
scheduler.MaxConnectionsPerRun = 25;
});
calendar.OnTwoWayConflictAsync = (conflict, ct) =>
Task.FromResult(SqlOSCalendarConflictDecision.PreferProvider);
});
});Set options.Calendar.Enabled = false to skip mapping the callback and admin endpoints entirely.
syncToken, Microsoft Graph delta links. Expired cursors fall back to a full-window pull automatically.OnTwoWayConflictAsync; the provider version wins by default.LastSyncStatus, LastSyncError, EventCount) and the connection status (Active/Error).Dashboard: /sqlos/admin/calendar/connections — status, owner, granted account, last sync, per-calendar cursor health, plus force sync, token refresh, and disconnect actions.
Admin API (dashboard-session guarded, mirrors the other admin surfaces):
GET /sqlos/admin/calendar/api/connections?search=&includeRevoked=&page=&pageSize=
GET /sqlos/admin/calendar/api/connections/{id}
POST /sqlos/admin/calendar/api/connections/{id}/sync
POST /sqlos/admin/calendar/api/connections/{id}/refresh
POST /sqlos/admin/calendar/api/connections/{id}/disconnect
GET /sqlos/admin/calendar/api/summarycalendar.connect.start, calendar.connection.created, calendar.connect.error, calendar.connection.synced, calendar.sync.failed, calendar.connection.refresh_failed, calendar.connection.disconnected, calendar.event.created.
Migration 026 adds SqlOSCalendarConnections (encrypted tokens, mode, owner, status), SqlOSCalendarSyncStates (per-calendar cursors and health), and SqlOSCalendarEvents (normalized copies, unique per provider event).
prompt=consent, so this usually means a Workspace policy stripped it, or a custom scope list dropped offline_access (Microsoft).Error — the last refresh or sync failed; see LastError in the dashboard, fix the underlying grant, then use Refresh token or reconnect.