Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Summary

Using OAuth 2 to authenticate with Synapse has many benefits over using older authentication mechanisms. We can try to consolidate authentication services to using OAuth flows wherever possible. The next step is to permit the creation of OAuth 2 public clients, which would allow OAuth 2 based authentication in Synapse command line apps and browser-based SPAs like the GWT web client.

To securely support OAuth 2 public clients, these actions are needed (backfill this list with Jira tickets when confirmed):

  • Support PKCE in the authorization code flow

  • Add field to label OAuth 2 clients as either “public” or “confidential”; backfill existing clients as “confidential”

    • Public clients should not be issued secrets

    • Public clients should be required to use PKCE

  • Revoke the current iteration of a refresh token when an old refresh token is used

Background

There are currently many ways to authenticate a request to Synapse, including

  • Session tokens. Session tokens grant access to all account functions. Session tokens expire after 24 hours, and they can also be refreshed (for an additional 24 hours) and revoked. To acquire a session token, users must either

    • Enter their username and password

    • Sign in using Google as an OIDC identity provider.

  • API keys. API keys grant access to all account functions. API keys do not expire, but they can be revoked, and a new key can be generated. Users may only have one API key active at a time. A user may retrieve their API key at any time (and as an implication, they are stored unhashed in Synapse).

  • Using an access token. Access tokens last 24 hours. Access tokens are also scoped, so that a token may only be used to perform specific types of actions. Only verified OAuth 2 clients may be issued access tokens. At this time, access tokens cannot be revoked unless they are issued with a refresh token.

    • There are two ways that a client can obtain an access token, both requiring client credentials.

      • Sending Synapse a valid authorization code, which is granted when a user authorizes the OAuth client. Authorization codes expire after one minute.

      • Sending Synapse a valid refresh token. The refresh token is granted when the user authorizes an app with the offline_access scope. Refresh tokens expire 180 days after being issued and are single-use, though a new refresh token is issued when one is used, so access should be considered non-expiring. Refresh tokens can be revoked, and access tokens associated with/granted by the refresh token will also be revoked. Additionally, we only store the hash of the refresh token, so the token can only be retrieved at the time it is generated.

Table Summary

Session Token

API Key

OAuth 2.0 access/refresh token

Access Granted

Entire account

Entire account

Scoped, defined by the client

Expires

24 hrs after being issued

Forever

Access: 24 hrs after issued

Refresh: 180 days after issued

Revocable

Yes

Yes

Only if associated with refresh token

Maximum that can be issued

?

1

1

Access: Unlimited

Refresh: 100 per user per OAuth client

Stored as/Re-retrievable

?

Unhashed/

?

Yes

Unhashed

token

/Yes

Access: Not stored/No

Refresh: Hashed token/No

Motivation

We can reduce the complexity and attack surface of authentication with Synapse by utilizing OAuth 2 login flows in places where other mechanisms are used.

...

Best practices suggest that authorization servers are required to support PKCE [2][43]. PKCE only provides benefits to public OAuth clients, since a malicious app must have knowledge of an OAuth client’s credentials to execute the attack.

...

All existing clients are considered to be “confidential” clients, so we can easily backfill that information.

We can also then use the public/privateconfidential label to handle logic accordingly [2]:

Authorization servers MUST record the client type in the client registration details in order to identify and process requests accordingly.

By doing thisAs an example, we can require that public clients utilize PKCE, among other considerations.

Open question: Can we make a breaking API change to require client creators to specify their client type? OAuth clients aren’t created programmatically (at least, it wouldn’t make sense if they are, since they must be manually verified to be functional). In OAuth 2.0, the authorization server SHOULD NOT assume client type [1].

Public clients may or may not be issued

...

a secret

Client credentials (other than the ID) are not useful for public clients [1]:

The authorization server MAY establish a client authentication method with public clients. However, the authorization server MUST NOT rely on public client authentication for the purpose of identifying the client.

OAuth 2.1 ([2]) suggests that they shouldn’t be required, so why issue them?:

Secrets that are statically included as part of an app distributed to multiple users should not be treated as confidential secrets, as one user may inspect their copy and learn the shared secret. For this reason, it is NOT RECOMMENDED for authorization servers to require client authentication of public native apps clients using a shared secret, as this serves little value beyond client identification which is already provided by the "client_id" request parameter.

The only legitimate case to issue secrets is if we have services for managing the OAuth client endpoints that require OAuth client credentials . However, in most cases, these services should be used by the user that owns the client, rather than the client itselfinvolve issuing or revoking tokens, so permitting public clients to have no secret should not cause any issues with, for example, public client metadata being editable by unauthorized parties.

Public clients may be required to use certain grant types

...

Notes on public clients using refresh tokens

OAuth 2.1 ([2]) Note on refresh tokens for public clients:

Authorization server MUST utilize one of these methods to detect refresh token replay by malicious actors for public clients:

* _Sender-constrained refresh tokens:_ the authorization server cryptographically binds the refresh token to a certain client instance by utilizing [I-D.ietf-oauth-token-binding] or [RFC8705]

* _Refresh token rotation:_ the authorization server issues a new refresh token with every access token refresh response. The previous refresh token is invalidated but information about the relationship is retained by the authorization server. If a refresh token is compromised and subsequently used by both the attacker and the legitimate client, one of them will present an invalidated refresh token, which will inform the authorization server of the breach. The authorization server cannot determine which party submitted the invalid refresh token, but it will revoke the active refresh token. This stops the attack at the cost of forcing the legitimate client to obtain a fresh authorization grant.

  • TODO: Look into implementation and costs/benefits of binding tokens to clientsToken binding introduces a public/private keypair and encrypts additional data to bind tokens to a TLS connection, thus a token could not be used if intercepted via MitM/token export. While powerful, implementing token binding can be complicated and has seen low adoption. We may choose to further investigate token binding as in [4] later. Token binding as described in [OAuth 2.0 Token Binding] does not seem to be an acceptable solution, because the draft has expired, and the mechanism to implement token binding on the client side does not seem to be supported by any major browsers (abandoned by Chrome, and only implemented in Edge before it became Chromium-based).

  • Refresh token rotation is implemented, but we currently do not revoke an active token if an invalid token is used (so an attacker could hijack the session). Should we implement this? If so, should this be the behavior for confidential clients as well?

...

  • . We should extend our implementation to include this behavior.

...

References

[1] OAuth 2.0 (RFC 6749)

[2] OAuth 2.1 (IETF Draft, last updated 2020 April 24)

[3] OAuth 2.0 for Browser-Based Apps Security Best Current Practice (IETF Draft, last updated 2020 April 05)

[4] OAuth 2.0 Security Best Current Practice (IETF Draft, last updated 2020 April 05)[5] Mutual-TLS Client Authentication and Certificate-Bound Access Tokens (RFC 8705)

Other Resources

OpenID Connect Core 1.0

[6] OAuth 2.0 for Native Apps (RFC 8252)

OAuth 2.0 for Browser-Based Apps (IETF Draft, last updated 2020 April 05)