TempoLife

TempoLifeFeaturesSign in with TempoLife

Sign in with TempoLife

Authorization code with PKCE, five read-only scopes, exact redirect URI matching and user-revocable consent. The specification is published and client registration is open; the authorization endpoint is not live yet.

Specification · Register a client

The authorization server is not live. There is no /oauth/authorize, no /oauth/token and no consent screen in production today — a request to any of them returns 404. What is open is client registration: you can reserve a client id and build against the specification below, and your registration moves from pending to approved when the endpoints ship. Do not put a "Sign in with TempoLife" button in front of users yet.

5scopes
0write scopes
code + PKCEflow
spec publishedstatus

What it will be for

A recipe site that wants to show a reader their own remaining calories for the day. A gym app that wants the member's sleep beside their training load. A dietitian tool that wants a client's food log with the client's explicit permission instead of a screenshot. All three want the same thing: read access to one person's data, granted by that person, scoped to what they agreed to, and revocable in one click.

That is the whole product. It is not a login system for your app — TempoLife will not be a general identity provider, and the profile scope deliberately does not include the email address, so you cannot use it to build an account for someone. It is a data-sharing consent flow with an authentication step in front.

Scopes

Every scope is read-only. There is no scope that lets a client write to a TempoLife account.
ScopeNameGrantsDoes not grant
profileBasic profileA stable pseudonymous account id, the display name and the account country.Not the email address, not the password, not the date of birth.
meals:readMealsLogged meals with their timestamp, name, portion and macronutrients.Not the meal photographs, and not the free-text notes attached to a meal.
steps:readStepsDaily step totals and the hourly buckets behind the last-24-hours curve.Not location, not routes — TempoLife stores neither.
sleep:readSleepSleep sessions with length and stage breakdown, plus naps. Dated by wake date.Not raw sensor samples; TempoLife only holds the summarised session.
weight:readWeightWeigh-ins as a date and a mass in grams.Not body composition, not goals, not the projection.

Ask for the least you need. A consent screen listing five scopes converts worse than one listing two, and the request will be reviewed at registration: a client asking for sleep and weight when its product is a recipe box will be asked why.

The flow, request by request

Standard OAuth 2.1 shapes. Everything below is the specification you can implement against; none of these URLs answer today.

1. Generate a PKCE pair

Before you send the user anywhere, create a high-entropy code_verifier and its SHA-256 challenge. The verifier never leaves your server; the challenge is what travels in the browser. This is what makes an intercepted authorization code useless to an attacker.

<?php
$verifier  = rtrim(strtr(base64_encode(random_bytes(64)), '+/', '-_'), '=');
$challenge = rtrim(strtr(base64_encode(hash('sha256', $verifier, true)), '+/', '-_'), '=');
$state     = bin2hex(random_bytes(16));
// Store $verifier and $state in the user's server-side session, not in a cookie
// you also read from JavaScript, and not in the URL.

2. Send the user to the authorization endpoint

GET /oauth/authorize
      ?response_type=code
      &client_id=tlc_0123456789abcdef
      &redirect_uri=https%3A%2F%2Fpartner.example%2Fcallback
      &scope=profile%20meals%3Aread
      &state=9f1c2a...
      &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
      &code_challenge_method=S256
Host: tempolife.app

code_challenge_method must be S256. The plain method will be rejected, and so will a request with no challenge at all — PKCE is mandatory for every client type, including confidential ones.

3. The user sees the consent screen

Below is what the screen will show. It is a mock-up rendered on this page for review, not a live screen — clicking nothing here does anything.

Mock-up: the consent screen

Recipe Box wants to access your TempoLife account.

Read-only. Recipe Box cannot change or delete anything in your TempoLife account. You can withdraw this at any time in Settings. Redirects to https://partner.example/callback.

AllowCancel

Three deliberate details in that screen. The exact redirect host is shown, because that is the single most useful anti-phishing signal a consent screen can give. Each scope is stated with what it does not include, because "access your meals" without a boundary makes people assume the worst. And "read-only" is asserted at the point of decision rather than buried in terms.

4. TempoLife redirects back with a code

HTTP/1.1 302 Found
Location: https://partner.example/callback?code=tlac_9c1f…&state=9f1c2a…

Compare state against what you stored before you do anything else. A callback with a missing or mismatched state is an attack or a bug; either way, stop. The code is single-use and expires after 60 seconds, so exchange it immediately rather than queueing it.

5. Exchange the code for tokens

POST /oauth/token HTTP/1.1
Host: tempolife.app
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <base64(client_id:client_secret)>

grant_type=authorization_code
&code=tlac_9c1f…
&redirect_uri=https%3A%2F%2Fpartner.example%2Fcallback
&code_verifier=<the verifier from step 1>
{
  "access_token": "tlat_…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "tlrt_…",
  "scope": "profile meals:read"
}

The redirect_uri is repeated here and must match the one from step 2 exactly, as an anchored string comparison. Note the returned scope: a user may untick a scope on the consent screen, so what you get back can be narrower than what you asked for. Read that field rather than assuming.

6. Call the API

GET /oauth/userinfo HTTP/1.1
Host: tempolife.app
Authorization: Bearer tlat_…

Access tokens last 60 minutes. Refresh with grant_type=refresh_token; refresh tokens rotate on use, so store the new one and discard the old. If a refresh is ever replayed with an already-used token, the whole grant will be revoked — that is the standard defence against a stolen refresh token, and it means a buggy client that retries a refresh will log its users out. Serialise your refreshes.

Revocation

Two directions, and both will work.

Consent is recorded as one row per client per user, with a granted timestamp and a revoked timestamp. Revoking sets the timestamp rather than deleting the row, so both sides can answer "when did this stop" later. Deleting a TempoLife account removes the user's consents entirely.

What a client must satisfy

RequirementRuleWhy
Redirect URIsExact string match against a registered URI. No wildcards, no path prefixes, no query-string variation.Prefix matching is how authorization codes get redirected to an attacker's path on a shared host.
Schemehttps, or http only on 127.0.0.1 / [::1] for local development.A code delivered over plaintext is a code anyone on the network has.
FragmentsNever accepted in a registered URI.The fragment is where the implicit flow put tokens. It has no business in a redirect target.
Implicit flowNot implemented and will not be.Tokens in URLs leak through history, referrers and logs.
PKCEMandatory, S256 only.Makes a stolen code unusable without the verifier.
Code lifetime60 seconds, single use.A code is a bearer credential; it should not survive long enough to be found in a log.
Client secretStored by TempoLife as a SHA-256 hash. Shown once at registration.A provider that can email you your secret can also leak it.
Public clientsA mobile or SPA client registers with no usable secret and relies on PKCE.A secret shipped in an app binary is not a secret.
StateRequired. Compared before the code is used.It is the CSRF defence for the callback.

What ships when the endpoints go live

So that nobody has to guess what "live" will mean: an authorization endpoint that renders the consent screen for a signed-in user, a token endpoint implementing the authorization code and refresh token grants, a revocation endpoint, a userinfo endpoint scoped to profile, and per-scope read endpoints for meals, steps, sleep and weight. Alongside them, a discovery document at /.well-known/oauth-authorization-server so libraries can configure themselves, and a consent management screen in account settings. Registered clients move from pending to approved as part of that release; nothing you register now is lost.

Until then, this page is a specification and a queue. If you need TempoLife data before that, the public food API is live today and needs no account at all — it just does not carry anyone's personal log.

Frequently asked questions

Can I ship a "Sign in with TempoLife" button today?

No. The authorization endpoint does not exist yet, so the button would lead to a 404. Register a client, build against this specification, and wait for the endpoints.

Why authorization code with PKCE and not implicit?

The implicit flow returns tokens in a URL fragment, where they land in browser history, in referrer headers and in any script on the page. It has been discouraged for years. TempoLife will not implement it, so do not design around it.

Will there be write scopes?

Not in this design. Every scope here is read-only, which matches the rest of the platform: nothing in TempoLife accepts a meal, a weight or a step count from a third party today.

How does a user take access away?

From their own account page, per client. Revocation kills the refresh token immediately; an access token already issued dies at the end of its hour. Deleting a TempoLife account cascades every consent with it.

Is my client secret recoverable?

No. Only a SHA-256 hash of it is stored. If you lose it you register a new client — that is the intended cost of storing secrets properly.

Source: TempoLife platform specification, published for partner review · checked 2026-09-02

Register a clientThe live food API