diff --git a/content/client-server-api/_index.md b/content/client-server-api/_index.md index 3edb5b16..52398c9c 100644 --- a/content/client-server-api/_index.md +++ b/content/client-server-api/_index.md @@ -71,7 +71,7 @@ These error codes can be returned by any API endpoint: Forbidden access, e.g. joining a room without permission, failed login. `M_UNKNOWN_TOKEN` -The access token specified was not recognised. +The access or refresh token specified was not recognised. An additional response parameter, `soft_logout`, might be present on the response for 401 HTTP status codes. See [the soft logout @@ -314,7 +314,8 @@ Most API endpoints require the user to identify themselves by presenting previously obtained credentials in the form of an `access_token` query parameter or through an Authorization Header of `Bearer $access_token`. An access token is typically obtained via the [Login](#login) or -[Registration](#account-registration-and-management) processes. +[Registration](#account-registration-and-management) processes. Access tokens +can expire; a new access token can be generated by using a refresh token. {{% boxes/note %}} This specification does not mandate a particular format for the access @@ -336,42 +337,92 @@ to prevent the access token being leaked in access/HTTP logs. The query string should only be used in cases where the `Authorization` header is inaccessible for the client. -When credentials are required but missing or invalid, the HTTP call will -return with a status of 401 and the error code, `M_MISSING_TOKEN` or -`M_UNKNOWN_TOKEN` respectively. +{{% changed-in v="1.3" %}} When credentials are required but missing or +invalid, the HTTP call will return with a status of 401 and the error code, +`M_MISSING_TOKEN` or `M_UNKNOWN_TOKEN` respectively. Note that an error code +of `M_UNKNOWN_TOKEN` could mean one of four things: + +1. the access token was never valid. +2. the access token has been logged out. +3. the access token has been [soft logged out](#soft-logout). +4. the access token [needs to be refreshed](#refreshing-access-tokens). + +When a client receives an error code of `M_UNKNOWN_TOKEN`, it should: + +- attempt to [refresh the token](#refreshing-access-tokens), if it has a refresh + token; +- if [`soft_logout`](#soft-logout) is set to `true`, it can offer to + re-log in the user, retaining any of the client's persisted + information; +- otherwise, consider the user as having been logged out. ### Relationship between access tokens and devices Client [devices](../index.html#devices) are closely related to access -tokens. Matrix servers should record which device each access token is -assigned to, so that subsequent requests can be handled correctly. +tokens and refresh tokens. Matrix servers should record which device +each access token and refresh token are assigned to, so that +subsequent requests can be handled correctly. When a refresh token is +used to generate a new access token and refresh token, the new access +and refresh tokens are now bound to the device associated with the +initial refresh token. By default, the [Login](#login) and [Registration](#account-registration-and-management) processes auto-generate a new `device_id`. A client is also free to generate its own `device_id` or, provided the user remains the same, reuse a device: in either case the client should pass the `device_id` in the request body. If the client sets the `device_id`, the server will -invalidate any access token previously assigned to that device. There is -therefore at most one active access token assigned to each device at any -one time. +invalidate any access and refresh tokens previously assigned to that device. + +### Refreshing access tokens + +{{% added-in v="1.3" %}} + +Access tokens can expire after a certain amount of time. Any HTTP +calls that use an expired access token will return with an error code +`M_UNKNOWN_TOKEN`, preferably with `soft_logout: true`. When a client +receives this error and it has a refresh token, it should attempt to +refresh the access token by calling `/refresh`. Clients can also +refresh their access token at any time, even if it has not yet +expired. If the token refresh succeeds, it should use the new token +for future requests, and can re-try previously-failed requests with +the new token. When an access token is refreshed, a new refresh token +may be returned; if a new refresh token is given, the old refresh +token will be invalidated, and the new refresh token should be used +when the access token needs to be refreshed. + +If the token refresh fails, then the client can treat it as a [soft +logout](#soft-logout), if the error response included a `soft_logout: +true` property, and attempt to obtain a new access token by re-logging +in. If the error response does not include a `soft_logout: true` +property, the client should consider the user as being logged out. + +Handling of clients that do not support refresh tokens is up to the +homeserver; clients indicate their support for refresh tokens by +including a `refresh_token: true` property in the request body of the +`/login` and `/register` endpoints. For example, homeservers may allow +the use of non-expiring access tokens, or may expire access tokens +anyways and rely on soft logout behaviour on clients that don't +support refreshing. ### Soft logout -When a request fails due to a 401 status code per above, the server can -include an extra response parameter, `soft_logout`, to indicate if the -client's persisted information can be retained. This defaults to -`false`, indicating that the server has destroyed the session. Any -persisted state held by the client, such as encryption keys and device -information, must not be reused and must be discarded. +A client can be in a "soft logout" state if the server requires +re-authentication before continuing, but does not want to invalidate +the client's session. That is, any persisted state held by the client, +such as encryption keys and device information, must not be reused and +must be discarded, and can be re-used if the client successfully +re-authenticates. -When `soft_logout` is true, the client can acquire a new access token by -specifying the device ID it is already using to the login API. In most -cases a `soft_logout: true` response indicates that the user's session -has expired on the server-side and the user simply needs to provide -their credentials again. +The server indicates that the client is in a soft logout state by +including a `soft_logout: true` parameter in an `M_UNKNOWN_TOKEN` +error response; the `soft_logout` parameter defaults to `false`. -In either case, the client's previously known access token will no -longer function. +A client that receives such a response can try to [refresh its access +token](#refreshing-access-tokens), if it has a refresh token +available. If it does not have a refresh token available, or +refreshing fails with `soft_logout: true`, the client can acquire a +new access token by specifying the device ID it is already using to +the login API. ### User-Interactive Authentication API @@ -1105,6 +1156,8 @@ errcode of `M_EXCLUSIVE`. {{% http-api spec="client-server" api="login" %}} +{{% http-api spec="client-server" api="refresh" %}} + {{% http-api spec="client-server" api="logout" %}} #### Login Fallback diff --git a/data/api/client-server/login.yaml b/data/api/client-server/login.yaml index 8865665f..3eb1e0d2 100644 --- a/data/api/client-server/login.yaml +++ b/data/api/client-server/login.yaml @@ -1,5 +1,6 @@ # Copyright 2016 OpenMarket Ltd # Copyright 2018 New Vector Ltd +# Copyright 2022 The Matrix.org Foundation C.I.C. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -133,6 +134,11 @@ paths: description: |- A display name to assign to the newly-created device. Ignored if `device_id` corresponds to a known device. + refresh_token: + type: boolean + description: |- + If true, the client supports refresh tokens. + x-addedInMatrixVersion: "1.3" required: ["type"] responses: @@ -142,6 +148,8 @@ paths: application/json: { "user_id": "@cheeky_monkey:matrix.org", "access_token": "abc123", + "refresh_token": "def456", + "expires_in_ms": 60000, "device_id": "GHTYAJCE", "well_known": { "m.homeserver": { @@ -163,6 +171,23 @@ paths: description: |- An access token for the account. This access token can then be used to authorize other requests. + refresh_token: + type: string + description: |- + A refresh token for the account. This token can be used to + obtain a new access token when it expires by calling the + `/refresh` endpoint. + x-addedInMatrixVersion: "1.3" + expires_in_ms: + type: integer + description: |- + The lifetime of the access token, in milliseconds. Once + the access token has expired a new access token can be + obtained by using the provided refresh token. If no + refresh token is provided, the client will need re-log in + to obtain a new access token. If not given, the client can + assume that the access token will not expire. + x-addedInMatrixVersion: "1.3" home_server: type: string description: |- diff --git a/data/api/client-server/refresh.yaml b/data/api/client-server/refresh.yaml new file mode 100644 index 00000000..abe8c8fb --- /dev/null +++ b/data/api/client-server/refresh.yaml @@ -0,0 +1,107 @@ +# Copyright 2022 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Client-Server Registration and Login API" + version: "1.0.0" +host: localhost:8008 +schemes: + - https + - http +basePath: /_matrix/client/v3 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/refresh": + post: + x-addedInMatrixVersion: "1.3" + summary: Refresh an access token + description: |- + Refresh an access token. Clients should use the returned access token + when making subsequent API calls, and store the returned refresh token + (if given) in order to refresh the new access token when necessary. + + After an access token has been refreshed, a server can choose to + invalidate the old access token immediately, or can choose not to, for + example if the access token would expire soon anyways. Clients should + not make any assumptions about the old access token still being valid, + and should use the newly provided access token instead. + + Note that this endpoint does not require authentication, since + authentication is provided via the refresh token. + + Application Service identity assertion is disabled for this endpoint. + operationId: refresh + parameters: + - in: body + name: body + required: true + schema: + type: object + example: { + "refresh_token": "some_token" + } + properties: + refresh_token: + type: string + description: The refresh token + responses: + 200: + description: A new access token and refresh token were generated. + examples: + application/json: { + "access_token": "a_new_token", + "expires_in_ms": 60000, + "refresh_token": "another_new_token" + } + schema: + type: object + properties: + access_token: + type: string + description: |- + The new access token to use. + refresh_token: + type: string + description: |- + The new refresh token to use when the access token needs to + be refreshed again. If not given, the old refresh token can + be re-used. + expires_in_ms: + type: integer + description: |- + The lifetime of the access token, in milliseconds. If not + given, the client can assume that the access token will not + expire. + required: + - access_token + 401: + description: |- + The provided token was unknown, or has already been used. + examples: + application/json: { + "errcode": "M_UNKNOWN_TOKEN", + "error": "Soft logged out", + "soft_logout": true + } + schema: + "$ref": "definitions/errors/error.yaml" + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" diff --git a/data/api/client-server/registration.yaml b/data/api/client-server/registration.yaml index 810d8bc9..7e6a34cd 100644 --- a/data/api/client-server/registration.yaml +++ b/data/api/client-server/registration.yaml @@ -1,4 +1,5 @@ # Copyright 2016 OpenMarket Ltd +# Copyright 2022 The Matrix.org Foundation C.I.C. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -127,6 +128,11 @@ paths: returned from this call, therefore preventing an automatic login. Defaults to false. example: false + refresh_token: + type: boolean + description: |- + If true, the client supports refresh tokens. + x-addedInMatrixVersion: "1.3" responses: 200: description: The account has been registered. @@ -152,6 +158,27 @@ paths: An access token for the account. This access token can then be used to authorize other requests. Required if the `inhibit_login` option is false. + refresh_token: + type: string + description: |- + A refresh token for the account. This token can be used to + obtain a new access token when it expires by calling the + `/refresh` endpoint. + + Omitted if the `inhibit_login` option is false. + x-addedInMatrixVersion: "1.3" + expires_in_ms: + type: integer + description: |- + The lifetime of the access token, in milliseconds. Once + the access token has expired a new access token can be + obtained by using the provided refresh token. If no + refresh token is provided, the client will need re-log in + to obtain a new access token. If not given, the client can + assume that the access token will not expire. + + Omitted if the `inhibit_login` option is false. + x-addedInMatrixVersion: "1.3" home_server: type: string description: |-