OOrchard

API Authentication

Default Mode (Authentication Disabled)

Out of the box, Orchard runs with authentication disabled (orchard.security.oauth2.enabled: false). In this mode, all requests are permitted without a token, and every request is resolved to the same shared "local" cultivator — there's no per-request identity check:

code
curl http://localhost:8080/api/groves

The default cultivator's UUID is 11111111-1111-1111-1111-111111111111 unless overridden via the orchard.dev.default-cultivator-id server property. Trowel doesn't send any identity header in this mode, and the server doesn't look for one.

OAuth2 / JWT (Production)

For production deployments, enable OAuth2 in application.yml:

code
orchard:
  security:
    oauth2:
      enabled: true
 
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://fence.your-domain.com

With OAuth2 enabled, the server validates JWT tokens from the configured issuer. Pass the token via the Authorization header:

code
curl -H "Authorization: Bearer <jwt-token>" http://localhost:8080/api/groves

The API's OAuth2 resource server config is issuer-agnostic, so any OAuth2/OIDC-compatible identity provider (Keycloak, Auth0, Okta, etc.) can technically issue the issuer-uri. In practice, Orchard ships its own first-party authorization server, Fence, as part of the platform — most deployments point issuer-uri at Fence rather than standing up a separate IdP.

Fence

Fence is Orchard's built-in OAuth2/OIDC authorization server (the fence module). It issues and validates the JWTs described above, so self-hosters generally don't need to configure an external identity provider just to turn on authentication — see Deployment for running Fence alongside the API server.

Fence itself can delegate the human sign-in step to an upstream identity provider (configured under fence.upstream) rather than storing passwords — this only affects how a cultivator proves who they are to Fence, not how the Orchard API validates tokens, which always happens against Fence's own issuer.

CLI login (device authorization grant)

Trowel authenticates via the OAuth 2.0 device authorization grant (RFC 8628):

code
trowel login

This requests a device code from Fence, opens your browser to a verification URL with a short code to confirm, then polls Fence until you approve the request. On success, Trowel stores the access and refresh tokens in ~/.orchard/config.toml and sends the access token as a Bearer Authorization header on subsequent requests automatically. See trowel login in the CLI Reference for flags.

SSH gateway tokens

The SSH gateway accepts a short-lived owner token as an SSH password, as an alternative to a registered SSH key. Exchange a Fence-issued access token for one:

code
curl -X POST https://fence.example.com/gateway-token \
  -H "Authorization: Bearer <access-token>"
code
{ "token": "eyJhbGciOiJSUzI1NiIs..." }

The token is valid for 5 minutes and is scoped to the gateway only (aud: orchard-gateway, scope: gateway-ssh). Connect with ssh -p 2222 <grove-id>@<gateway-host> and paste the token at the password prompt. The gateway accepts it only if the token's email belongs to the grove's owner. The access token must carry an email claim, or Fence returns 400 Bad Request.

When OAuth2 is disabled, anyone with network access to the API is treated as the same shared default cultivator — there is no per-cultivator isolation at all. Always enable OAuth2 (and run Fence) in production environments exposed to a network.