CMS Integration API

**Referenced Files in This Document** - [worker.js](file://worker.js) - [wrangler.jsonc](file://wrangler.jsonc) - [README.md](file://README.md) - [src/admin/config.yml](file://src/admin/config.yml) - [src/admin/index.html](file://src/admin/index.html) - [package.json](file://package.json) - [netlify.toml](file://netlify.toml) - [cloudflare-pages.toml](file://cloudflare-pages.toml)

Table of Contents

  1. Introduction
  2. Project Structure
  3. Core Components
  4. Architecture Overview
  5. Detailed Component Analysis
  6. Dependency Analysis
  7. Performance Considerations
  8. Troubleshooting Guide
  9. Conclusion
  10. Appendices

Introduction

This document provides comprehensive API documentation for the GitHub OAuth integration used by Sveltia CMS within the project. It explains the /api/auth and /api/auth/callback endpoints, GitHub OAuth configuration, redirect URI handling, CORS preflight support, token exchange with GitHub, secure token transmission to the CMS, and integration with the CMS admin interface. It also covers security considerations, error handling, and troubleshooting guidance.

Project Structure

The CMS integration relies on a Cloudflare Worker that serves static assets and exposes authentication endpoints. The Sveltia CMS admin is served from the static assets and communicates with the Worker for authentication.

graph TB
subgraph "Static Site (_site)"
AdminUI["/admin/index.html<br/>Sveltia CMS entry"]
ConfigYml["/admin/config.yml<br/>CMS schema"]
end
subgraph "Cloudflare Worker"
FetchHandler["Fetch handler<br/>routes requests"]
OAuthStart["/api/auth (GET)<br/>OAuth start"]
OAuthCallback["/api/auth/callback (GET)<br/>OAuth callback"]
CorsPreflight["OPTIONS *<br/>CORS preflight"]
end
Browser["Browser"]
GitHub["GitHub OAuth API"]
Browser --> AdminUI
AdminUI --> FetchHandler
FetchHandler --> OAuthStart
OAuthStart --> GitHub
Browser --> OAuthCallback
OAuthCallback --> FetchHandler
FetchHandler --> CorsPreflight

Diagram sources

  • [worker.js:301-320](file://worker.js#L301-L320)
  • [worker.js:183-191](file://worker.js#L183-L191)
  • [worker.js:193-198](file://worker.js#L193-L198)
  • [worker.js:200-227](file://worker.js#L200-L227)
  • [src/admin/index.html:1-12](file://src/admin/index.html#L1-L12)
  • [src/admin/config.yml:1-5](file://src/admin/config.yml#L1-L5)

Section sources

  • [worker.js:301-320](file://worker.js#L301-L320)
  • [src/admin/index.html:1-12](file://src/admin/index.html#L1-L12)
  • [src/admin/config.yml:1-5](file://src/admin/config.yml#L1-L5)

Core Components

  • Worker fetch handler: Routes requests to appropriate handlers or serves static assets.
  • OAuth start handler (/api/auth): Initiates GitHub OAuth authorization.
  • OAuth callback handler (/api/auth/callback): Exchanges authorization code for an access token and posts it to the opener window.
  • CORS preflight handler (OPTIONS): Supports browser preflight for cross-origin authentication.
  • Sveltia CMS admin: Loads the CMS UI and integrates with the Worker for authentication.

Section sources

  • [worker.js:301-320](file://worker.js#L301-L320)
  • [worker.js:183-191](file://worker.js#L183-L191)
  • [worker.js:193-198](file://worker.js#L193-L198)
  • [worker.js:200-227](file://worker.js#L200-L227)
  • [src/admin/index.html:1-12](file://src/admin/index.html#L1-L12)

Architecture Overview

The CMS admin loads in the browser and communicates with the Worker to initiate GitHub OAuth. The Worker redirects to GitHub, receives the callback, exchanges the code for a token, and posts the token back to the CMS via postMessage.

sequenceDiagram
participant Browser as "Browser"
participant Admin as "Sveltia CMS Admin"
participant Worker as "Cloudflare Worker"
participant GitHub as "GitHub OAuth"
Browser->>Admin : Load /admin/
Admin->>Worker : GET /api/auth (initiate OAuth)
Worker->>GitHub : Redirect to authorize
GitHub-->>Browser : Redirect to /api/auth/callback?code=...
Browser->>Worker : GET /api/auth/callback
Worker->>GitHub : POST /login/oauth/access_token
GitHub-->>Worker : {access_token}
Worker-->>Admin : HTML with script<br/>window.opener.postMessage({provider : 'github', token})
Admin-->>Admin : Receive token via postMessage<br/>Proceed to CMS editor

Diagram sources

  • [worker.js:193-198](file://worker.js#L193-L198)
  • [worker.js:200-227](file://worker.js#L200-L227)
  • [src/admin/index.html:1-12](file://src/admin/index.html#L1-L12)

Detailed Component Analysis

/api/auth (GET) — OAuth Start

  • Purpose: Initiate GitHub OAuth authorization.
  • Behavior:
    • Constructs the GitHub authorization URL with client_id, redirect_uri, and requested scopes.
    • Redirects the browser to GitHub.
  • Redirect URI:
    • Dynamically computed from the incoming request origin to ensure correctness across environments.
  • CORS:
    • Intended for browser preflight; actual OAuth start is a redirect.

Section sources

  • [worker.js:193-198](file://worker.js#L193-L198)

/api/auth/callback (GET) — OAuth Callback

  • Purpose: Handle the OAuth callback, exchange the authorization code for an access token, and securely transmit it to the CMS.
  • Behavior:
    • Validates presence of the authorization code.
    • Calls GitHub’s token endpoint to exchange code for an access token.
    • On success, returns an HTML page containing a script that posts the token to the opener window using postMessage.
    • On failure, returns an error response with appropriate status codes.
  • Security:
    • Uses postMessage to transfer the token to the opener window, avoiding exposure in URL fragments.
    • The target origin is derived from the request origin, ensuring the message is sent to the correct location.
flowchart TD
Start(["/api/auth/callback"]) --> GetCode["Extract 'code' from query params"]
GetCode --> HasCode{"Has 'code'?"}
HasCode --> |No| Err400["Return 400: Missing code parameter"]
HasCode --> |Yes| Exchange["POST to GitHub /login/oauth/access_token"]
Exchange --> RespOK{"Response OK?"}
RespOK --> |No| Err400Bis["Return 400: GitHub error"]
RespOK --> |Yes| PostMsg["Return HTML with script<br/>postMessage(access_token,<br/>origin)"]
Err400 --> End(["Exit"])
Err400Bis --> End
PostMsg --> End

Diagram sources

  • [worker.js:200-227](file://worker.js#L200-L227)

Section sources

  • [worker.js:200-227](file://worker.js#L200-L227)

CORS Preflight Handling (OPTIONS)

  • Purpose: Support browser preflight requests for cross-origin authentication.
  • Behavior:
    • Returns headers allowing GET, POST, OPTIONS and Content-Type.
    • Limits Access-Control-Allow-Origin to the expected origin to mitigate CSRF risks.

Section sources

  • [worker.js:183-191](file://worker.js#L183-L191)

Sveltia CMS Integration

  • Admin entry point: The CMS admin is loaded from the static assets at /admin/.
  • Configuration: The CMS schema is defined in config.yml, specifying the backend and content collections.
  • Communication pattern: The CMS uses postMessage to receive the token from the Worker after successful OAuth.

Section sources

  • [src/admin/index.html:1-12](file://src/admin/index.html#L1-L12)
  • [src/admin/config.yml:1-5](file://src/admin/config.yml#L1-L5)

GitHub OAuth Configuration Requirements

  • Client ID and Secret:
    • The Worker expects GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET in environment variables/secrets.
    • These are used during the token exchange with GitHub.
  • Redirect URI:
    • The Worker constructs the redirect_uri dynamically from the request origin, ensuring it matches the registered GitHub App redirect URI.
  • Scopes:
    • The authorization request requests repo and user scopes.

Section sources

  • [worker.js:195-196](file://worker.js#L195-L196)
  • [worker.js:208-212](file://worker.js#L208-L212)
  • [wrangler.jsonc:31-33](file://wrangler.jsonc#L31-L33)

Token Transmission to CMS

  • Mechanism:
    • The Worker returns an HTML page with a script that calls window.opener.postMessage with provider and token.
    • The opener is the CMS window that initiated the OAuth flow.
  • Origin validation:
    • The postMessage targetOrigin is set to the request origin, ensuring the token is only sent to the correct origin.

Section sources

  • [worker.js:217-222](file://worker.js#L217-L222)

Error Handling

  • Missing code parameter:
    • Returns 400 with a descriptive message.
  • GitHub API errors:
    • Returns 400 with the error description from GitHub.
  • Unexpected errors:
    • Returns 500 with a generic error message.

Section sources

  • [worker.js:201-202](file://worker.js#L201-L202)
  • [worker.js:214-215](file://worker.js#L214-L215)
  • [worker.js:224-226](file://worker.js#L224-L226)

Dependency Analysis

  • Worker-to-GitHub:
    • The Worker calls GitHub’s authorization and token endpoints.
  • Worker-to-Browser:
    • Uses postMessage to communicate the token to the opener window.
  • CMS-to-Worker:
    • The CMS initiates OAuth by requesting /api/auth and receives the token via postMessage after /api/auth/callback.
graph LR
Browser["Browser"]
Worker["Cloudflare Worker"]
GitHub["GitHub OAuth"]
CMS["Sveltia CMS Admin"]
Browser --> CMS
CMS --> Worker
Worker --> GitHub
GitHub --> Worker
Worker --> CMS

Diagram sources

  • [worker.js:193-198](file://worker.js#L193-L198)
  • [worker.js:200-227](file://worker.js#L200-L227)
  • [src/admin/index.html:1-12](file://src/admin/index.html#L1-L12)

Section sources

  • [worker.js:193-198](file://worker.js#L193-L198)
  • [worker.js:200-227](file://worker.js#L200-L227)
  • [src/admin/index.html:1-12](file://src/admin/index.html#L1-L12)

Performance Considerations

  • Token exchange latency:
    • The token exchange involves an external API call; keep the number of round-trips minimal by handling the exchange server-side.
  • Static asset delivery:
    • The Worker proxies unmatched routes to static assets, ensuring efficient delivery of the CMS UI and associated resources.

Troubleshooting Guide

  • OAuth fails with “Missing code parameter”:
    • Ensure the callback URL matches the registered GitHub App redirect URI and that the browser reaches /api/auth/callback with the code query parameter.
  • OAuth fails with a GitHub error:
    • Verify GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET are set in the Worker environment.
  • Token not received by CMS:
    • Confirm the opener window exists and that postMessage is targeting the correct origin.
  • CORS preflight issues:
    • Ensure the browser sends OPTIONS preflight and that the Worker responds with appropriate Allow-Origin and Allow-Headers.

Section sources

  • [worker.js:201-202](file://worker.js#L201-L202)
  • [worker.js:214-215](file://worker.js#L214-L215)
  • [worker.js:183-191](file://worker.js#L183-L191)
  • [worker.js:217-222](file://worker.js#L217-L222)

Conclusion

The CMS Integration API leverages a Cloudflare Worker to provide a secure, server-side OAuth flow for Sveltia CMS. It handles authorization initiation, callback processing, token exchange, and safe token transmission to the CMS via postMessage. Proper configuration of GitHub OAuth credentials and redirect URIs, along with CORS preflight support, ensures a smooth and secure authentication experience.

Appendices

API Definitions

  • GET /api/auth
    • Purpose: Initiate GitHub OAuth authorization.
    • Response: 302 redirect to GitHub authorization URL.
  • GET /api/auth/callback
    • Purpose: Handle OAuth callback and return token to CMS.
    • Response: HTML page with script posting token via postMessage; or error responses (400/500).
  • OPTIONS *
    • Purpose: CORS preflight support.
    • Response: Headers enabling GET, POST, OPTIONS with Content-Type.

Section sources

  • [worker.js:193-198](file://worker.js#L193-L198)
  • [worker.js:200-227](file://worker.js#L200-L227)
  • [worker.js:183-191](file://worker.js#L183-L191)

Deployment and Configuration References

  • Cloudflare Worker configuration and secrets:
    • Refer to wrangler.jsonc for binding and secret setup.
  • Environment variables and secrets:
    • Refer to README for secret names and setup steps.
  • Static site and redirects:
    • netlify.toml and cloudflare-pages.toml provide legacy and reference configurations.

Section sources

  • [wrangler.jsonc:1-35](file://wrangler.jsonc#L1-L35)
  • [README.md:497-548](file://README.md#L497-L548)
  • [netlify.toml:1-26](file://netlify.toml#L1-L26)
  • [cloudflare-pages.toml:1-17](file://cloudflare-pages.toml#L1-L17)