Skip to main content

tuwunel_service/oauth/
server.rs

1mod auth;
2mod client;
3mod jwk;
4mod signing_key;
5mod token;
6
7use std::sync::Arc;
8
9use serde_json::Value as JsonValue;
10use tuwunel_core::{Result, debug_info, debug_warn, err, implement, warn};
11use tuwunel_database::Map;
12
13pub use self::{
14	auth::{AUTH_REQUEST_LIFETIME, AuthCodeSession, AuthRequest},
15	client::{ClientRegistration, DcrRequest},
16	token::IdTokenClaims,
17};
18use self::{
19	jwk::init_jwk,
20	signing_key::{SigningKey, init_signing_key},
21};
22use crate::services::OnceServices;
23
24pub struct Server {
25	services: Arc<OnceServices>,
26	db: Data,
27	jwk: JsonValue,
28	key: SigningKey,
29}
30
31struct Data {
32	oidc_signingkey: Arc<Map>,
33	oidcclientid_registration: Arc<Map>,
34	oidccode_authsession: Arc<Map>,
35	oidcreqid_authrequest: Arc<Map>,
36}
37
38impl Server {
39	pub(super) fn build(args: &crate::Args<'_>) -> Result<Option<Self>> {
40		if !Self::can_build(args) {
41			return Ok(None);
42		}
43
44		let db = Data {
45			oidc_signingkey: args.db["oidc_signingkey"].clone(),
46			oidcclientid_registration: args.db["oidcclientid_registration"].clone(),
47			oidccode_authsession: args.db["oidccode_authsession"].clone(),
48			oidcreqid_authrequest: args.db["oidcreqid_authrequest"].clone(),
49		};
50
51		let key = init_signing_key(&db)?;
52		debug_info!(
53			key = ?key.key_id,
54			"Initializing OIDC server for next-gen auth (MSC2965)"
55		);
56
57		Ok(Some(Self {
58			services: args.services.clone(),
59			db,
60			jwk: init_jwk(&key.key_der, &key.key_id)?,
61			key,
62		}))
63	}
64}
65
66#[implement(Server)]
67fn can_build(args: &crate::Args<'_>) -> bool {
68	let has_idp = !args.server.config.identity_provider.is_empty();
69	let has_cwk = args.server.config.well_known.client.is_some();
70
71	if has_idp && !has_cwk {
72		warn!(
73			"OIDC server (Next-gen auth) requires `well_known.client` to be configured to serve \
74			 your `identity_provider`."
75		);
76	}
77
78	if !has_idp || !has_cwk {
79		debug_warn!(
80			"OIDC server (Next-gen auth) requires at least one `identity_provider` to be \
81			 configured."
82		);
83
84		return false;
85	}
86
87	true
88}
89
90#[implement(Server)]
91pub fn issuer_url(&self) -> Result<String> {
92	self.services
93		.config
94		.well_known
95		.client
96		.as_ref()
97		.map(|url| {
98			let s = url.to_string();
99			if s.ends_with('/') { s } else { s + "/" }
100		})
101		.ok_or_else(|| {
102			err!(Config("well_known.client", "well_known.client must be set for OIDC server"))
103		})
104}
105
106#[inline]
107pub fn extract_device_id(scope: &str) -> Option<String> {
108	scope
109		.split_whitespace()
110		.find_map(|s| s.strip_prefix("urn:matrix:org.matrix.msc2967.client:device:"))
111		.map(ToOwned::to_owned)
112}