diff --git a/changes/changelog.d/api.doc b/changes/changelog.d/api.doc new file mode 100644 index 000000000..017949411 --- /dev/null +++ b/changes/changelog.d/api.doc @@ -0,0 +1 @@ +Updated API developer documentation (#1912, #1909) diff --git a/docs/api.rst b/docs/api.rst deleted file mode 100644 index 650b3885e..000000000 --- a/docs/api.rst +++ /dev/null @@ -1,6 +0,0 @@ -Funkwhale API -============= - -Funkwhale API is still a work in progress and should not be considered as -stable. We offer an `interactive documentation using swagger `_ -were you can browse available endpoints and try the API. diff --git a/docs/developer_documentation/api/authentication.md b/docs/developer_documentation/api/authentication.md new file mode 100644 index 000000000..a8af39c95 --- /dev/null +++ b/docs/developer_documentation/api/authentication.md @@ -0,0 +1,137 @@ +# API authentication + +Funkwhale uses the OAuth [authorization grant flow](https://tools.ietf.org/html/rfc6749#section-4.1) for external apps. This flow is a secure way to authenticate apps that requires a user's explicit consent to perform actions. + +```{mermaid} +%%{init: { 'sequence': {'mirrorActors': false} } }%% + sequenceDiagram + accTitle: "Funkwhale OAuth token flow" + accDescr: "A sequence diagram showing how apps authenticate with Funkwhale" + autonumber + actor User + participant A as Application + participant F as Funkwhale web interface + participant T as Token endpoint + User ->> A: Log in to Funkwhale + A ->> F: Direct to login screen + F -->> User: Authenticate this app? + User ->> F: Confirm + F -->> A: Authorization code + A ->> T: Authorization code and redirect URI + T -->> A: Access token and refresh token + loop Refresh + A ->> T: Refresh token + T -->> A: Access token + end +``` + +```{contents} Steps +:local: +``` + +## 1. Create an application + +To connect to the Funkwhale API using OAuth, you need to create an **application**. This represents the entity credentials are related to. + +When creating an application you need to define the [**scopes**](https://www.rfc-editor.org/rfc/rfc6749#section-3.3) the application has access to. Scopes define what information your application can access. Each scope can be granted with the following rights: + +- `read:`: grants read-only access to the resource +- `write:`: grants write-only access to the resource + +`read` rights are required to fetch information using a `GET` request. All other actions (`POST`, `PATCH`, `PUT`, and `DELETE`) require `write` priviliges. You may give an application **both** `read` and `write` access to any scope. + +```{list-table} + :header-rows: 1 + + * - Scope + - Description + * - `read` + - Read-only access to all data + * - `write` + - Read-only access to all data + * - `:profile` + - Access to profile data (email address, username, etc.) + * - `:libraries` + - Access to library data (uploads, libraries, tracks, albums, artists, etc.) + * - `:favorites` + - Access to favorites + * - `:listenings` + - Access to history + * - `:follows` + - Access to followers + * - `:playlists` + - Access to playlists + * - `:radios` + - Access to radios + * - `:filters` + - Access to content filters + * - `:notifications` + - Access to notifications + * - `:edits` + - Access to metadata edits + +``` + +Next, you need to define a [**Redirect URI**](https://www.rfc-editor.org/rfc/rfc6749#section-3.1.2). This is the location the user is redirected to once they authenticate your app. This can be any URI you want. + +```{note} +Funkwhale supports the `urn:ietf:wg:oauth:2.0:oob` redirect URI for non-web applications. If you use this URI, the user is shown a token to copy and paste. +``` + +Once you've decided on your scopes and your redirect URI, you can create your app using one of the following methods: + +1. Visit `/settings/applications/new` on your Funkwhale pod while logged in +2. Send a `POST` request to `/api/v1/oauth/apps`. See our [API documentation](https://docs.funkwhale.audio/swagger/) for more information + +Both methods return a [**client ID**](https://www.rfc-editor.org/rfc/rfc6749#section-2.2) and a [**secret**](https://www.rfc-editor.org/rfc/rfc6749#section-2.3.1). + +## 2. Get an authorization code + +```{important} +Authorization codes are only valid for 5 minutes after the user approves the request. +``` + +You need an [**authorization code**](https://www.rfc-editor.org/rfc/rfc6749#section-1.3.1) to request an access token for your user. This code confirms to the server that a user has authorized access to their account. + +To fetch an authorization code, you need to send the user to their Funkwhale pod to authenticate. This sends an [authorization request](https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2) to the server. + +To do this, call the `/authorize` endpoint with the following URL encoded query parameters: + +- `client_id`* - Your application's client ID +- `response_type`* - Must be set to `code`. +- `redirect_uri` - Your redirect URI +- `scope` - A list of scopes +- `state` - Used to maintain state between the request and the callback to prevent cross-site request forgery. Typically corresponds with a location in the app (e.g. `/library`) + +Here is an example URL: `https://demo.funkwhale.audio/authorize?response_type=code&scope=read%20write&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fauth%2Fcallback&state=/library&client_id=jDOUfhqLlrbuOkToDCanZmBKEiyorMb9ZUgD2tFQ`. + +When the user authorizes your app, the server responds with an authorization code. See [the OAuth spec](https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2) for more information about this response. + +## 3. Get an access token + +Once you receive your authorization code, you need to [request an access token](https://www.rfc-editor.org/rfc/rfc6749#section-4.1.3). To request an access token, call the `/api/v1/oauth/token` endpoint with the following information: + +- `grant_type`* - Must be set to `authorization_code` +- `code`* - Your application's authorization code +- `redirect_uri`* - Your redirect URI +- `client_id`* Your application's client ID + +The server responds with an [`access_token`](https://www.rfc-editor.org/rfc/rfc6749#section-1.4) and a [`refresh_token`](https://www.rfc-editor.org/rfc/rfc6749#section-1.5). See [the OAuth spec](https://www.rfc-editor.org/rfc/rfc6749#section-4.1.4) for more information about this response. + +You can use this token to authenticate calls from your application to the Funkwhale API by passing it as a request header with the following format: `Authorization: Bearer `. + +## 4. Refresh your access token + +```{important} +When you refresh your token the endpoint returns a new `refresh_token`. You must update your refresh token each time you request a new access token. +``` + +By default, Funkwhale access tokens are valid for **10 hours**. Pod admins can configure this by setting the `ACCESS_TOKEN_EXPIRE_SECONDS` variable in their `.env` file. + +After the access token expires, you must request a new access token by calling the `/api/v1/oauth/token` endpoint with the following information: + +- `grant_type`* - Must be set to `refresh_token` +- `refresh_token`* - Your current refresh token +- `scope` - A list of scopes + +See [the OAuth spec](https://www.rfc-editor.org/rfc/rfc6749#section-6) for more information about this response. diff --git a/docs/developer_documentation/api/index.md b/docs/developer_documentation/api/index.md new file mode 100644 index 000000000..07c63ae04 --- /dev/null +++ b/docs/developer_documentation/api/index.md @@ -0,0 +1,18 @@ +# Funkwhale API + +The Funkwhale API is a [REST API](https://developer.mozilla.org/en-US/docs/Glossary/REST) written in [Python](https://www.python.org/) using the [Django REST framework](https://www.django-rest-framework.org/). It is the central component of the project and houses the application's logic. + +The current API (v1) is **stable**, meaning we are committed to not introducing breaking changes and to maintaining compatibility. We are currently working on Funkwhale API v2, but this is a work in progress and not yet ready for production use. + +```{toctree} +--- +caption: Resources +maxdepth: 1 +--- + +API explorer +authentication +rate_limit +subsonic + +``` \ No newline at end of file diff --git a/docs/developer_documentation/api/rate_limit.md b/docs/developer_documentation/api/rate_limit.md new file mode 100644 index 000000000..c63dfb0b2 --- /dev/null +++ b/docs/developer_documentation/api/rate_limit.md @@ -0,0 +1,51 @@ +# Rate limiting + +Funkwhale supports rate-limiting as of version 0.2.0. Pod admins can choose to rate limit specific endpoints to prevent abuse and improve the stability of the service. If the server drops a request due to rate-limiting, it returns a `429` status code. + +By default, rate limits follow these rules: + +1. Anonymous (unauthenticated) requests are subject to lower limits than authenticated requests +2. `PUT`, `DELETE`, `PUT`, `POST`, and `PATCH` requests are subject to lower limits than `GET` requests + +You can return a full list of scope with their corresponding rate-limits by making a `GET` request to `/api/v1/rate-limit`. + +## HTTP headers + +Each API call returns HTTP headers to pass the following information: + +- What was the scope of the request +- What is the rate-limit associated with the request scope +- How many more requests in the scope can be made within the rate-limit timeframe +- How much time does the client need to wait to send another request + +Here is a full list of supported headers + +```{list-table} +:header-rows: 1 + + * - Header + - Example value + - Description + * - `X-RateLimit-Limit` + - 50 + - The number of requests allowed within a given period + * - `X-RateLimit-Duration` + - 3600 + - The time window, in seconds, during which the number of requests are measured + * - `X-RateLimit-Scope` + - `login` + - The name of the scope computed for the request + * - `X-RateLimit-Remaining` + - 42 + - How many requests can be sent with the same scope before the rate-limit applies + * - `Retry-After` + - 3543 + - How many seconds the client must wait before it can retry. Only applies if `X-RateLimit-Remaining` is `0` + * - `X-RateLimit-Reset` + - 1568126089 + - A timestamp indicating when the `X-RateLimit-Remaining` value will reset + * - `X-RateLimit-ResetSeconds` + - 3599 + - The number of seconds until the `X-RateLimit-Remaining` value resets + +``` diff --git a/docs/developer_documentation/api/subsonic.md b/docs/developer_documentation/api/subsonic.md new file mode 100644 index 000000000..7c36caa70 --- /dev/null +++ b/docs/developer_documentation/api/subsonic.md @@ -0,0 +1,71 @@ +# Subsonic API + +Funkwhale supports a subset of the [Subsonic API's](http://www.subsonic.org/pages/api.jsp) endpoints. This enables users to listen to music stored on their Funkwhale pod through a Subsonic-compatible app. + +We aim to support as many endpoints as we can to give Subsonic users the best possible experience. However, some endpoints require a folder-based endpoint. This doesn't match Funkwhale's internal structure, which means emulating them is difficult. + +## Supported endpoints + +```{note} +We aim to keep this list up-to-date. If you think something is missing, you can see all supported endpoints in the [API views](https://dev.funkwhale.audio/funkwhale/funkwhale/blob/develop/api/funkwhale_api/subsonic/views.py). +``` + +Funkwhale supports both XML and JSON formats for the following Subsonic endpoints: + +- [`createPlaylist`](http://www.subsonic.org/pages/api.jsp#createPlaylist) +- [`deletePlaylist`](http://www.subsonic.org/pages/api.jsp#deletePlaylist) +- [`getAlbum`](http://www.subsonic.org/pages/api.jsp#getAlbum) +- [`getAlbumList2`](http://www.subsonic.org/pages/api.jsp#getAlbumList2) +- [`getArtist`](http://www.subsonic.org/pages/api.jsp#getArtist) +- [`getArtistInfo2`](http://www.subsonic.org/pages/api.jsp#getArtistInfo2) +- [`getArtists`](http://www.subsonic.org/pages/api.jsp#getArtists) +- [`getAvatar`](http://www.subsonic.org/pages/api.jsp#getAvatar) +- [`getCoverArt`](http://www.subsonic.org/pages/api.jsp#getCoverArt) +- [`getIndexes`](http://www.subsonic.org/pages/api.jsp#getIndexes) +- [`getLicense`](http://www.subsonic.org/pages/api.jsp#getLicense) +- [`getMusicFolders`](http://www.subsonic.org/pages/api.jsp#getMusicFolders) +- [`getPlaylist`](http://www.subsonic.org/pages/api.jsp#getPlaylist) +- [`getPlaylists`](http://www.subsonic.org/pages/api.jsp#getPlaylists) +- [`getRandomSongs`](http://www.subsonic.org/pages/api.jsp#getRandomSongs) +- [`getSong`](http://www.subsonic.org/pages/api.jsp#getSong) +- [`getStarred`](http://www.subsonic.org/pages/api.jsp#getStarred) +- [`getStarred2`](http://www.subsonic.org/pages/api.jsp#getStarred2) +- [`getUser`](http://www.subsonic.org/pages/api.jsp#getUser) +- [`ping`](http://www.subsonic.org/pages/api.jsp#ping) +- [`scrobble`](http://www.subsonic.org/pages/api.jsp#scrobble) +- [`search3`](http://www.subsonic.org/pages/api.jsp#search3) +- [`star`](http://www.subsonic.org/pages/api.jsp#star) +- [`stream`](http://www.subsonic.org/pages/api.jsp#stream) +- [`unstar`](http://www.subsonic.org/pages/api.jsp#unstar) +- [`updatePlaylist`](http://www.subsonic.org/pages/api.jsp#updatePlaylist) + +### Additional properties + +Funkwhale returns some additional properties to Subsonic payloads. You can use these properties to adapt your client behavior if needed: + +```{list-table} + * - Property + - Data type + - Description + * - `type` + - String + - The name of the app (`funkwhale`) + * - `funkwhaleVersion` + - String + - The Funkwhale version the pod is running +``` + +```{code-block} json +{ + "subsonic-response": { + "type": "funkwhale", + "funkwhaleVersion": "1.3.0" + } +} +``` + +## Test a Subsonic app + +We host a demo server at which you can use to test your Subsonic app. + +You can test the Subsonic API by logging in with a Subsonic client or by directly by calling an endpoint. For example, call this URL to test the `ping` endpoint: diff --git a/docs/developers/subsonic.rst b/docs/developers/subsonic.rst deleted file mode 100644 index 89ab52289..000000000 --- a/docs/developers/subsonic.rst +++ /dev/null @@ -1,69 +0,0 @@ -Subsonic API -============ - -Funkwhale implements a subset of the `Subsonic API `_ that makes it compatible -with various apps in the Subsonic ecosystem (See `our list of supported apps `_). - -Supported endpoints -------------------- - -We seek the best compatibility with existing apps and will eventually implement -all endpoints that match Funkwhale's feature set. However, the current implementation -do not include folder-based endpoints, as it does not match our internal model at all -and will require substantial effort to emulate. - -We'll try to keep this list up-to-date, but you can also browse `the relevant code -`_ -if needed. - -As of today, the following endpoints are implemented: - -- createPlaylist -- deletePlaylist -- getAlbum -- getAlbumList2 -- getArtist -- getArtistInfo2 -- getArtists -- getAvatar -- getCoverArt -- getIndexes -- getLicense -- getMusicFolders -- getPlaylist -- getPlaylists -- getRandomSongs -- getSong -- getStarred -- getStarred2 -- getUser -- ping -- scrobble -- search3 -- star -- stream -- unstar -- updatePlaylist - -We support both XML and JSON formats for all those endpoints. - -Additional properties ---------------------- - -Regardless of the endpoints, we always return those additional properties -in our payload, which you can use to adapt your client behaviour if needed: - -.. code-block:: json - - { - "subsonic-response": { - "type": "funkwhale", - "funkwhaleVersion": "0.17" - } - } - -Testing a Subsonic app ----------------------- - -We maintain a demo server at https://demo.funkwhale.audio/, which you can use for -your tests. Example with the ping endpoint: https://demo.funkwhale.audio/rest/ping.view?f=json diff --git a/docs/index.md b/docs/index.md index cd0cc0259..29edc47be 100644 --- a/docs/index.md +++ b/docs/index.md @@ -74,6 +74,7 @@ developer_documentation/index developer_documentation/setup/index developer_documentation/contribute/index developer_documentation/workflows/index +developer_documentation/api/index ``` diff --git a/docs/swagger.yml b/docs/swagger.yml index e632460b1..a732c8be4 100644 --- a/docs/swagger.yml +++ b/docs/swagger.yml @@ -1,117 +1,57 @@ openapi: "3.0.3" info: description: | - Interactive documentation for [Funkwhale](https://funkwhale.audio) API. + API explorer for the [Funkwhale](https://funkwhale.audio) API. Backward compatibility between minor versions (1.X to 1.Y) is guaranteed for all the endpoints documented here. - Usage - ----- + ## Usage Click on an endpoint name to inspect its properties, parameters and responses. Use the "Try it out" button to send a real world payload to the endpoint and inspect the corresponding response. - OAuth Authentication - -------------------- + ## OAuth Authentication - You can register your own OAuth app using the `/api/v1/oauth/apps/` endpoint. Proceed to the standard OAuth flow afterwards: + Funkwhale uses the OAuth [authorization grant flow](https://tools.ietf.org/html/rfc6749#section-4.1) for external apps. This flow is + a secure way to authenticate apps that requires a user's explicit consent to perform actions. You can use our demo server at + for testing purposes. - - Our authorize URL is at `/authorize` - - Our token acquisition and refresh URL is at `/api/v1/oauth/token` - - The list of supported scopes is available by clicking the `Authorize` button in the Swagger UI documentation - - Use `urn:ietf:wg:oauth:2.0:oob` as your redirect URI if you want the user to get a copy-pastable authorization code - - At the moment, endpoints that deal with admin or moderator-level content are not accessible via OAuth, only through the Web UI + To authenticate with the Funkwhale API: - You can use our demo server at `https://demo.funkwhale.audio` for testing purposes. + 1. Create an application by sending a `POST` request to `api/v1/oauth/apps`. Include your scopes and redirect URI (use `urn:ietf:wg:oauth:2.0:oob` + to get an authorization code you can copy) + 2. Send an [authorization request](https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2) to the `/authorize` endpoint to receive an authorization code + 3. [Request an access token](https://www.rfc-editor.org/rfc/rfc6749#section-4.1.3) from `/api/v1/oauth/token` + 4. Use your access token to authenticate your calls with the following format: `Authorization: Bearer ` + 5. Refresh your access token by sending a refresh request to `/api/v1/oauth/token` - Application token authentication - -------------------------------- + For more detailed instructions, see [our API authentication documentation](https://docs.funkwhale.audio/developer_documentation/api/authentication.html). - If using OAuth isn't practical and you have an account on the Funkwhale pod, you can create an application by visiting `/settings`. + ## Application token authentication - Once the application is created, you can authenticate using its access token in the `Authorization` header, like this: `Authorization: Bearer `. + If you have an account on your target pod, you can create an application at `/settings/applications/new`. Once you authorize the application you + can retrieve an access token. Use your access token to authenticate your calls with the following format: `Authorization: Bearer ` - Rate limiting - ------------- + ## Rate limiting - Depending on server configuration, pods running Funkwhale 0.20 and higher may rate-limit incoming - requests to prevent abuse and improve the stability of service. Requests that are dropped because of rate-limiting - receive a 429 HTTP response. + Funkwhale supports rate-limiting as of version 0.2.0. Pod admins can choose to rate limit specific endpoints to prevent abuse and improve the stability of the service. + If the server drops a request due to rate-limiting, it returns a `429` status code. - The limits themselves vary depending on: + Each API call returns HTTP headers to pass the following information: - - The client: anonymous requests are subject to lower limits than authenticated requests - - The operation being performed: Write and delete operations, as performed with DELETE, POST, PUT and PATCH HTTP methods are subject to lower limits + - What was the scope of the request (`X-RateLimit-Scope`) + - What is the rate-limit associated with the request scope (`X-RateLimit-Limit`) + - How many more requests in the scope can be made within the rate-limit timeframe (`X-RateLimit-Remaining`) + - How much time does the client need to wait to send another request (`Retry-After`) - Those conditions are used to determine the scope of the request, which in turns determine the limit that is applied. - For instance, authenticated POST requests are bound to the `authenticated-create` scope, with a default limit of - 1000 requests/hour, but anonymous POST requests are bound to the `anonymous-create` scope, with a lower limit of 1000 requests/day. + For more information, check our [rate limit documentation](https://docs.funkwhale.audio/developer_documentation/api/rate-limit.html) - A full list of scopes with their corresponding description, and the current usage data for the client performing the request - is available via the `/api/v1/rate-limit` endpoint. + ## Resources - Additionally, we include HTTP headers on all API response to ensure API clients can understand: - - - what scope was bound to a given request - - what is the corresponding limit - - how much similar requests can be sent before being limited - - and how much time they should wait if they have been limited - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Rate limiting headers
HeaderExample valueDescription value
X-RateLimit-Limit50The number of allowed requests whithin a given period
X-RateLimit-Duration3600The time window, in seconds, during which those requests are accounted for.
X-RateLimit-ScopeloginThe name of the scope as computed for the request
X-RateLimit-Remaining42How many requests can be sent with the same scope before the limit applies
Retry-After (if X-RateLimit-Remaining is 0)3543How many seconds to wait before a retry
X-RateLimit-Reset1568126089A timestamp indicating when X-RateLimit-Remaining will return to its higher possible value
X-RateLimit-ResetSeconds3599How many seconds to wait before X-RateLimit-Remaining returns to its higher possible value
- - - Resources - --------- - - For more targeted guides regarding API usage, and especially authentication, please - refer to [https://docs.funkwhale.audio/api.html](https://docs.funkwhale.audio/api.html) + For more information about API usage, refer to [our API documentation](https://docs.funkwhale.audio/developer_documentation/api/index.html). version: "1.0.0" title: "Funkwhale API"