Skip to main content

OAuth Beta

Foxglove supports OAuth 2.0 authentication, which is recommended if you're building an application that integrates with Foxglove. The application calls the Foxglove API as the Foxglove user who approved it, limited to the scopes the user approved and to that user's own permissions.

Create an application​

An organization admin registers an application under Settings → Integrations → OAuth applications.

The consent page shows the name, developer, and description to the user.

Foxglove shows the client secret once, when you create the application or rotate it. The client ID stays available on the application page.

note

An application you register is private to your organization. Only members of that organization can approve it.

Authorize a user​

Foxglove uses the OAuth 2.0 authorization code flow with PKCE (Proof Key for Code Exchange). Send the user to the Foxglove authorization page. The user signs in, reviews the application, and approves or denies it.

https://app.foxglove.dev/oauth/authorize
ParameterDescription
client_idRequired. Client ID from the application page
redirect_uriRequired. A registered redirect URI, URL-encoded
response_typeRequired. code
code_challengeRequired. Base64url-encoded SHA-256 hash of code_verifier, without padding
code_challenge_methodRequired. S256
scopeRequired. The capabilities the application needs, separated by spaces and URL-encoded
stateOptional, recommended. Returned unchanged on the callback. Up to 2048 characters

code_verifier is a random string of 43 to 128 characters from A-Z, a-z, 0-9, -, ., _, and ~. Keep it until the token request. Send only code_challenge to the authorization page.

Example​

https://app.foxglove.dev/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=https%3A%2F%2Fexample.com%2Foauth%2Fcallback&scope=projects.list%20datasets.list&code_challenge=CHALLENGE&code_challenge_method=S256&state=STATE

Handle the callback​

After the user approves, Foxglove redirects to redirect_uri with code and, when you sent one, state. Refuse a callback whose state doesn't match the value you stored.

A user who has already approved every requested scope is sent back with a code and doesn't see the consent page again. A request for a scope the user hasn't approved shows the consent page again, with the new scopes marked. If the user denies the request, the callback has error=access_denied instead of code. See Errors for the other values.

The authorization code expires after 60 seconds. See Authorization codes for the rest of its rules.

Example​

https://example.com/oauth/callback?code=CODE&state=STATE

Exchange the code​

Exchange the authorization code for an access token and a refresh token. Pass parameters in the body as a URL-encoded form with Content-Type: application/x-www-form-urlencoded, and authenticate with the client secret.

POST https://api.foxglove.dev/oauth/token
ParameterDescription
grant_typeRequired. authorization_code
codeRequired. The code from the callback
redirect_uriRequired. The same redirect_uri you sent to the authorization page
code_verifierRequired. The verifier that produced code_challenge
client_idRequired. Client ID from the application page
client_secretRequired. The application's client secret

Example​

curl https://api.foxglove.dev/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=CODE" \
-d "redirect_uri=https://example.com/oauth/callback" \
-d "code_verifier=CODE_VERIFIER" \
-d "client_id=CLIENT_ID" \
-d "client_secret=CLIENT_SECRET"

Response​

{
"access_token": "fox_oat_...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "fox_ort_...",
"scope": "projects.list datasets.list"
}

expires_in is the access token lifetime in seconds. Schedule the next refresh from it. See Token lifetimes. scope lists the scopes the access token has.

Refresh an access token​

Exchange the refresh token for a new access token and a new refresh token. Pass parameters in the body as a URL-encoded form with Content-Type: application/x-www-form-urlencoded, and authenticate with the client secret.

POST https://api.foxglove.dev/oauth/token
ParameterDescription
grant_typeRequired. refresh_token
refresh_tokenRequired. The refresh token from the previous response
scopeOptional. A subset of the refresh token's scopes, separated by spaces. The new tokens keep the same scopes when omitted
client_idRequired. Client ID from the application page
client_secretRequired. The application's client secret

Response​

{
"access_token": "fox_oat_...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "fox_ort_...",
"scope": "projects.list datasets.list"
}

Store the new refresh token. The refresh token you presented can't be used again, and presenting it again revokes the approval. See Refresh token rotation.

Call the API​

Send the access token to the Foxglove API in an Authorization: Bearer header:

curl https://api.foxglove.dev/v1/projects \
-H "Authorization: Bearer ACCESS_TOKEN"

The token acts as the user who approved the application, within that user's permissions, including role and project access. The request needs the route's capability both in the token's scopes and in that user's role. A token without the scope gets 403 with a WWW-Authenticate header that names the missing scope:

WWW-Authenticate: Bearer error="insufficient_scope", scope="datasets.list"

To get a token that has it, send the user to the authorization page again with every scope the application needs, not only the missing one. The new tokens carry only the scopes in that request.

Scopes​

Each scope is a Foxglove API capability. Request only the scopes your application needs. The user sees them in plain language on the consent page, and can review them later under Settings → Connected accounts.

A scope never gives the application more than the user can do. If the user's role doesn't allow a capability, a request that needs it fails even when the token has the scope.

Applications can request these scopes. For the current list, read scopes_supported in the authorization server metadata at https://api.foxglove.dev/.well-known/oauth-authorization-server.

AreaReadWrite
Recordingsrecordings.list, data.imports.pending.listrecordings.delete, data.upload
Sessionssessions.listsessions.create, sessions.update, sessions.delete
Data coveragedata.coverage.list
Topicsdata.topics.list
Data streamingdata.stream
Data searchdata.search
Devicesdevices.listdevices.create, devices.delete, devices.update
Eventsevents.listevents.create, events.update, events.delete
Event typeseventTypes.listeventTypes.create, eventTypes.delete, eventTypes.update
Custom propertiesproperties.listproperties.create, properties.update, properties.delete
Extensionsextensions.listextensions.create, extensions.delete
Layoutslayouts.listlayouts.create, layouts.delete, layouts.update
Importsdata.imports.listdata.imports.delete
Projectsprojects.list
Sitessites.listsites.create, sites.delete, sites.update
Datasetsdatasets.listdatasets.create, datasets.update, datasets.delete
Episodesepisodes.listepisodes.create, episodes.update, episodes.delete
Automationsautomations.listautomations.create, automations.update, automations.delete, automations.run

Applications can't request capabilities that create or manage credentials, such as device tokens and site tokens.

Revoke access​

Revoke an access token or a refresh token from your application. Pass parameters in the body as a URL-encoded form with Content-Type: application/x-www-form-urlencoded, and authenticate with the client secret.

POST https://api.foxglove.dev/oauth/revoke
ParameterDescription
tokenRequired. The access token or refresh token to revoke
token_type_hintOptional. access_token or refresh_token
client_idRequired. Client ID from the application page
client_secretRequired. The application's client secret

Response​

A successful call returns 200 and an empty JSON object, including when the token is already revoked or unknown.

Revoking a refresh token also invalidates the access tokens for that approval. Revoking an access token invalidates that access token. A refresh token that is still valid can obtain a new access token.

Revoke from Foxglove​

The user can revoke their own approval under Settings → Connected accounts, on the Authorized applications card. An organization admin can revoke an application's access for every member of the organization under Settings → Integrations → Authorized applications. Either action deletes the approval and its tokens.

Rotate or delete an application​

Rotate the client secret from the application's settings page. The previous secret stops working immediately. Access tokens and refresh tokens already issued keep working. The next call to the token or revocation endpoint must use the new secret. The new secret is shown once.

note

Rotating the client secret doesn't revoke access tokens or refresh tokens already issued. To cut off access right away, also revoke the application's approvals. An organization admin can revoke them for every member of the organization at once.

Deleting an application removes it, along with every approval and token issued for it.

Client authentication​

Every application is a confidential client. The token and revocation endpoints always need the client secret, sent with the client ID as the client_id and client_secret form fields. Keep these calls on a server you control, and don't ship the secret in a native or browser application.

Token lifetimes and limits​

Authorization codes​

The user has 15 minutes to approve. The authorization code can be exchanged once and expires after 60 seconds. A mismatch on the client, redirect_uri, or code_verifier uses the code. A missing or malformed code_verifier, or rejected client credentials, doesn't. Send the user to the authorization page again to get a new code.

An unknown client_id or a redirect_uri that isn't registered stays on the Foxglove page. Those requests aren't sent to your callback. The user must be signed in to the organization that registered the application. A session in another organization stays on the Foxglove page, where the user can sign in to that organization.

Token lifetimes​

The access token expires in one hour (expires_in is 3600). Each refresh token expires 30 days after it is issued.

Refresh token rotation​

Each refresh returns a new refresh token, and the one you presented can't be used again. Presenting a refresh token that was already exchanged revokes that user's approval of your application. Later refresh attempts for that approval fail until the user approves the application again.

An access token already issued stays valid until it expires, the approval is revoked, or a refresh token for that approval is revoked.

Errors​

Token and revocation errors​

The token and revocation endpoints return JSON:

{
"error": "invalid_grant",
"error_description": "The authorization grant is no longer valid"
}
StatuserrorWhen
400invalid_requestThe body is malformed or larger than 16 KiB, code_verifier is missing or malformed, or the revocation request has no token
400invalid_clientclient_id or client_secret is missing or wrong
400invalid_grantcode or refresh_token is missing, unknown, expired, or already used, code_verifier doesn't match code_challenge, or the grant doesn't match this client or redirect_uri. Presenting a refresh token that was already exchanged for a new one also revokes the approval
400unsupported_grant_typegrant_type isn't authorization_code or refresh_token
400invalid_scopeOn a refresh, scope lists a capability that applications can't request, or one the refresh token doesn't have

Authorization errors​

The authorization page adds these query parameters to your redirect_uri when it can redirect:

ParameterDescription
errorAlways included. One of the values below
error_descriptionHuman-readable detail. Included with invalid_request, invalid_scope, and unsupported_response_type
stateThe state you sent to the authorization page. Included when you sent one
errorWhen
invalid_requestresponse_type is missing, code_challenge_method isn't S256, or code_challenge is missing or malformed
unsupported_response_typeresponse_type isn't code
invalid_scopescope is missing or empty, or lists a capability that applications can't request
access_deniedThe user denied the request