Skip to main content
Docs Authentication

Authentication

The Hostsmith API uses OAuth 2.0 with Authorization Code + PKCE for authentication. This is the same flow used by MCP clients like Claude Code and Cursor.

Overview

  1. Your application redirects the user to Hostsmith to authorize access
  2. The user reviews the requested permissions and approves
  3. Hostsmith redirects back to your application with an authorization code
  4. Your application exchanges the code for an access token
  5. Use the access token as a Bearer token in API requests

Access tokens are short-lived JWTs (1 hour). Refresh tokens are provided to obtain new access tokens without re-authorizing.

Discovery

The authorization server publishes its configuration at a well-known endpoint:

GET https://hostsmith.net/.well-known/oauth-authorization-server

Response:

{
  "issuer": "https://hostsmith.net",
  "authorization_endpoint": "https://hostsmith.net/api/oauth/authorize",
  "token_endpoint": "https://hostsmith.net/api/oauth/token",
  "registration_endpoint": "https://hostsmith.net/api/oauth/register",
  "revocation_endpoint": "https://hostsmith.net/api/oauth/revoke",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "code_challenge_methods_supported": ["S256"],
  "token_endpoint_auth_methods_supported": ["none"],
  "scopes_supported": [
    "sites:read",
    "sites:write",
    "domains:read",
    "files:write"
  ]
}

Client registration

Before starting the OAuth flow, your application needs a client_id. There are two options:

Host a JSON metadata document at an HTTPS URL and use that URL as your client_id. No registration step needed - Hostsmith fetches the metadata when the user authorizes.

Example: if your app is at https://myapp.example.com, host this at https://myapp.example.com/.well-known/oauth-client:

{
  "client_id": "https://myapp.example.com/.well-known/oauth-client",
  "client_name": "My App",
  "redirect_uris": ["https://myapp.example.com/callback"]
}

Then use client_id=https://myapp.example.com/.well-known/oauth-client in the authorization request. The client_id field in the document must match the URL.

Option 2: Dynamic Client Registration

Register your client programmatically:

POST https://hostsmith.net/api/oauth/register
Content-Type: application/json

{
  "client_name": "My App",
  "redirect_uris": ["https://myapp.example.com/callback"]
}

Response:

{
  "client_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "client_name": "My App",
  "redirect_uris": ["https://myapp.example.com/callback"]
}

Dynamically registered clients expire unless a user completes the authorization flow, which makes the client permanent.

Authorization flow

1. Generate PKCE challenge

PKCE is required for all authorization requests. Generate a random code_verifier and derive the code_challenge:

// Generate a random 43-character code verifier
const verifier = crypto.randomUUID() + crypto.randomUUID();
const encoder = new TextEncoder();
const digest = await crypto.subtle.digest("SHA-256", encoder.encode(verifier));
const challenge = btoa(String.fromCharCode(...new Uint8Array(digest)))
  .replace(/\+/g, "-")
  .replace(/\//g, "_")
  .replace(/=+$/, "");

2. Redirect to authorize

Open the authorization URL in the user's browser:

https://hostsmith.net/api/oauth/authorize?
  client_id=YOUR_CLIENT_ID&
  redirect_uri=https://myapp.example.com/callback&
  response_type=code&
  code_challenge=YOUR_CHALLENGE&
  code_challenge_method=S256&
  scope=sites:read sites:write files:write&
  state=RANDOM_STATE

Parameters:

ParameterRequiredDescription
client_idYesYour client ID (UUID or metadata URL)
redirect_uriYesMust match a registered redirect URI
response_typeYesAlways code
code_challengeYesS256 PKCE challenge
code_challenge_methodYesAlways S256
scopeYesSpace-separated list of scopes
stateRecommendedRandom string to prevent CSRF - verify it on callback
resourceRecommendedAPI resource identifier(s) - see Resource binding

The user sees a consent screen listing the requested permissions. After approval, Hostsmith redirects to your redirect_uri:

https://myapp.example.com/callback?code=AUTH_CODE&state=RANDOM_STATE

3. Exchange code for tokens

POST https://hostsmith.net/api/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=AUTH_CODE&
client_id=YOUR_CLIENT_ID&
redirect_uri=https://myapp.example.com/callback&
code_verifier=YOUR_VERIFIER

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "rt_a1b2c3d4..."
}

Making API requests

Include the access token as a Bearer token:

curl https://use2.api.hostsmith.net/v1/sites \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."

A 401 response means the token is missing, expired, or invalid. Check the WWW-Authenticate header for details.

Refreshing tokens

Access tokens expire after 1 hour. Use the refresh token to get a new one:

POST https://hostsmith.net/api/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&
refresh_token=rt_a1b2c3d4...&
client_id=YOUR_CLIENT_ID

Response includes a new access token and a new refresh token (rotation). The old refresh token is invalidated.

Refresh tokens are valid for 90 days from the original authorization. After that, the user must re-authorize through the consent flow.

Revoking tokens

To revoke a refresh token (e.g. on user logout):

POST https://hostsmith.net/api/oauth/revoke
Content-Type: application/x-www-form-urlencoded

token=rt_a1b2c3d4...&
client_id=YOUR_CLIENT_ID

Scopes

Request only the scopes your application needs:

ScopeDescription
sites:readList and view sites
sites:writeCreate, update, and delete sites
domains:readList and view domains
files:writeUpload and manage site files

Resource binding

The resource parameter binds the access token to specific API endpoints by setting the JWT aud (audience) claim. Each regional API validates that its identifier is present in the token's audience.

To bind a token to a single region, include one resource parameter:

https://hostsmith.net/api/oauth/authorize?
  ...&
  resource=https://use2.api.hostsmith.net

To bind a token to multiple regions, include resource multiple times:

https://hostsmith.net/api/oauth/authorize?
  ...&
  resource=https://use2.api.hostsmith.net&
  resource=https://euc1.api.hostsmith.net

Each regional API publishes its resource identifier at /.well-known/oauth-protected-resource:

curl https://use2.api.hostsmith.net/.well-known/oauth-protected-resource
{
  "resource": "https://use2.api.hostsmith.net",
  "authorization_servers": ["https://hostsmith.net"],
  "scopes_supported": ["sites:read", "sites:write", "domains:read", "files:write"]
}

If you need to call the API in both regions, include both resource identifiers when authorizing. The resulting token will be valid for both.

Regional endpoints

The API is deployed in multiple regions. Choose the region where your sites are hosted:

RegionEndpointResource identifier
US East (Ohio)https://use2.api.hostsmith.nethttps://use2.api.hostsmith.net
EU (Frankfurt)https://euc1.api.hostsmith.nethttps://euc1.api.hostsmith.net

Include the resource identifier(s) in the resource parameter when authorizing to bind the token to the API.