Skip to content

OAuth

client.oauth: /oauth/authorize and /api/v1/oauth/...

Bases: Resource

OAuth2 and OpenID Connect: build sign-in links, exchange codes, read the user's claims.

Most apps use sweatstack.fastapi or sweatstack.streamlit, which do this for you.

authorization_url(*, client_id, redirect_uri, code_challenge=None, scope=DEFAULT_SCOPE, prompt=None, state=None, nonce=None, integration=None, email_auth_mode=None)

Builds the URL to send a user to for signing in with SweatStack.

Endpoint: GET /oauth/authorize

Parameters:

  • client_id (str) –

    Your app's client ID.

  • redirect_uri (str) –

    Where SweatStack sends the user back to; must be registered.

  • code_challenge (str | None, default: None ) –

    A PKCE challenge from :meth:generate_pkce_params.

  • scope (str, default: DEFAULT_SCOPE ) –

    Space-separated scopes.

  • prompt (str | None, default: None ) –

    The OpenID Connect prompt. Leave it out and SweatStack skips the consent screen when the user's grant already covers the request; "consent" always shows it.

  • state (str | None, default: None ) –

    An opaque value to check on the way back, against CSRF.

  • nonce (str | None, default: None ) –

    Echoed in the id_token that comes with the code exchange.

  • integration (str | None, default: None ) –

    One provider ("garmin_connect", "intervals_icu", "wahoo"): the user skips the sign-in chooser and goes straight to it.

  • email_auth_mode (str | None, default: None ) –

    "existing_only" presents email sign-in as for existing accounts, so a new user picks a wearable sign-in instead.

Returns:

  • str ( str ) –

    The authorization URL.

Examples:

from sweatstack import Client

client = Client()
verifier, challenge = client.oauth.generate_pkce_params()
url = client.oauth.authorization_url(
    client_id="YOUR_CLIENT_ID",
    redirect_uri="http://localhost:8000/callback",
    code_challenge=challenge,
)

exchange_code(code, *, client_id, code_verifier=None, client_secret=None, persist=False)

Exchanges an authorization code for tokens, and signs this client in with them.

Endpoint: POST /api/v1/oauth/token

The tokens stay in this client. In a server, keep them with the user's session; they are that user's credentials, not the machine's.

Parameters:

  • code (str) –

    The code from the redirect back to your app.

  • client_id (str) –

    Your app's client ID.

  • code_verifier (str | None, default: None ) –

    The PKCE verifier, if you sent a challenge.

  • client_secret (str | None, default: None ) –

    Your app's secret, for confidential clients.

  • persist (bool, default: False ) –

    Also save the tokens on this machine for later sessions, as authenticate() does. Never in a server.

Returns:

  • TokenResponse ( TokenResponse ) –

    access_token, refresh_token and their metadata.

Raises:

  • SweatStackAuthError –

    If the code or credentials are rejected.

  • SweatStackAPIError –

    If the API request fails for any other reason.

  • ValueError –

    If persist is set on a client built with ambient_credentials=False.

Examples:

from sweatstack import Client

client = Client()
tokens = client.oauth.exchange_code(
    "code-from-redirect", client_id="YOUR_CLIENT_ID", code_verifier="verifier"
)

generate_pkce_params()

Generates a PKCE code verifier and its S256 code challenge.

Returns:

  • str –

    tuple[str, str]: (code_verifier, code_challenge). Send the challenge with

  • str –

    meth:authorization_url; keep the verifier for :meth:exchange_code.

Examples:

from sweatstack import Client

client = Client()
verifier, challenge = client.oauth.generate_pkce_params()

userinfo()

Retrieves the OpenID Connect claims of the user, and why they may have no data.

Endpoint: GET /api/v1/oauth/userinfo

Requires the openid scope. sub and registered_at always come; the name claims (name, given_name, family_name) need profile and email needs the email scope, otherwise they are None. The response also has issue (beta): None when there is nothing to say, otherwise the one thing to tell the user now. issue.destination says the user can fix it in the Portal: pass it to client.portal.sessions.create() when they click. It is None on delegated tokens and on syncing issues, so show a button only when it is set. Issues are reported only for the capabilities your app's scopes reach. Without the openid scope, client.profile.status() gives the same issue.

Returns:

Raises:

Examples:

from sweatstack import Client

client = Client()
user = client.oauth.userinfo()
if user.issue:
    print(user.issue.message)