Only allow C-S device scopes when the C-S API scope has been requested

It'd be weird for a client to request a device on the client-server API but yet not request any client-server API scopes to use it with.

By adding this restriction, we can then create a partial index on the oauth2_sessions table to quickly identify sessions that have C-S API scopes and use this as a proxy metric for how many sessions may have device scopes.
This in turn makes it feasible to efficiently limit the number of 'devices' a user has, or more precisely: the number of sessions with client-server API access.

We can't do the same for device scopes themselves because, other than nastiness like parsing the JSON stringification of the scope list, it's not feasible to identify device scopes within a Postgres index predicate.

Part of: #4339
This commit is contained in:
Olivier 'reivilibre
2025-10-31 15:13:07 +00:00
parent f45d9c1291
commit aeabc9cbf2
2 changed files with 41 additions and 1 deletions

View File

@@ -98,6 +98,26 @@ uses_stable_scopes if {
count({scope | some scope in scope_list; startswith(scope, "urn:matrix:client:")}) > 0
}
has_device_scope if {
scope_list := split(input.scope, " ")
count({scope | some scope in scope_list; startswith(scope, "urn:matrix:client:device:")}) > 0
}
has_device_scope if {
scope_list := split(input.scope, " ")
count({scope | some scope in scope_list; startswith(scope, "urn:matrix:org.matrix.msc2967.client:device:")}) > 0
}
has_cs_api_scope if {
scope_list := split(input.scope, " ")
count({scope | some scope in scope_list; startswith(scope, "urn:matrix:client:api:")}) > 0
}
has_cs_api_scope if {
scope_list := split(input.scope, " ")
count({scope | some scope in scope_list; startswith(scope, "urn:matrix:org.matrix.msc2967.client:api:")}) > 0
}
# METADATA
# entrypoint: true
violation contains {"msg": msg} if {
@@ -116,6 +136,12 @@ violation contains {"msg": "only one device scope is allowed at a time"} if {
count({scope | some scope in scope_list; startswith(scope, "urn:matrix:client:device:")}) > 1
}
# Prevent the creation of C-S API devices for sessions that don't have C-S API access.
violation contains {"msg": "device scopes are only allowed when the client-server API scope is requested"} if {
has_device_scope
not has_cs_api_scope
}
violation contains {"msg": "request cannot mix unstable and stable scopes"} if {
uses_stable_scopes
uses_unstable_scopes

View File

@@ -141,7 +141,21 @@ test_stable_device_scopes if {
# Not authorization_grant.allowed for the client credentials grant
not authorization_grant.allow with input.client as client
with input.grant_type as "client_credentials"
with input.scope as "urn:matrix:client:api:* urn:matrix:client:device:AAbbCCdd01"
}
test_device_scope_only_with_cs_api_scope if {
not authorization_grant.allow with input.user as user
with input.client as client
with input.grant_type as "authorization_code"
# Requested a device scope but no C-S API scope:
with input.scope as "urn:matrix:client:device:AAbbCCdd01"
not authorization_grant.allow with input.user as user
with input.client as client
with input.grant_type as "authorization_code"
# Requested a device scope but no C-S API scope:
with input.scope as "urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd01"
}
test_mix_stable_and_unstable_scopes if {