feat: Add support for MSC4363

This commit is contained in:
Ginger authored and Ellis Git committed 2026-10-05 14:22:53 +00:00
1 parent af84202ba4
commit e963adf524
12 files changed
+252 -161

No files matched your search

+39 -13
View File
@@ -5,8 +5,9 @@ use axum::{
routing::on,
};
use conduwuit_service::oauth::{
SessionInfo,
client_metadata::ClientMetadata,
grant::{AuthorizationCodeQuery, DeviceCodeVerifyQuery, Prompt, RequestedScope},
grant::{AuthorizationCodeQuery, DeviceCodeVerifyQuery, Prompt, RequestedScopes},
};
use ruma::{OwnedUserId, api::OAuthClientScope};
use serde::{Deserialize, de::IgnoredAny};
@@ -37,6 +38,7 @@ template! {
user_avatar: Avatar,
client_metadata: ClientMetadata,
scopes: ClientScopes,
existing_session_info: Option<SessionInfo>,
device_code: Option<String>
}
}
@@ -102,13 +104,23 @@ async fn route_authorization_code(
return Err(WebError::BadRequest("Invalid client ID".to_owned()));
};
let scopes = query
.scope
.to_scopes()
.map_err(WebError::BadRequest)?
.iter()
.filter_map(RequestedScope::as_client_scope)
.collect();
let RequestedScopes { device_id, mut scopes } =
query.scope.to_scopes().map_err(WebError::BadRequest)?;
let existing_session_info = if let Some(device_id) = &device_id {
services
.oauth
.get_session_info_for_device(&user_id, device_id)
.await
} else {
None
};
if let Some(existing_session_info) = &existing_session_info {
// For step-up auth, don't show scopes that the device already has
let existing_scopes = existing_session_info.scopes();
scopes.retain(|scope| !existing_scopes.contains(scope));
}
let user_avatar = Avatar::for_local_user(&services, &user_id).await;
@@ -124,6 +136,7 @@ async fn route_authorization_code(
user_avatar,
client,
ClientScopes { scopes },
existing_session_info,
None,
))
}
@@ -189,11 +202,23 @@ async fn route_device_code(
));
};
let scopes = grant_info
.requested_scopes
.iter()
.filter_map(RequestedScope::as_client_scope)
.collect();
let RequestedScopes { device_id, mut scopes } = grant_info.requested_scopes;
let existing_session_info = if let Some(device_id) = &device_id {
services
.oauth
.get_session_info_for_device(&user_id, device_id)
.await
} else {
None
};
if let Some(existing_session_info) = &existing_session_info {
// For step-up auth, don't show scopes that the device already
// has
let existing_scopes = existing_session_info.scopes();
scopes.retain(|scope| !existing_scopes.contains(scope));
}
let user_avatar = Avatar::for_local_user(&services, &user_id).await;
@@ -209,6 +234,7 @@ async fn route_device_code(
user_avatar,
grant_info.client_metadata,
ClientScopes { scopes },
existing_session_info,
Some(grant_info.device_code),
))
},
-1
View File
@@ -8,7 +8,6 @@
margin-inline: 1em;
color: var(--secondary);
font-size: x-large;
font-weight: bold;
user-select: none;
}
}
@@ -4,7 +4,7 @@
{% when OAuthClientScope::ApiFullAccess %}
<li>Access and control all features of your Matrix account</li>
{% when OAuthClientScope::ServerAdministration %}
<li><em class="danger">Access your homeserver's administrator tools</em></li>
<li>⚠️ Access your homeserver's administrator tools</li>
{% when _ %}
<li>missingno</li>
{% endmatch %}
+2 -2
View File
@@ -26,7 +26,7 @@ Device information
<p>
{% if let Some((_, session_info)) = client_metadata %}
This device has permission to:
{{ ClientScopes { scopes: session_info.scopes.clone() } }}
{{ ClientScopes { scopes: session_info.scopes() } }}
{% else if device_card.dehydrated %}
This is your dehydrated device. It saves encryption keys for you while you're offline.
{% else %}
@@ -45,7 +45,7 @@ Device information
</p>
<p>
Scopes: <code>
{% for scope in session_info.scopes %}
{% for scope in session_info.scopes() %}
{{ scope | safe }}
{% endfor %}
</code>
+10 -4
View File
@@ -27,10 +27,16 @@ Authorize client
</div>
<p>
<b>{{ client_name }}</b> (<a href="{{ client_metadata.client_uri }}">{{ client_metadata.client_uri.domain().unwrap() }}</a>) would like
your permission to:
{{ scopes }}
<b>{{ client_name }}</b> (<a href="{{ client_metadata.client_uri }}">{{ client_metadata.client_uri.domain().unwrap() }}</a>)
{% if let Some(existing_session_info) = existing_session_info %}
would like temporary additional permissions to:
{{ scopes }}
{{ client_name }} already has permission to:
{{ ClientScopes { scopes: existing_session_info.scopes() } }}
{% else %}
would like your permission to:
{{ scopes }}
{% endif %}
</p>
{% if scopes.scopes.contains(&OAuthClientScope::ServerAdministration) %}