# Acting for each user: OAuth grants

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.

```jadpo
// 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: ["calendar.events"]
        return_to: "/settings/integrations"
    }
}

// domains/scheduling/actions/add_to_calendar.jadpo
attempt 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
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:

1. `POST /connections/<name>/start`, called by the signed-in caller,
   returns the provider's consent address. It stores the OAuth `state` on
   the server, against the caller's session or API token.
2. The provider sends the person back to the generated
   `GET /connections/<name>/callback`. It finds the `state`, exchanges the
   code with PKCE, stores the grant, and redirects to the frontend's
   `return_to` with success or failure.

The callback needs no cookie, so the app's own API-token clients connect
the same way.
[Acting for a user at another service: OAuth grants](acting-for-a-user-at-another-service-oauth-grants.md)
covers how this touches sign-in.

## 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 } }`.
That key is a secret, read and rotated as signing keys are, with its
previous key kept ([Signing keys](signing-keys.md)).
No code can read a grant.

## Calling with a grant

Every call names whose grant it uses: `on_behalf_of: owner_id`.
`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](policy.md) decides who may connect for an owner. Connecting
and disconnecting need the `update` grant on the owner's record, so
grants bring in no new kind of permission.

## 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](erasure.md))
deletes and revokes its grants.

## In development and tests

Calls are faked by default. `jadpo dev grants add <connection> --owner <id>`
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 aren't built yet ([What is built](external-services-built.md)).
