OAuth2 quick reference¶
Everything SweatStack-specific about the OAuth2 flow, on one page. SweatStack is a standard OAuth2 and OpenID Connect provider. If you have a working integration with another platform, it will almost certainly work here unchanged. For the full walk-through, see OAuth2. For the drop-in button that signs users in and onboards new ones in the same step, see Sign in with SweatStack.
Four things that differ from other platforms¶
- PKCE is optional, not required. The plain Authorization Code flow with a client secret is fully supported.
- Credentials are self-service. Nobody emails you a client ID or secret. You get them from the app's settings page.
- The client secret is not created automatically. You create it in the Secrets section. With PKCE you need no secret at all.
- There is no approval queue. Filling in the four required fields makes your app public the moment you save.
Endpoints¶
| Authorization | https://app.sweatstack.no/oauth/authorize |
| Token | https://app.sweatstack.no/api/v1/oauth/token |
| OIDC discovery | https://app.sweatstack.no/.well-known/openid-configuration |
| JWKS | https://app.sweatstack.no/.well-known/jwks.json |
If your library supports OpenID Connect discovery, point it at the discovery URL. It then configures every endpoint, scope, and signing key itself.
A complete authorization URL (scopes are space-delimited, URL-encoded):
https://app.sweatstack.no/oauth/authorize?response_type=code&client_id={client_id}&redirect_uri={redirect_uri}&scope=data:read%20data:write
Authorization request parameters¶
Everything GET /oauth/authorize accepts. The first table is standard OAuth2 and OpenID Connect,
with the places where SweatStack's behavior differs from the specification called out. The
second table is SweatStack's own. Both of its parameters are optional.
| Parameter | Standard | Values | Notes |
|---|---|---|---|
response_type |
required | code |
Send it, as every client library does. SweatStack does not validate it: code is the only flow. |
client_id |
required | your Application ID | Also brands the sign-in and consent screens with your app's name. |
redirect_uri |
required | a registered redirect URI | See Redirect URIs. |
scope |
required | space-delimited scopes | See Scopes. |
state |
recommended | any string | Returned to you unchanged on the callback. Use it for CSRF protection. |
code_challenge, code_challenge_method |
PKCE | S256 |
A flow started with a challenge must finish with its verifier. |
nonce |
OIDC, optional | any string | Echoed in the id_token that comes with the code exchange. |
prompt |
OIDC, optional | none, consent, login, select_account |
Default consent. none skips the consent screen when the user is signed in and has already granted your app these scopes. Where SweatStack differs from the specification: when it cannot skip, it shows the sign-in or consent screen instead of returning login_required, consent_required or interaction_required to your redirect URI. Consent is always shown for a loopback (localhost) redirect, whatever prompt says. login and select_account are accepted and behave as consent. |
| Parameter | Default | Values | Effect |
|---|---|---|---|
email_auth_mode |
enabled |
enabled, existing_only |
How the sign-in screen presents email. existing_only reframes it as "Sign in with an existing email account", so a new user self-selects into a wearable sign-in instead. It changes the wording, not the rules: a user who already has an email account signs in as before. Recommended for apps that need wearable data from day one. |
integration |
none | garmin_connect, intervals_icu, wahoo |
Name one provider and SweatStack skips the sign-in chooser: the user goes straight to that provider, and a signed-in user who has not connected it is sent to connect it before consent. One value per request. A misspelled value returns 422. See Send users straight to one provider. |
Anything else on the URL is ignored.
Credentials¶
- Client ID = the Application ID on the app's settings page. Public. Safe to ship.
- Client secret = created by you in the Secrets section. Needed only for the flow without PKCE. Treat it like a password. Never ship it to a browser or a device.
Create an app at Settings → API.
Scopes¶
Space-delimited (RFC 6749 §3.3); commas also accepted.
| Scope | Grants |
|---|---|
data:read |
Read access to all user data: activities, dailies (including health measures the user entered), and tests. There is no narrower read scope. |
data:write |
Write (and delete) access. Does not grant read. Request data:read too if you need both. |
profile |
Profile information (includes email). |
offline_access |
Issues a refresh token. Required for PKCE clients to get one; client-secret clients always do. |
Pushing data in? If your integration uploads activities into SweatStack, request data:write and see Uploading files: FIT and CSV, a per-file 202 response, and source_processed / source_failed webhooks.
PKCE¶
Supported and recommended, not required. A flow started with a code_challenge must be
completed with its code_verifier. There is no downgrade to a secret. A flow started without PKCE uses
the client secret.
Redirect URIs¶
Three accepted types (RFC 8252): claimed https,
loopback http (localhost or 127.0.0.1, any port), and private-use custom schemes
(com.example.app://oauth/callback, myapp://callback, reverse-DNS not required). https and
loopback match by sub-path. Custom schemes match exactly, so register the precise string your
app presents. Public plain http:// and URIs with a #fragment are rejected. Details:
accepted formats.
Tokens¶
| Token | Lifetime |
|---|---|
| Access token | 15 minutes (JWT, RS256). |
| Refresh token | Until the user revokes your app. |
On a 401, refresh and retry instead of restarting the OAuth flow. Token errors follow
RFC 6749 §5.2: a JSON body with an error
code (invalid_grant, invalid_client, invalid_request, and so on).
Going live¶
Your app is private (only you can connect) until you fill in four fields: description, URL, image, and privacy statement. Anyone else who opens your authorization link, including you on a second account, sees a "still private" page that names their account and lists what's missing. Save the four fields and the app is public immediately: no review, no queue. See Going live.