# Acting for a user at another service: OAuth grants

Signing in proves to your app who a person is. An OAuth grant goes the
other way: your app acts at another service, such as a user's Google
Calendar, an organisation's Xero or a Slack workspace, 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.

The connection and its calls are in
[Integrations](acting-for-each-user-oauth-grants.md). This
topic covers what touches identity. A connection's credential is an
`OAuthGrant`:

```text
// app.jadpo
calendar: {
    package: GoogleCalendar.v3
    domains: [scheduling]
    credential: OAuthGrant {
        owner: User
        client_id: config.google_client_id
        client_secret: config.google_client_secret
        scopes: [...]
        return_to: "/settings/integrations"
    }
}
```

- **A grant is owned by one of the app's records**: `owner:` is the
  principal's user entity or any other entity (`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.
- **Connecting is endpoints, not pages.** `POST /connections/<name>/start`
  is a generated route that needs a signed-in caller (`authentication:
  RouteAuthentication.none` is not available on it) and answers with the
  provider's consent address. It keeps the `state` on the server against
  the caller's browser session or API token. The provider sends the browser
  back to the generated `GET /connections/<name>/callback`, which finds the
  `state` (a consent can complete only once, for the caller who started
  it), exchanges the code with PKCE, stores the grant and redirects to the
  frontend's `return_to`, a path on the app's own site, with success or
  failure. The callback needs no cookie, so your own API-token clients
  connect exactly as browsers do. Both appear in `jadpo routes`, OpenAPI
  and the API record, marked generated
  ([Generated routes](generated-routes.md)).
- **Who may connect for an owner is [Policy](policy.md)'s.** 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. There is no new kind
  of permission for it. The start route checks it before answering.
- **Grants are held by Jadpo**, like 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. It rotates as
  a `signing_key:` does: the current value encrypts, and its `_PREVIOUS`
  value is still read ([Signing keys](signing-keys.md)).
  Code can never read the tokens, and Jadpo refreshes them before they
  expire. `encryption_key:` is one of the places in `application { }` a
  secret may be read ([Secrets](secrets.md)).
- **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.
  `grant_missing` and `grant_revoked` are failures your app maps to its
  own ([Integrations](external-services.md)).
  The caller of the route need not be the owner: a scheduled job acting for
  an organisation names the organisation.
- **Disconnecting** is the generated `POST /connections/<name>/disconnect`
  and its operation, revoking at the provider when the package declares
  how. Erasing a person or a record ([Personal data](personal-data.md))
  deletes and revokes its grants. A revoked grant gives the `GrantRevoked`
  event, so your app can ask the owner to reconnect.
- **In development and tests**, `jadpo dev grants add <connection> --owner
  <id>` makes a fake grant, and tests declare grants as fixtures.

Not built yet: OAuth grants, their routes and `on_behalf_of:`. Acting as
an OAuth provider isn't built either.
