External servicesConnecting and calling
OAuth grants
not builtThis page is the design; Jadpo doesn't do this yet. See What is built.
On this page
Use a grant when your app acts at another service for each of its users or organisations: a user's Google Calendar, an organisation's Xero, a Slack workspace install. Each owner connects their own account, and your app calls the service with that owner's permission.
Signing in proves to your app who a person is. A grant goes the other way: your app acts at another service, with that owner's consent. It isn't a way to sign in. Your app acting as an OAuth provider, for other apps to sign in with, is a third, separate thing.
// app.jadpocalendar: { package: GoogleCalendar.v3 domains: [scheduling] credential: OAuthGrant { owner: User client_id: config.google_client_id client_secret: config.google_client_secret scopes: ["calendar.events"] return_to: "/settings/integrations" }}// domains/scheduling/actions/add_to_calendar.jadpoattempt calendar.events.insert(event, on_behalf_of: booking.organiser_id) { grant_missing: CalendarNotConnected grant_revoked: CalendarNotConnected}
The grant is the connection's credential
A connection's credential may be a grant owned by one of the app's
records. The owner is User or any entity, such as Organisation. The
grant belongs to that record, not to a session, so it outlives every
session and is used from subscribers and schedules, which run with no
caller. The provider's OAuth facts (its authorize, token and revoke
addresses) are oauth: in the package's provider.jadpo, and each
operation declares the scope it needs (scope: "calendar.events").
Nothing is specific to one provider, and the connection's name is the
app's.
Connecting
Connecting is two generated endpoints, not pages:
POST /connections/<name>/start, called by the signed-in caller, returns the provider's consent address. It needs a signed-in caller, soauthentication: RouteAuthentication.noneisn't available on it. It stores the OAuthstateon the server, against the caller's browser session or API token.- The provider sends the person back to the generated
GET /connections/<name>/callback. It finds thestate, so a consent can complete only once, for the caller who started it. It exchanges the code with PKCE, stores the grant, and redirects to the frontend'sreturn_to, a path on the app's own site, with success or failure.
The callback needs no cookie, so the app's own API-token clients connect
the same way as browsers. Both routes appear in jadpo routes, OpenAPI and
the API record, marked generated
(Generated routes).
Where grants are kept
The runtime holds grants, as it holds sessions, in a jadpo_grants
system table in the owner entity's store, so grants need a database.
Their tokens are encrypted with
application { grants: { encryption_key: config.grant_encryption_key } },
a SigningKey setting. That key is a secret, rotated as a signing_key:
is: the current value encrypts, and its _PREVIOUS value is still read
(Signing keys).
encryption_key: is one of the places in application { } a secret may be
read (Secrets). No code can read a
grant.
Calling with a grant
Every call names whose grant it uses: on_behalf_of: owner_id, an id of the
owner's entity that the action has loaded or been given. The caller of the
route need not be the owner: a scheduled job acting for an organisation
names the organisation. grant_missing and grant_revoked are failures the
app maps, as the example maps both to CalendarNotConnected.
Tokens are refreshed for you: renewed before they expire, once when
several calls race, and a rotated refresh token kept. A revoked grant
gives grant_revoked and a GrantRevoked event, so the app can ask the
owner to connect again.
Scopes are checked when you compile: calling an operation whose scope the connection doesn't request is an error.
Who may connect
Policy decides who may connect for an owner. Connecting
and disconnecting need the update grant on the owner's record. So a user
connects their own account, and an organisation's grant needs whatever lets
the caller update the organisation. Grants bring in no new kind of
permission, and the start route checks this before it answers.
Disconnecting
The generated POST /connections/<name>/disconnect, and its operation,
disconnect a grant, revoking it at the provider when the package declares
how. Erasing a person or a record (Erasure)
deletes and revokes its grants.
In development and tests
Calls are faked by default. jadpo dev grants add <connection> --owner <id>,
not built yet, makes a fake grant, and tests declare grants as fixtures.
Later
Each of these comes when an app needs it: several accounts per owner, scopes added after connecting, provider notifications per grant, and GitHub App installation tokens (a credential scheme in the runtime).
OAuth grants, their routes,
on_behalf_of:andjadpo dev grants addaren't built yet, and nor is acting as an OAuth provider (What is built).
Markdown for agents: /docs/oauth-grants.md.Built from Jadpo main at 7b64d64.