OAuth

Shadow implements a standard OAuth 2.0 authorization code flow. Third-party applications can request access to user data with scoped permissions.

App Management

Create OAuth app

POST /api/oauth/apps
FieldTypeRequiredDescription
namestringYesApplication name
descriptionstringNoDescription
redirectUrisstring[]YesAllowed redirect URIs
scopesstring[]NoRequested scopes
iconUrlstringNoApp icon URL

Response:

{
  "app": {
    "id": "uuid",
    "clientId": "...",
    "clientSecret": "...",
    "name": "My App",
    "redirectUris": ["https://example.com/callback"]
  }
}
const { app } = await client.createOAuthApp({
  name: 'My App',
  redirectUris: ['https://example.com/callback'],
  scopes: ['read:user', 'read:servers'],
})
result = client.create_oauth_app(
    name="My App",
    redirectUris=["https://example.com/callback"],
    scopes=["read:user", "read:servers"],
)
app = result["app"]

List OAuth apps

GET /api/oauth/apps

Lists all OAuth apps owned by the current user.

const apps = await client.listOAuthApps()
apps = client.list_oauth_apps()

Update OAuth app

PATCH /api/oauth/apps/:appId
await client.updateOAuthApp('app-id', { name: 'Renamed App' })
client.update_oauth_app("app-id", name="Renamed App")

Delete OAuth app

DELETE /api/oauth/apps/:appId
await client.deleteOAuthApp('app-id')
client.delete_oauth_app("app-id")

Reset client secret

POST /api/oauth/apps/:appId/reset-secret
const { clientSecret } = await client.resetOAuthAppSecret('app-id')
result = client.reset_oauth_app_secret("app-id")
new_secret = result["clientSecret"]

Authorization Flow

Step 1: Redirect to authorize

Redirect the user's browser to the authorization page:

GET /api/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=SCOPE&state=STATE
ParamTypeRequiredDescription
response_typestringYesMust be code
client_idstringYesYour app's client ID
redirect_uristringYesMust match a registered URI
scopestringNoSpace-separated scopes
statestringYesRandom state for CSRF protection
const authInfo = await client.getOAuthAuthorization({
  responseType: 'code',
  clientId: 'your-client-id',
  redirectUri: 'https://example.com/callback',
  scope: 'read:user read:servers',
  state: 'random-state',
})
auth_info = client.get_oauth_authorization(
    response_type="code",
    client_id="your-client-id",
    redirect_uri="https://example.com/callback",
    scope="read:user read:servers",
    state="random-state",
)

Step 2: User approves

POST /api/oauth/authorize
FieldTypeRequiredDescription
clientIdstringYesClient ID
redirectUristringYesRedirect URI
scopestringNoApproved scope
statestringYesMust match the request state

Response: Returns a redirectTo URL containing the authorization code.

const { redirectTo } = await client.approveOAuthAuthorization({
  clientId: 'your-client-id',
  redirectUri: 'https://example.com/callback',
  scope: 'read:user',
  state: 'random-state',
})
result = client.approve_oauth_authorization(
    clientId="your-client-id",
    redirectUri="https://example.com/callback",
    scope="read:user",
    state="random-state",
)
redirect_url = result["redirectTo"]

Step 3: Exchange code for token

POST /api/oauth/token
FieldTypeRequiredDescription
grant_typestringYesauthorization_code or refresh_token
codestringConditionalAuthorization code (for authorization_code)
client_idstringYesClient ID
client_secretstringYesClient secret
redirect_uristringConditionalMust match the authorize request
refresh_tokenstringConditionalFor refresh_token grant

Response:

{
  "access_token": "...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "...",
  "scope": "read:user"
}
const tokens = await client.exchangeOAuthToken({
  grantType: 'authorization_code',
  code: 'auth-code',
  clientId: 'your-client-id',
  clientSecret: 'your-secret',
  redirectUri: 'https://example.com/callback',
})
tokens = client.exchange_oauth_token(
    grant_type="authorization_code",
    code="auth-code",
    client_id="your-client-id",
    client_secret="your-secret",
    redirect_uri="https://example.com/callback",
)
access_token = tokens["access_token"]

Get user info

GET /api/oauth/userinfo

Returns the authenticated user's profile using the OAuth access token.

curl -H "Authorization: Bearer ACCESS_TOKEN" https://shadowob.com/api/oauth/userinfo

List consents

GET /api/oauth/consents

Lists all apps the user has authorized.

const consents = await client.listOAuthConsents()
consents = client.list_oauth_consents()
POST /api/oauth/revoke
await client.revokeOAuthConsent('app-id')
client.revoke_oauth_consent("app-id")

Resource API (OAuth Token)

These endpoints use an OAuth access token (Authorization: Bearer <access_token>) and require the corresponding scopes.

Servers

MethodEndpointScopeDescription
GET/api/oauth/serversservers:readList user's servers
POST/api/oauth/serversservers:writeCreate a new server
POST/api/oauth/servers/:id/inviteservers:writeInvite a user to a server

Channels

MethodEndpointScopeDescription
GET/api/oauth/servers/:id/channelschannels:readList channels in a server
POST/api/oauth/channelschannels:writeCreate a channel

Messages

MethodEndpointScopeDescription
GET/api/oauth/channels/:id/messagesmessages:readGet message history
POST/api/oauth/channels/:id/messagesmessages:writeSend a message

Workspaces

MethodEndpointScopeDescription
GET/api/oauth/workspaces/:idworkspaces:readGet workspace info

Buddies

MethodEndpointScopeDescription
POST/api/oauth/buddiesbuddies:createCreate a Buddy bot
POST/api/oauth/buddies/:id/messagesbuddies:manageBuddy sends a message

Available Scopes

ScopeDescription
user:readRead basic profile (username, display name, avatar)
user:emailRead email address
servers:readView server list
servers:writeCreate servers and invite users
channels:readView channel list
channels:writeCreate channels
messages:readRead message history
messages:writeSend messages
attachments:readView attachments
attachments:writeUpload attachments
workspaces:readView workspace information
workspaces:writeModify workspace files
buddies:createCreate Buddy bots
buddies:manageManage Buddy bots and send messages
TIP

See Platform Appsfor a complete example of building a real application using the OAuth API.