Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSC2918: Refresh tokens #2918

Merged
merged 22 commits into from
Sep 28, 2021
Merged
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ab50b62
Refresh tokens MSC
sandhose Dec 18, 2020
f8dad2a
MSC2918: minor changes
sandhose Jan 14, 2021
0e615f7
MSC2918: access token expiration as milliseconds
sandhose May 20, 2021
870cded
MSC2918: account registration API changes
sandhose May 20, 2021
6530ecc
MSC2918: fix `expires_in_ms` example
sandhose May 20, 2021
b320001
MSC2918: add precision about token revocation
sandhose Jun 3, 2021
d433e3b
MSC2918: specify error codes for the refresh API
sandhose Jun 3, 2021
87566c3
MSC2918: clarify that the change also applies to ASes
sandhose Jun 3, 2021
269fcac
Apply suggestions from code review
sandhose Jul 1, 2021
4d73b7e
MSC2918: clarify what problem this MSC solves
sandhose Jul 15, 2021
db8ceab
MSC2918: minor formatting and rephrasing
sandhose Jul 15, 2021
9bbb4c5
MSC2918: clarify ratelimiting, masquerading and authentication on ref…
sandhose Jul 15, 2021
a050dc3
MSC2918: make expires_in_ms/refresh_token optional
sandhose Jul 15, 2021
2c11e6f
MSC2918: soft logout in refresh token API
sandhose Jul 15, 2021
4cd94e3
MSC2918: add detailed rationale
sandhose Aug 12, 2021
04ae1c3
MSC2918: minor fix
sandhose Aug 12, 2021
488e9e1
MSC2918: clarifications on backward compatibility
sandhose Sep 9, 2021
4cf821c
MSC2918: advertise support in the request body
sandhose Sep 10, 2021
c076763
MSC2918: clarify on what happen when token expire
sandhose Sep 10, 2021
a157cc3
MSC2918: remove redundant precision about token expiration and lifetime
sandhose Sep 23, 2021
ed54213
MSC2918: minor clarification
sandhose Sep 23, 2021
70b2dfc
MSC2918: soft logout when using expired token
sandhose Sep 23, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions proposals/2918-refreshtokens.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# MSC2918: Refresh tokens
richvdh marked this conversation as resolved.
Show resolved Hide resolved
turt2live marked this conversation as resolved.
Show resolved Hide resolved

Requests are currently authenticated using non-expiring, revocable access tokens.
sandhose marked this conversation as resolved.
Show resolved Hide resolved
This goes against security best practices known in the OAuth2 world.
This MSC make the access tokens expiring and introduces refresh tokens to renew them to fight against token replay attacks.
sandhose marked this conversation as resolved.
Show resolved Hide resolved

## Proposal

The access token returned by the login endpoint expires after a short amount of time, forcing the client to renew it with a refresh token.
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
A refresh token is issued on login and rotates on each usage.

Homeservers can choose to make the access tokens signed and non-revocable for performance reasons if the expiration is short enough (less than 5 minutes).
turt2live marked this conversation as resolved.
Show resolved Hide resolved

### Login API changes
richvdh marked this conversation as resolved.
Show resolved Hide resolved

The login API returns two additional fields:
clokep marked this conversation as resolved.
Show resolved Hide resolved

- `expires_in`: The lifetime in seconds of the access token.
richvdh marked this conversation as resolved.
Show resolved Hide resolved
- `refresh_token`: The refresh token, which can be used to obtain new access tokens.

### Token refresh API

This API lets the client refresh the access token.
A new refresh token is also issued, and the existing one is revoked.
The Matrix server doesn't have to make the old access token invalid, since its lifetime is short enough.

`POST /refresh`
clokep marked this conversation as resolved.
Show resolved Hide resolved

```json
{
"refresh_token": "aaaabbbbccccdddd"
}
```

response:

```json
{
"access_token": "xxxxyyyyzzz",
"expires_in": 60,
"refresh_token": "eeeeffffgggghhhh"
}
```

### Device handling

The current spec states that "Matrix servers should record which device each access token is assigned to".
This must be updated to reflect that devices are bound to a session, which are created during login and stays the same one after refreshing the token.
richvdh marked this conversation as resolved.
Show resolved Hide resolved

uhoreg marked this conversation as resolved.
Show resolved Hide resolved
## Potential issues

The refresh token being rotated on each refresh is strongly recommended in the OAuth2 world for unauthenticated clients to avoid token replay attacks.
This can however make the deployment of CLI tools for Matrix a bit harder, since the credentials can't be statically defined anymore.
This is not an issue in OAuth2 because usually CLI tools use the client credentials flow, also known as service accounts.
An alternative would be to make the refresh token non-rotating for now but recommend clients to support rotation of refresh tokens and enforce it later on.

## Alternatives

This MSC defines a new endpoint for token refresh, but it could also be integrated as a new authentication mechanism.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to expand on this, and the "potential issues" section above, what are the concerns with introducing it as some form of opt-in (or opt-out) mechanism for things like long-lived bots or scripts which do not easily have a refresh opportunity? For example, a nightly batch job to prune rooms/events/etc could use a static access token instead of having to login, do the work, then log out again, which would put the password near the script rather than a single revocable token.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for both use cases (bots and scripts) I'd rather make use of the org.matrix.login.jwt login type with some KMS signing it for the initial login and still have the token refresh. Storing long-lived access tokens without proper secret handling is at least as bad as storing the login/pass of the bot IMO, especially if the user has admin access.
If we still need some kind of static access token, I'd rather have that in Synapse (in the config or something) than in the spec

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair, that sounds reasonable. Just wanted to expand on the potential usecase, but agreed that scripts can find other ways to authenticate (or better yet: be replaced by features within the protocol/homeserver implementation)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think an authentication option for scripts needs to be in the spec. I have a lot of scripts that push notifications or upload files from CI jobs for example. Those use access tokens, because CI jobs do sometimes get compromised (happened once because of codecov) and that way the access token can be easily rotated without being a homeserver admin. If the script used username and password instead, an attacker would have been able to get past UIA and change the password and just in general do much more nasty stuff than with an access token. The jobs also can't refresh the access token, since they may be running concurrently and can't change CI variables.

What would be my alternative for that use case, that works independent of the specific homeserver implementation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an ongoing effort to rework the whole authentication process, with use cases like scripts running in CI in mind. This MSC is also done to prepare clients for the eventual migration to this new authentication stack without having them to logout all their existing sessions.

The login API with non-expiring token will hopefully stay until this new auth stack is ready, so when you would need to migrate you will have a proper alternative.

In the meantime, if you want to still adopt refresh tokens and you are admin of your homeserver, I suggest you look into the org.matrix.login.jwt login type in Synapse. Even though it is not standard, it will let you login using a JWT signed by some party.
It might still need some changes in Synapse to allow restricting the tokens (like not being able to use them for UIA to avoid letting the account to be nuked), but I prefer to go that route rather than adding this kind of special cases in this MSC if it will be superseded by something else soon-ish.


## Security considerations

TBD

## Unstable prefix

TBD