Credentials & Auth (Cloud Drives + CloudKit)

This document explains how to obtain credentials for the providers that require OAuth / platform-specific keys:

iCloud Drive does not expose a public server-side file API. ManagedCode.Storage.CloudKit targets CloudKit app data, not iCloud Drive.

Overview

flowchart LR
  App[Your app] --> Auth[OAuth / Platform credentials]
  Auth --> SDK[Official SDK client]
  SDK --> Storage[ManagedCode.Storage provider]

General rules

OneDrive (Microsoft Graph / Entra ID)

What you need

Microsoft docs:

sequenceDiagram
  participant Admin as Admin
  participant Entra as Entra ID
  participant App as Your app
  participant Graph as Microsoft Graph
  Admin->>Entra: Register app + permissions
  App->>Entra: Get access token
  App->>Graph: Call Graph API

Typical steps (server-to-server)

  1. Create an Entra ID app registration.
  2. Add Microsoft Graph Application permissions (example: Files.ReadWrite.All or Sites.ReadWrite.All) and grant admin consent.
  3. Create a client secret and store it securely.

Minimal GraphServiceClient example (client credentials)

using Azure.Identity;
using Microsoft.Graph;

var tenantId = configuration["OneDrive:TenantId"]!;
var clientId = configuration["OneDrive:ClientId"]!;
var clientSecret = configuration["OneDrive:ClientSecret"]!;

var credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(credential, new[] { "https://graph.microsoft.com/.default" });

Minimal storage registration example

using ManagedCode.Storage.OneDrive.Extensions;

builder.Services.AddOneDriveStorageAsDefault(options =>
{
    options.GraphClient = graphClient;
    options.DriveId = "me";
    options.RootPath = "app-data";
});

Suggested configuration keys

Advanced (optional)

Google Drive (Google APIs)

What you need

Google docs:

sequenceDiagram
  participant Dev as Developer
  participant GCP as Google Cloud Console
  participant App as Your app
  participant Drive as Google Drive API
  Dev->>GCP: Enable Drive API + create credentials
  App->>Drive: Call Drive API via DriveService

Typical steps (service account)

  1. Enable the Google Drive API.
  2. Create a service account and download a JSON key.
  3. Share the target folder/drive with the service account email so it can access files.

Minimal DriveService example (service account)

using Google.Apis.Auth.OAuth2;
using Google.Apis.Drive.v3;
using Google.Apis.Services;

var serviceAccountJsonPath = configuration["GoogleDrive:ServiceAccountJsonPath"]!;

var credential = GoogleCredential
    .FromFile(serviceAccountJsonPath)
    .CreateScoped(DriveService.Scope.Drive);

var driveService = new DriveService(new BaseClientService.Initializer
{
    HttpClientInitializer = credential,
    ApplicationName = "MyApp"
});

Minimal storage registration example

using ManagedCode.Storage.GoogleDrive.Extensions;

builder.Services.AddGoogleDriveStorageAsDefault(options =>
{
    options.DriveService = driveService;
    options.RootFolderId = configuration["GoogleDrive:RootFolderId"] ?? "root";
});

Suggested configuration keys

Advanced (optional)

Dropbox

What you need

Dropbox docs:

sequenceDiagram
  participant Dev as Developer
  participant Console as Dropbox App Console
  participant User as User
  participant App as Your app
  participant Dropbox as Dropbox API
  Dev->>Console: Create app + scopes
  User->>App: OAuth authorize
  App->>Dropbox: Exchange code for tokens
  App->>Dropbox: Use access/refresh token for API calls

Typical steps

  1. Create an app in the Dropbox App Console (Scoped access, Full Dropbox or App folder).
  2. Enable required scopes (example: files.content.read, files.content.write, files.metadata.read, files.metadata.write).
  3. Obtain a token:
    • quick: generate an access token in the app console
    • production: use OAuth code flow to obtain an access token + refresh token

Minimal storage registration example

Access token (simple):

using ManagedCode.Storage.Dropbox.Extensions;

builder.Services.AddDropboxStorageAsDefault(options =>
{
    options.AccessToken = configuration["Dropbox:AccessToken"];
    options.RootPath = "/Apps/my-app";
});

Refresh token (recommended for production/offline access):

using ManagedCode.Storage.Dropbox.Extensions;

builder.Services.AddDropboxStorageAsDefault(options =>
{
    options.RefreshToken = configuration["Dropbox:RefreshToken"];
    options.AppKey = configuration["Dropbox:AppKey"];
    options.AppSecret = configuration["Dropbox:AppSecret"]; // optional in PKCE flows
    options.RootPath = "/Apps/my-app";
});

Suggested configuration keys

Advanced (optional)

CloudKit (iCloud app data)

What you need

Apple docs:

sequenceDiagram
  participant Dev as Developer
  participant Apple as Apple Developer / CloudKit Dashboard
  participant App as Your app
  participant CK as CloudKit Web Services
  Dev->>Apple: Configure container + schema + token/key
  App->>CK: Signed/API-token request
  CK-->>App: Record + Asset URL

Typical steps

  1. Configure the container in CloudKit Dashboard and note the container id.
  2. Ensure the record type exists (default MCStorageFile) and add fields:
    • path (String, queryable/indexed)
    • contentType (String)
    • file (Asset)
  3. Create an API token for the container (or server-to-server key) and store it securely.

Minimal storage registration example

using ManagedCode.Storage.CloudKit.Extensions;
using ManagedCode.Storage.CloudKit.Options;

builder.Services.AddCloudKitStorageAsDefault(options =>
{
    options.ContainerId = configuration["CloudKit:ContainerId"]!; // identifier, not a secret
    options.Environment = CloudKitEnvironment.Production;
    options.Database = CloudKitDatabase.Public;
    options.RootPath = configuration["CloudKit:RootPath"] ?? "app-data";

    // Choose ONE auth mode:
    options.ApiToken = configuration["CloudKit:ApiToken"];
    // OR:
    // options.ServerToServerKeyId = configuration["CloudKit:KeyId"];
    // options.ServerToServerPrivateKeyPem = configuration["CloudKit:PrivateKeyPem"];
});

“App-specific passwords” note

Apple “app-specific passwords” are intended for legacy protocols (e.g., Mail) and do not provide a supported, server-side “iCloud Drive” file API. For server-side storage, the supported option is CloudKit Web Services (this provider) — see ADR 0001 for the iCloud Drive vs CloudKit decision.

Web auth token note (ckWebAuthToken)

If you use ckWebAuthToken (typically for user-scoped/private DB scenarios), CloudKit treats it as single-use and returns a rotated token on each response. The next request must use the new token.

ManagedCode.Storage.CloudKit will update CloudKitStorageOptions.WebAuthToken when a rotated token is returned, and serializes CloudKit requests when WebAuthToken is set (to avoid concurrent requests using an invalidated token).

Suggested configuration keys

Advanced (optional)

If you need to route/inspect CloudKit Web Services calls (proxy, custom handler in tests), you can provide either:

Example:

var cloudKitHttpClient = new HttpClient
{
    Timeout = TimeSpan.FromSeconds(100)
};

builder.Services.AddCloudKitStorageAsDefault(options =>
{
    options.ContainerId = configuration["CloudKit:ContainerId"]!;
    options.Environment = CloudKitEnvironment.Production;
    options.Database = CloudKitDatabase.Public;

    options.ApiToken = configuration["CloudKit:ApiToken"];
    options.HttpClient = cloudKitHttpClient;
});