OAuth

Choose and configure OAuth client credentials, authorization code, and device code auth in Restish.

Use this guide when an API uses OAuth and you need Restish to fetch, cache, and refresh bearer tokens for a profile or generated OpenAPI credential.

OAuth setup is provider-specific. Restish owns the local CLI workflow, token cache, request header, and OpenAPI credential binding. Your identity provider still owns client registration, redirect URI rules, scopes, consent, and token policy.

Choose A Flow

FlowRestish typeUse it when
Client credentialsoauth-client-credentialsA script, service account, CI job, or machine-to-machine integration calls the API.
Authorization code with PKCEoauth-authorization-codeA human signs in through a browser and grants access to their account.
Device codeoauth-device-codeThe terminal cannot receive a localhost browser callback, or the provider recommends device authorization for CLIs.

Use external-tool instead when your organization already has an SSO helper or request signer that should stay in charge of tokens.

Restish does not automatically switch an authorization-code profile into device code, even when issuer discovery advertises both endpoints. Choose the flow that matches the OAuth client registration and provider instructions. Use --rsh-no-browser for a one-off browserless authorization-code sign-in; use oauth-device-code when device authorization is the intended CLI flow.

Store OAuth Config

For one API/profile, put OAuth directly under the profile’s auth field. For several APIs or several credentials, put the OAuth config in top-level auth_profiles and reference it with auth_ref.

{
  "auth_profiles": {
    "work-user": {
      "type": "oauth-authorization-code",
      "params": {
        "issuer_url": "https://issuer.test",
        "client_id": "env:WORK_CLIENT_ID",
        "scopes": "read:items offline_access",
        "redirect_path": "/callback"
      }
    }
  },
  "apis": {
    "work": {
      "base_url": "https://api.vendor.test",
      "profiles": {
        "default": {
          "auth_ref": "work-user"
        }
      }
    }
  }
}

Prefer env:NAME for client IDs and secrets when the value should not live in the config file. Keep the config file private because OAuth settings can still describe sensitive tenants and clients.

Use Issuer Discovery

When your provider has OpenID Connect discovery, prefer issuer_url:

{
  "type": "oauth-client-credentials",
  "params": {
    "issuer_url": "https://issuer.test",
    "client_id": "env:CLIENT_ID",
    "client_secret": "env:CLIENT_SECRET",
    "scopes": "read:items"
  }
}

Restish fetches /.well-known/openid-configuration under that issuer and uses the advertised authorization, device authorization, and token endpoints as needed. Discovery endpoints must stay under the issuer host and path scope.

If your provider does not publish discovery, configure direct endpoint URLs:

{
  "type": "oauth-client-credentials",
  "params": {
    "token_url": "https://issuer.test/oauth/token",
    "client_id": "env:CLIENT_ID",
    "client_secret": "env:CLIENT_SECRET",
    "audience": "https://api.vendor.test/"
  }
}

OAuth endpoints must use HTTPS except for localhost or loopback development URLs. Endpoint URLs must not include credentials, fragments, or query strings.

When OAuth authorize, device authorization, or token endpoints live on the same host as the API, you may use relative paths in manual config. oauth2/token resolves under the active API/profile base_url path, while /oauth2/token resolves at that host root. For example, with base_url: https://api.vendor.test/v1, oauth2/token becomes https://api.vendor.test/v1/oauth2/token.

Browser Sign-In

Authorization code auth starts a local callback listener, opens the browser, exchanges the returned code for a token, and caches the result.

{
  "type": "oauth-authorization-code",
  "params": {
    "authorize_url": "https://issuer.test/authorize",
    "token_url": "https://issuer.test/oauth/token",
    "client_id": "env:CLIENT_ID",
    "scopes": "read:items offline_access",
    "redirect_path": "/callback"
  }
}

Allow this callback URL in your OAuth app unless you change the port or path:

http://localhost:8484/

With the example above, allow:

http://localhost:8484/callback

Some providers distinguish localhost from 127.0.0.1. Restish sends localhost in the authorization request, so exact-match providers must allow the localhost URL.

If your provider requires an HTTPS redirect URL, provide your own local callback certificate and key:

{
  "type": "oauth-authorization-code",
  "params": {
    "authorize_url": "https://issuer.test/authorize",
    "token_url": "https://issuer.test/oauth/token",
    "client_id": "env:CLIENT_ID",
    "redirect_scheme": "https",
    "redirect_port": "8484",
    "redirect_path": "/callback",
    "redirect_cert": "./localhost.pem",
    "redirect_key": "./localhost.key"
  }
}

Allow https://localhost:8484/callback in the OAuth app. Restish does not generate certificates or install local trust roots; use a certificate that your browser accepts for localhost.

The browser callback page uses the active Restish theme. To brand that local page, set callback_success_html and/or callback_error_html on the authorization-code profile:

{
  "type": "oauth-authorization-code",
  "params": {
    "authorize_url": "https://issuer.test/authorize",
    "token_url": "https://issuer.test/oauth/token",
    "client_id": "env:CLIENT_ID",
    "callback_success_html": "<html><body><h1>Signed in</h1><p>You can return to the terminal.</p></body></html>",
    "callback_error_html": "<html><body><h1>Sign-in failed: $ERROR</h1><p>$DETAILS</p></body></html>"
  }
}

Callback HTML supports $TITLE and $DETAILS; failure HTML also supports $ERROR. Restish escapes substituted values before inserting them, and does not forward callback HTML params to OAuth authorization or token endpoints.

Use --rsh-no-browser when browser launch is not possible:

restish --rsh-no-browser myapi list-items

Restish prints the authorization URL and, when prompting is available, asks you to paste the authorization code.

This keeps the configured flow as authorization code. It is useful over SSH, in remote terminals, and on machines where opening the local browser is not possible, as long as the provider allows copying the final authorization code back into the terminal.

Device Code

Device code auth prints the provider’s verification instructions and polls the token endpoint until you finish authorization or the provider’s code expires.

{
  "type": "oauth-device-code",
  "params": {
    "issuer_url": "https://issuer.test",
    "client_id": "env:CLIENT_ID",
    "scopes": "read:items offline_access"
  }
}

Without discovery, set both device_authorization_url and token_url.

Choose device code when the provider documents it for CLI use, when a localhost callback is impractical, or when the OAuth app is registered for device authorization rather than redirect-based sign-in. Do not rely on Restish to infer this from the issuer metadata; make the auth type explicit in config.

Generated APIs

When an OpenAPI spec declares OAuth security schemes, api connect can prompt for the required client details and write credential bindings under the active profile.

restish api connect myapi https://api.vendor.test \
  'prompt.credentials.UserOAuth.client_id: env:CLIENT_ID' \
  'prompt.credentials.UserOAuth.scopes: read:items'

Generated commands use the operation’s OpenAPI security requirements. If an operation has several allowed alternatives, choose one explicitly:

restish myapi list-items --rsh-auth UserOAuth
restish myapi signed-report --rsh-auth UserOAuth+PartnerKey

Use api auth inspect to see configured credentials and whether OAuth access tokens are cached:

restish api auth inspect myapi

Token Cache

Restish caches OAuth tokens by API/profile or shared auth profile. Expired access tokens are refreshed when a refresh token is available. If refresh fails with invalid_grant, Restish clears that cached token and reruns the interactive flow when the flow supports it.

If an API returns 401 Unauthorized for a token-bearing OAuth request, Restish forces fresh auth and retries that request once. This handles tokens that look valid locally but were revoked, expired early, or rejected by the API’s auth layer. Restish does not keep retrying beyond that one controlled recovery; if the retried request is still unauthorized, inspect or clear the cached auth state.

Clear a cached OAuth token when consent changes, a grant is revoked, or you want the next request to force a fresh sign-in:

restish api auth logout myapi
restish api auth logout myapi --all-profiles
restish api auth logout --auth-profile work-user

OAuth token cache is separate from HTTP response cache. restish cache clear does not log you out.

Provider Parameters

Restish forwards provider-specific OAuth params that are not reserved by the flow. Common examples are:

ParamUse
audienceAuth0-style resource server audience.
resourceMicrosoft-style resource identifier.
organizationTenant or organization hint used by some providers.

Set auth_method: client_secret_basic when the provider expects the client secret in an HTTP Basic token request header. The default is client_secret_post, which sends the secret in the token request body.

Troubleshooting

If Restish says an authorization-code credential has no cached access token, rerun the command from an interactive terminal so the browser flow can complete before the API request is sent.

If the provider does not return a refresh token, add the provider’s offline scope when required, often offline_access. Restish warns when a cached OAuth flow receives an access token without a refresh token.

If discovery fails, confirm the issuer URL has no query string or fragment, and that /.well-known/openid-configuration returns endpoints on the same issuer host and path scope.