Skip to content

Commit 7b49e20

Browse files
dasha-uwulytedev
authored andcommitted
feat: implement OIDC server for next-gen auth (MSC2965/2964/2966/2967)
Implements a built-in OIDC authorization server that allows Matrix clients like Element X to authenticate via the next-gen auth flow (MSC2964). User authentication is delegated to upstream identity providers (e.g. Kanidm) through the existing SSO/OAuth client flow. Endpoints: - auth_issuer + auth_metadata discovery (stable v1 + unstable MSC2965) - OpenID Connect discovery (/.well-known/openid-configuration) - Dynamic Client Registration (MSC2966) - Authorization + token + revocation + JWKS + userinfo - SSO bridge: authorize → SSO redirect → complete → code → token Features: - ES256 (P-256) JWT signing with persistent key material - PKCE (S256) support - Authorization code grant with refresh tokens - All OIDC state persisted in RocksDB (signing keys, client registrations, auth codes, pending auth requests) - Device ID extraction from MSC2967 scopes - Fixes sso_default_provider_id config handling (was previously unused) Refs: #246, #266
1 parent 0381547 commit 7b49e20

File tree

204 files changed

+3244
-994
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

204 files changed

+3244
-994
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/admin/context.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ impl Context<'_> {
2727
}
2828

2929
#[inline]
30-
pub(crate) async fn write_string(&self, s: String) -> Result { self.write_str(&s).await }
30+
pub(crate) async fn write_string(&self, s: String) -> Result {
31+
self.write_str(&s).await
32+
}
3133

3234
pub(crate) fn write_str<'a>(
3335
&'a self,

src/admin/debug/commands.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,10 @@ pub(super) async fn get_remote_pdu(
243243
match self
244244
.services
245245
.federation
246-
.execute(&server, ruma::api::federation::event::get_event::v1::Request {
247-
event_id: event_id.clone(),
248-
})
246+
.execute(
247+
&server,
248+
ruma::api::federation::event::get_event::v1::Request { event_id: event_id.clone() },
249+
)
249250
.await
250251
{
251252
| Err(e) => {
@@ -579,10 +580,13 @@ pub(super) async fn force_set_room_state_from_server(
579580
let remote_state_response = self
580581
.services
581582
.federation
582-
.execute(&server_name, get_room_state::v1::Request {
583-
room_id: room_id.clone(),
584-
event_id: first_pdu.event_id().to_owned(),
585-
})
583+
.execute(
584+
&server_name,
585+
get_room_state::v1::Request {
586+
room_id: room_id.clone(),
587+
event_id: first_pdu.event_id().to_owned(),
588+
},
589+
)
586590
.await?;
587591

588592
for pdu in remote_state_response.pdus.clone() {

src/admin/processor.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ use tuwunel_service::{
3636
use crate::{admin, admin::AdminCommand, context::Context};
3737

3838
#[must_use]
39-
pub(super) fn complete(line: &str) -> String { complete_command(AdminCommand::command(), line) }
39+
pub(super) fn complete(line: &str) -> String {
40+
complete_command(AdminCommand::command(), line)
41+
}
4042

4143
#[must_use]
4244
pub(super) fn dispatch(services: Arc<Services>, command: CommandInput) -> ProcessorFuture {
@@ -78,8 +80,9 @@ async fn process_command(services: Arc<Services>, input: &CommandInput) -> Proce
7880
String::from_utf8(take(output.get_mut())).expect("invalid utf8 in command output stream");
7981

8082
match result {
81-
| Ok(()) if logs.is_empty() =>
82-
Ok(Some(reply(RoomMessageEventContent::notice_markdown(output), context.reply_id))),
83+
| Ok(()) if logs.is_empty() => {
84+
Ok(Some(reply(RoomMessageEventContent::notice_markdown(output), context.reply_id)))
85+
},
8386

8487
| Ok(()) => {
8588
logs.write_str(output.as_str())
@@ -99,8 +102,7 @@ async fn process_command(services: Arc<Services>, input: &CommandInput) -> Proce
99102
}
100103

101104
fn handle_panic(error: &Error, command: &CommandInput) -> ProcessorResult {
102-
let link =
103-
"Please submit a [bug report](https://github.com/matrix-construct/tuwunel/issues/new). \
105+
let link = "Please submit a [bug report](https://github.com/matrix-construct/tuwunel/issues/new). \
104106
🥺";
105107
let msg = format!("Panic occurred while processing command:\n```\n{error:#?}\n```\n{link}");
106108
let content = RoomMessageEventContent::notice_markdown(msg);

src/admin/query/oauth.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(clippy::semicolon_if_nothing_returned)]
2+
13
use clap::Subcommand;
24
use futures::{StreamExt, TryStreamExt};
35
use ruma::OwnedUserId;
@@ -261,11 +263,12 @@ pub(super) async fn oauth_revoke(&self, id: SessionOrUserId) -> Result {
261263
.await
262264
.ok();
263265
},
264-
| Right(user_id) =>
266+
| Right(user_id) => {
265267
self.services
266268
.oauth
267269
.revoke_user_tokens(&user_id)
268-
.await,
270+
.await
271+
},
269272
}
270273

271274
self.write_str("revoked").await

src/admin/query/raw.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -256,18 +256,20 @@ pub(super) async fn raw_keys(
256256
let timer = Instant::now();
257257

258258
let stream = match from.as_ref().or(prefix.as_ref()) {
259-
| Some(from) =>
259+
| Some(from) => {
260260
if !backwards {
261261
map.raw_keys_from(from).boxed()
262262
} else {
263263
map.rev_raw_keys_from(from).boxed()
264-
},
265-
| None =>
264+
}
265+
},
266+
| None => {
266267
if !backwards {
267268
map.raw_keys().boxed()
268269
} else {
269270
map.rev_raw_keys().boxed()
270-
},
271+
}
272+
},
271273
};
272274

273275
let prefix = prefix.as_ref().map(String::as_bytes);
@@ -393,18 +395,20 @@ pub(super) async fn raw_iter(
393395
let map = self.services.db.get(&map)?;
394396
let timer = Instant::now();
395397
let stream = match from.as_ref().or(prefix.as_ref()) {
396-
| Some(from) =>
398+
| Some(from) => {
397399
if !backwards {
398400
map.raw_stream_from(from).boxed()
399401
} else {
400402
map.rev_raw_stream_from(from).boxed()
401-
},
402-
| None =>
403+
}
404+
},
405+
| None => {
403406
if !backwards {
404407
map.raw_stream().boxed()
405408
} else {
406409
map.rev_raw_stream().boxed()
407-
},
410+
}
411+
},
408412
};
409413

410414
let prefix = prefix.as_ref().map(String::as_bytes);

src/admin/room/alias.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,13 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Context<'_>) ->
7373
.set_alias(&room_alias, &room_id, server_user)
7474
{
7575
| Err(err) => Err!("Failed to remove alias: {err}"),
76-
| Ok(()) =>
76+
| Ok(()) => {
7777
context
7878
.write_str(&format!(
7979
"Successfully overwrote alias (formerly {id})"
8080
))
81-
.await,
81+
.await
82+
},
8283
}
8384
},
8485
| (false, Ok(id)) => Err!(
@@ -109,10 +110,11 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Context<'_>) ->
109110
.await
110111
{
111112
| Err(err) => Err!("Failed to remove alias: {err}"),
112-
| Ok(()) =>
113+
| Ok(()) => {
113114
context
114115
.write_str(&format!("Removed alias from {id}"))
115-
.await,
116+
.await
117+
},
116118
},
117119
}
118120
},
@@ -123,16 +125,17 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Context<'_>) ->
123125
.await
124126
{
125127
| Err(_) => Err!("Alias isn't in use."),
126-
| Ok(id) =>
128+
| Ok(id) => {
127129
context
128130
.write_str(&format!("Alias resolves to {id}"))
129-
.await,
131+
.await
132+
},
130133
}
131134
},
132135
| RoomAliasCommand::List { .. } => unreachable!(),
133136
}
134137
},
135-
| RoomAliasCommand::List { room_id } =>
138+
| RoomAliasCommand::List { room_id } => {
136139
if let Some(room_id) = room_id {
137140
let aliases: Vec<OwnedRoomAliasId> = services
138141
.alias
@@ -170,6 +173,7 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Context<'_>) ->
170173

171174
let plain = format!("Aliases:\n{plain_list}");
172175
context.write_str(&plain).await
173-
},
176+
}
177+
},
174178
}
175179
}

src/admin/tests.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
#![cfg(test)]
22

33
#[test]
4-
fn get_help_short() { get_help_inner("-h"); }
4+
fn get_help_short() {
5+
get_help_inner("-h");
6+
}
57

68
#[test]
7-
fn get_help_long() { get_help_inner("--help"); }
9+
fn get_help_long() {
10+
get_help_inner("--help");
11+
}
812

913
#[test]
10-
fn get_help_subcommand() { get_help_inner("help"); }
14+
fn get_help_subcommand() {
15+
get_help_inner("help");
16+
}
1117

1218
fn get_help_inner(input: &str) {
1319
use clap::Parser;

src/api/client/appservice.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,12 @@ pub(crate) async fn appservice_ping(
3838

3939
let _response = services
4040
.appservice
41-
.send_request(appservice_info.registration.clone(), ping::send_ping::v1::Request {
42-
transaction_id: body.transaction_id.clone(),
43-
})
41+
.send_request(
42+
appservice_info.registration.clone(),
43+
ping::send_ping::v1::Request {
44+
transaction_id: body.transaction_id.clone(),
45+
},
46+
)
4447
.await?
4548
.expect("We already validated if an appservice URL exists above");
4649

src/api/client/directory.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,8 +371,9 @@ async fn user_can_publish_room(
371371
.get_power_levels(room_id)
372372
.await
373373
{
374-
| Ok(power_levels) =>
375-
Ok(power_levels.user_can_send_state(user_id, StateEventType::RoomHistoryVisibility)),
374+
| Ok(power_levels) => {
375+
Ok(power_levels.user_can_send_state(user_id, StateEventType::RoomHistoryVisibility))
376+
},
376377
| _ => {
377378
match services
378379
.state_accessor

0 commit comments

Comments
 (0)