~netlandish/links-wiki

3bf741892d14f3aade70c8d94cb6b9829608490c — Peter Sanchez 2 months ago 371dc94
Adding oauth documentation
2 files changed, 336 insertions(+), 9 deletions(-)

M graphql.md
A oauth.md
M graphql.md => graphql.md +9 -9
@@ 6,15 6,6 @@ description: 'Using the LinkTaco API (GraphQL)'
LinkTaco offers an API for our services via [GraphQL](https://graphql.org).
This page documents the traits our GraphQL API.

# Attribution

Our GraphQL setup is heavily based on the [SourceHut][srht] GraphQL services.
As such this document is a modified version of the [original SourceHut
document][srht og].

[srht]: https://sourcehut.org "SourceHut"
[srht og]: https://man.sr.ht/graphql.md "SourceHut GraphQL"

# API Info

The API is accessed via the location `https://api.linktaco.com`.


@@ 243,3 234,12 @@ An additional field is provided by the `version` resolver: `deprecationDate`.
The field, If not null, indicates the date at which a major
version increment is planned. Interested parties may want to monitor this value
and incorporate it into their planning.

# Documentation Attribution

Our GraphQL setup is heavily based on the [SourceHut][srht] GraphQL services.
As such this document is a modified version of the [original SourceHut
document][srht og].

[srht]: https://sourcehut.org "SourceHut"
[srht og]: https://man.sr.ht/graphql.md "SourceHut GraphQL"

A oauth.md => oauth.md +327 -0
@@ 0,0 1,327 @@
---
title: OAuth 2.0 via LinkTaco
description: 'Using OAuth 2.0 with LinkTaco API'
---

LinkTaco offers an OAuth 2.0-compatible ([RFC 6749][RFC 6749]) authorization
process for access to the GraphQL API. This document explains the
specific details of our RFC 6749 implementation and is intended to be
accompanied by a reading of the RFC.

[RFC 6749]: https://tools.ietf.org/html/rfc6749

## Constraints

Our OAuth 2.0 implementation has the following caveats:

- Only confidential clients are supported; public clients are not allowed

In the use-case of a native application, where the public client type specified
in RFC 6749 is preferred, the application must either provide a web server
component which completes the confidential authentication process and shares
the access token with the application; or the application should ask the user
to provide a personal access token, which can be generated at
[linktaco.com/oauth2/personal][oauth personal]. In the latter case, the
application should inform the user of the grant string which supports only the
minimum access level required to use the application's features.

- The implicit grant (section 4.2) is not supported for the same reasons
- The resource owner password credentials grant (section 4.3) is not supported
- The client credentials grant (section 4.4) is not used

[oauth personal]: https://linktaco.com/oauth2/personal

## Access scopes

OAuth 2.0 clients using LinkTaco must specify explicitly the list of features they
wish to be granted permission to use.

The format of the scope string, per [section 3.3][RFC 6749:3.3] is a
space-delineated list of grants. Each grant has three components: service,
scope, and access kind; and is formatted as `scope:access kind`.

[RFC 6749:3.3]: https://tools.ietf.org/html/rfc6749#section-3.3

The scope is the specific feature of that service which the grant applies to,
such as "PROFILE". The list of access scopes available for each API is
documented in the GraphQL schema for that API in the `enum AccessScope` type.

The access kind is either `RO` or `RW`; respectively referring to read-only or
read/write access to that scope.

Example: `PROFILE:RO LINKS:RW ANALYTICS:RO`

The scopes necessary to use each GraphQL resolver are also indicated in the
GraphQL schema with the `@access` directive.

The access scopes supported by LinkTaco, and the required scopes to utilize
each resolver, are documented in the [GraphQL schema][gql schema].

[gql schema]: https://git.code.netlandish.com/~netlandish/links/tree/master/item/api/graph/schema.graphqls

They can also be requested programmatically by fetching the following URL:

https://api.linktaco.com/query/api-scopes.json

The following scopes are available for use:

- PROFILE
- LINKS
- LISTS
- SHORTS
- ORGS
- DOMAINS
- BILLING
- ANALYTICS
- QRCODES

## Client registration

OAuth 2.0 clients may be registered at [linktaco.com/oauth2/clients][oauth
clients]. You will be issued a client ID and secret per [sections 2.2 &
2.3][RFC 6749:2.2]. The client ID is a [UUID][RFC 4122], and the secret is
64-byte random string, [base64][RFC 4648] encoded. It is not necessary to
interpret them; they are passed verbatim to our OAuth 2.0 endpoints.

[oauth clients]: https://linktaco.com/oauth2/clients
[RFC 6749:2.2]: https://tools.ietf.org/html/rfc6749#section-2.2
[RFC 4122]: https://tools.ietf.org/html/rfc4122
[RFC 4648]: https://tools.ietf.org/html/rfc4648

## Authorization endpoint

The authorization endpoint (see [section 4.1.1][RFC 6749:4.1.1]) is
`https://linktaco.com/oauth2/authorize`. Note that LinkTaco differs from the
specification in that it REQUIRES the scope parameter to be provided; per
[section 3.3][RFC 6749:3.3] meta.sr.ht interprets the absence of the scope
parameter as an invalid scope and will cause the request to fail.

### The OAuth 2.0 consent page

To start the exchange, direct the user to the following URL:

    https://linktaco.com/oauth2/authorize

Provide the following parameters in the query string:

<dl>
    <dt>client_id</dt>
    <dd>The client ID assigned to you in the previous step.</dd>
    <dt>scope</dt>
    <dd>A list of scopes you're requesting &mdash; see next section.</dd>
    <dt>response_type</dt>
    <dd>This **must** be set to `code`.</dd>
    <dt>state</dt>
    <dd>Optional: an arbitrary string — see notes.</dd>
    <dt>redirect_uri</dt>
    <dd>Optional: your application URI for redirect the user to &mdash; see notes.</dd>
</dl>

The optional `state` field is returned to you after the redirect. One example
use-case of this field is generating a token before directing the user to the
authorization page and validating it after the redirect, to prevent users from
initiating the OAuth flow without you. Most users don't need to worry about
this.

The optional `redirect_uri` must match the redirect uri specified in the OAuth
2.0 client configuration.

The authorization code issued is a 32 character hexadecimal string, and it must
be used within 5 minutes.

[RFC 6749:4.1.1]: https://tools.ietf.org/html/rfc6749#section-4.1.1

## Access token endpoint

The access token endpoint (see [section 4.1.3][RFC 6749:4.1.3]) is
`https://linktaco.com/oauth2/access-token`. The optional `redirect_uri` must
match the redirect uri specified in the OAuth 2.0 client configuration.  HTTP
Basic authentication is also recommended per [section 2.3.1][RFC 6749:2.3.1].
Our access token response will always set the token type to "bearer".

[RFC 6749:4.1.3]: https://tools.ietf.org/html/rfc6749#section-4.1.3
[RFC 6749:2.3.1]: https://tools.ietf.org/html/rfc6749#section-2.3.1

# Supported authentication profiles

There are various ways, in order of complexity and flexibility, of
authenticating with the API. Your goal is to obtain an **access token**, which
can be used to authenticate with the API.

## Personal Access Tokens

The easiest and fastest way to authenticate with LinkTaco is to [create a personal
access token][oauth personal]. This token will have
unrestricted access to the API and can be used like a normal access token
to authenticate API requests (see [Authenticating API
requests](#authenticating-api-requests)).

**Warning**: do not give your personal access tokens to third parties. Any third
party which encourages you to give them a personal access token should instead
develop an OAuth 2.0 client as described in the next section.

## OAuth 2.0 Clients

Personal access tokens are suitable for authenticating yourself, but if you
intend to provide software that other LinkTaco users can log into, you should
[create an OAuth client][oauth clients] instead. The OAuth 2.0
flow allows end-users to grant your client only the privileges it requires to
execute its duties and to revoke this access at any time. This is accomplished
through an **OAuth 2.0 exchange**:

1. Direct the user (in a web browser) to a special page on LinkTaco where we
   present them with the permissions you're asking for and they provide
   consent.
2. The user is redirected back to your application's **configured redirect
   URI**. We will add an **exchange token** to the query string via the `code`
   parameter.
3. Your application sends an HTTP request directly to LinkTaco using this
   exchange token and your client credentials to obtain an access token.

To this end, you need to have an HTTP server running somewhere (`localhost` is
suitable for testing) that the user can be redirected to upon consenting to the
permissions you requested. Decide on a URL we can redirect the user to in your
application, and fill out the base redirect URI accordingly.

Upon submitting the form, your **client ID** and **client secret** will be shown
to you. **Record your client secret now**. It will not be shown to you again.

### OAuth 2.0 scopes

API endpoints on LinkTaco generally require an access token valid for a specific
*scope* to be used. This allows you to only request access to the subset of the
API that you require. Scopes are written with the following notation(s):

    name
    name:access

`name` is the name of the OAuth 2.0 scope, which is documented alongside the API
resolvers which require it (e.g. `getFeed` requires the `PROFILE`
scope). `access` is either `RO` or `RW`, and may be omitted (default:
`RO`).

Scopes sent to the authorize endpoint should be space separated. For instance:

`PROFILE:RO LINKS:RW ANALYTICS`

### The application redirect

Once the user consents, they will be redirected to your configured application
`redirect_uri`.  We will update your redirect URI with some additional query
string parameters you can use for the next steps:

<dl>
    <dt>code</dt>
    <dd>An exchange token you can use to obtain an access token in the next step.</dd>
    <dt>iss</dt>
    <dd>The base URI for the api server. See [RFC 2907][].</dd>
    <dt>state</dt>
    <dd>If present, it will be the "state" value given by your application.</dd>
    <dt>error</dt>
    <dd>If present, indicates that an error occurred in the process — see notes.</dd>
    <dt>error_description</dt>
    <dd>If present, a human friendly error string, if that human is an engineer.</dd>
    <dt>error_uri</dt>
    <dd>If present, a URL to a webpage with more details for the given error.</dd>
</dl>

Possible values for `error`:

- `access_declined`
- `invalid_request`
- `invalid_scope`
- `server_error`

**Important**: the user is able to **edit** the scopes you've requested before
consenting. You must handle the case where the scopes returned when obtaining
an access token do not match the scopes requested.

[RFC 9207]: https://datatracker.ietf.org/doc/html/rfc9207

### Obtaining an access token

Once your application retrieves the exchange token from the redirect, you can
submit an HTTP POST request to `https://linktaco.com/oauth2/access-token`. The
request `Content-Type` should be `application/x-www-form-urlencoded` and
include the following parameters:

<dl>
    <dt>client_id</dt>
    <dd>The client ID assigned to you when you registered the application.</dd>
    <dt>client_secret</dt>
    <dd>The client secret assigned to you when you registered the application.</dd>
    <dt>grant_type</dt>
    <dd>The request type being requested. Must be `authorization_code` or
    `refresh_token`.</dd>
    <dt>code</dt>
    <dd>The exchange token issued in the previous step.</dd>
    <dt>refresh_token</dt>
    <dd>Only required if the `grant_type` is set to `refresh_token`.</dd>
</dl>

You will receive a response like this:

```json
{
    "access_token": "your access token",
    "token_type": "bearer",
    "expires": 1736201028,
    "scope": "PROFILE:RO ANALYTICS:RW",
    "refresh_token": "refresh token"
}
```

The `expires` date is in Unix epoch format.

You can now use this token for [Authenticating API
requests](#authenticating-api-requests).

# Authenticating API requests

Authenticating your API request is simple once you have an access token. You
just need to set the `Authorization` header to `Bearer your-access-token`. For
example:

```sh
oauth_token=your oauth token
curl \
  --oauth2-bearer "$oauth_token" \
  -H 'Content-Type: application/json' \
  -d '{"query": "{ version { major, minor, patch } }"}' \
  https://api.linktaco.com/query
```

## Refreshing access tokens

You can refresh an access token programmatically to avoid service disruptions.
To refresh a token simply follow the same steps in "Obtaining an access token"
above. You must:

- Specify `refresh_token` in the `grant_type` parameter.
- Give the access token's refresh token via the `refresh_token` parameter.

Upon a successful refresh you will receive the same response json as when
obtaining a new token. You should then update your own database with the new
access token, refresh token, and expiration date.

## Rotating your client secret

On the security tab of your OAuth 2.0 client's dashboard (which can be accessed
from the [OAuth clients page][oauth clients]), you can rotate your client
secret, in the event that it is compromised.

## Revoking access tokens

On the security tab of your OAuth 2.0 client's dashboard (which can be accessed
from the [OAuth clients page][oauth clients]), you can revoke all issued access
tokens at once, in the event some or all of them are compromised. Users will
have to repeat the authorization flow.

# Documentation Attribution

Our GraphQL setup is heavily based on the [SourceHut][srht] GraphQL services.
As such this document is a modified version of the [original SourceHut
document][srht og].

[srht]: https://sourcehut.org "SourceHut"
[srht og]: https://man.sr.ht/meta.sr.ht/oauth.md

Do not follow this link