chore: Clippy and formatting

This commit is contained in:
Ginger committed 2025-11-11 11:36:28 -05:00
1 parent 17f8ec21a3
commit fdbb1c86b1
11 files changed
+224 -197

No files matched your search

+2 -1
View File
@@ -76,7 +76,8 @@ pub(crate) async fn upload_keys_route(
if deser_device_keys.device_id != sender_device {
return Err!(Request(Unknown(
"Device ID in keys uploaded ({}) does not match your own device ID ({})",
deser_device_keys.device_id, sender_device,
deser_device_keys.device_id,
sender_device,
)));
}
+34 -32
View File
@@ -1,7 +1,7 @@
use axum::extract::{Query, State};
use conduwuit::{Result, err, utils::ReadyExt};
use conduwuit_web::oidc::{
oidc_consent_form, oidc_login_form, AuthorizationQuery, OidcRequest, OidcResponse,
AuthorizationQuery, OidcRequest, OidcResponse, oidc_consent_form, oidc_login_form,
};
use oxide_auth::{
endpoint::{OwnerConsent, Solicitation},
@@ -41,15 +41,15 @@ pub(crate) async fn authorize(
// Redirect to the login page if no token or token not known.
let hostname = services.config.server_name.host();
let Some(token) = oauth.authorization_header() else {
return Ok(oidc_login_form(hostname, &query));
};
let Some(token) = oauth.authorization_header() else {
return Ok(oidc_login_form(hostname, &query));
};
tracing::debug!("submitting OIDC authorisation for token : {token:#?}");
// Get the user id from the token and add it to the query.
let (owner_id, _) = services.oidc.user_and_device_from_token(token).await?;
let mut query_with_user_id = query.clone();
query_with_user_id.username = Some(owner_id.localpart().to_string());
let mut query_with_user_id = query.clone();
query_with_user_id.username = Some(owner_id.localpart().to_owned());
services
.oidc
@@ -88,47 +88,49 @@ pub(crate) async fn authorize_consent(
};
let server_name = services.globals.server_name();
let owner_id = UserId::parse_with_server_name(owner_id.clone(), server_name)
.map_err(|err|
err!(Request(InvalidUsername("invalid username {owner_id:?}: {err}")))
)?;
let Some(matrix_client) = services.oidc
.map_err(|err| err!(Request(InvalidUsername("invalid username {owner_id:?}: {err}"))))?;
let Some(matrix_client) = services
.oidc
.client_from_client_id(&query.client_id)
.await? else {
return Err(err!(Request(Unknown("no client has registered client_id {:?}", query.client_id))));
.await?
else {
return Err(err!(Request(Unknown(
"no client has registered client_id {:?}",
query.client_id
))));
};
let scope = query.scope
.parse()
.map_err(|err|
err!(Request(Unknown("could not parse scope {:?}: {}", query.scope, err)))
)?;
let device_id = services.oidc.device_id_from_scope(scope)?;
let scope = query.scope.parse().map_err(|err| {
err!(Request(Unknown("could not parse scope {:?}: {}", query.scope, err)))
})?;
let device_id = services.oidc.device_id_from_scope(&scope)?;
// Check that the device is registered in the owner devices list.
// Note that this is _not_ the OIDC client registration.
let device_is_registered_with_owner = services
.users
.all_device_ids(&owner_id)
.ready_any(|v| v == device_id)
.await;
if ! device_is_registered_with_owner {
.await;
if !device_is_registered_with_owner {
// TODO get the client's IP from the request.
let client_ip = None;
services.oidc.register_device(
&query.client_id,
(&owner_id, &device_id),
matrix_client.name.as_deref(),
client_ip,
).await?;
services
.oidc
.register_device(
&query.client_id,
(&owner_id, &device_id),
matrix_client.name.as_deref(),
client_ip,
)
.await?;
}
services
.oidc
.endpoint()
.with_solicitor(FnSolicitor(
move |_: &mut _, _: Solicitation<'_>| match allow.clone() {
| None => OwnerConsent::Denied,
| Some(user_id) => OwnerConsent::Authorized(user_id),
},
))
.with_solicitor(FnSolicitor(move |_: &mut _, _: Solicitation<'_>| match allow.clone() {
| None => OwnerConsent::Denied,
| Some(user_id) => OwnerConsent::Authorized(user_id),
}))
.authorization_flow()
.execute(oauth)
.map_err(|err| err!(Request(Unknown("consent request failed: {err:?}"))))
+1 -3
View File
@@ -1,8 +1,6 @@
use axum::extract::State;
use conduwuit::{Result, err, utils::hash::verify_password};
use conduwuit_web::oidc::{
LoginError, LoginQuery, OidcRequest, OidcResponse, oidc_consent_form,
};
use conduwuit_web::oidc::{LoginError, LoginQuery, OidcRequest, OidcResponse, oidc_consent_form};
use ruma::user_id::UserId;
//#[axum::debug_handler]
+27 -28
View File
@@ -1,9 +1,9 @@
use axum::{Json, extract::State};
use conduwuit::{Result, err};
use conduwuit_service::oidc::normalize_redirect;
use oxide_auth::primitives::prelude::Client;
use reqwest::Url;
use ruma::{DeviceId, ClientSecret, identifiers_validation};
use conduwuit_service::oidc::normalize_redirect;
use ruma::{ClientSecret, DeviceId, identifiers_validation};
/// The required parameters to register a new client for OAuth2 application.
/// See the required metadata in OAuth2 authorization grant flow in [MSC2966].
@@ -22,8 +22,9 @@ pub(crate) struct ClientQuery {
response_types: Vec<String>,
/// Must include the literals "authorization_code" and "refresh_token".
grant_types: Vec<String>,
/// How the client intends to authenticate its requests. Can be "none", meaning
/// that the client will negotiate its token with the "authorization code" flow.
/// How the client intends to authenticate its requests. Can be "none",
/// meaning that the client will negotiate its token with the
/// "authorization code" flow.
token_endpoint_auth_method: String,
/// Link to the logo.
logo_uri: Option<Url>,
@@ -42,8 +43,8 @@ pub(crate) struct ClientResponse {
client_id: String,
/// If the client is private, the secret it authenticates itself with.
client_secret: Option<String>,
/// If there's a `client_secret`, its expiration date in seconds since 1970-01-01T00:00.
/// Some(0) means no expiration date.
/// If there's a `client_secret`, its expiration date in seconds since
/// 1970-01-01T00:00. Some(0) means no expiration date.
client_secret_expires_at: Option<u32>,
client_name: String,
/// Points to the "about" page of the client.
@@ -51,10 +52,10 @@ pub(crate) struct ClientResponse {
logo_uri: Option<Url>,
tos_uri: Option<Url>,
policy_uri: Option<Url>,
/// Registered redirect uris, which will be matched against when authenticating.
/// If a localhost address, must contain instances of oxide-auth's
/// `RegisteredUrl::IgnorePortOnLocalhost` to let authorization flow through any port over
/// localhost.
/// Registered redirect uris, which will be matched against when
/// authenticating. If a localhost address, must contain instances of
/// oxide-auth's `RegisteredUrl::IgnorePortOnLocalhost` to let
/// authorization flow through any port over localhost.
redirect_uris: Vec<Url>,
token_endpoint_auth_method: String,
response_types: Vec<String>,
@@ -74,20 +75,18 @@ pub(crate) async fn register_client(
Json(client): Json<ClientQuery>,
) -> Result<Json<ClientResponse>> {
tracing::trace!("processing OIDC device register request for client: {client:#?}");
if client.redirect_uris.len() == 0 {
return Err(err!(Request(Unknown(
"the client's registration request should contain at least a redirect_uri"
))));
if client.redirect_uris.is_empty() {
return Err(err!(Request(Unknown(
"the client's registration request should contain at least a redirect_uri"
))));
}
let mut redirect_uris = client.redirect_uris.clone();
let redirect_uri = redirect_uris.pop().expect("at least one redirect_uri");
let redirect_uri = normalize_redirect(redirect_uri);
let remaining_uris = redirect_uris
.into_iter()
.map(|url| normalize_redirect(url))
.collect();
let remaining_uris = redirect_uris.into_iter().map(normalize_redirect).collect();
let device_id = DeviceId::new();
// Only provide a default scope, we'll test the client's proposed scope for consent anyway.
// Only provide a default scope, we'll test the client's proposed scope for
// consent anyway.
let scope = "default".parse().unwrap();
// TODO check if the users service needs an update.
//services.users.update_device_metadata();
@@ -96,12 +95,12 @@ pub(crate) async fn register_client(
// it's a public client. This is usually the case in Matrix.
let is_private = client.token_endpoint_auth_method != "none";
let client_secret = match is_private {
true => {
| true => {
let secret = ClientSecret::new();
identifiers_validation::client_secret::validate(secret.as_str())?;
Some(secret.to_string())
},
false => None
| false => None,
};
let registration = match is_private {
| true => &Client::confidential(
@@ -109,15 +108,15 @@ pub(crate) async fn register_client(
redirect_uri,
scope,
client_secret.as_ref().unwrap().as_bytes(),
).with_additional_redirect_uris(remaining_uris),
| _ => &Client::public(
device_id.as_ref(),
redirect_uri,
scope,
).with_additional_redirect_uris(remaining_uris)
)
.with_additional_redirect_uris(remaining_uris),
| _ => &Client::public(device_id.as_ref(), redirect_uri, scope)
.with_additional_redirect_uris(remaining_uris),
};
tracing::trace!("registering OIDC device : {registration:#?}");
services.oidc.register_client(Some(client.client_name.clone()), &registration);
services
.oidc
.register_client(Some(client.client_name.clone()), registration);
let client_response = ClientResponse {
client_id: device_id.to_string(),
+8 -8
View File
@@ -434,14 +434,14 @@ pub(super) static MAPS: &[Descriptor] = &[
name: "userroomid_notificationcount",
..descriptor::RANDOM
},
Descriptor {
name: "client_registrar",
..descriptor::RANDOM
},
Descriptor {
name: "deviceid_clientidmap",
..descriptor::RANDOM_SMALL
},
Descriptor {
name: "client_registrar",
..descriptor::RANDOM
},
Descriptor {
name: "deviceid_clientidmap",
..descriptor::RANDOM_SMALL
},
Descriptor {
name: "userroomid_invitesender",
..descriptor::RANDOM_SMALL
+106 -103
View File
@@ -5,11 +5,15 @@
//!
//! [oxide-auth]: https://docs.rs/oxide-auth
use std::{borrow::Cow, sync::{Arc, Mutex}};
use std::{
borrow::Cow,
sync::{Arc, Mutex},
};
use async_trait::async_trait;
use conduwuit::{err, Result};
use conduwuit::{Result, err};
use conduwuit_core::utils;
use database::{Deserialized, Json, Map};
use oxide_auth::{
endpoint::{PreGrant, Scope},
frontends::simple::endpoint::{Generic, Vacant},
@@ -24,25 +28,17 @@ use oxide_auth::{
},
};
use ruma::{
api::client::device::Device,
MilliSecondsSinceUnixEpoch,
OwnedDeviceId,
OwnedUserId,
UserId,
MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedUserId, UserId, api::client::device::Device,
};
use database::{Deserialized, Json, Map};
use serde::{Deserialize, Serialize};
use url::Url;
use once_cell::sync::Lazy;
use crate::{globals, Dep};
use crate::{Dep, globals};
pub const SCOPE_PREFIX_DEVICE: &str = "urn:matrix:org.matrix.msc2967.client:device:";
pub const SCOPE_PREFIX_API : &str = "urn:matrix:org.matrix.msc2967.client:api:";
static PASSWORD_POLICY: Lazy<Argon2> = Lazy::new(Argon2::default);
pub const SCOPE_PREFIX_API: &str = "urn:matrix:org.matrix.msc2967.client:api:";
static PASSWORD_POLICY: std::sync::LazyLock<Argon2> = std::sync::LazyLock::new(Argon2::default);
/// A client app that connects to continuwuity via OIDC, as recorded in the
/// database.
@@ -116,14 +112,12 @@ impl Service {
let client = client.clone().encode(&*PASSWORD_POLICY);
let client_id = client.client_id.clone();
let oidc_client = OidcClient {
name: display_name.clone(),
name: display_name,
// Matrix clients have no device_id at registration time.
device_id: None,
client,
};
self.db
.client_registrar
.put(client_id, Json(oidc_client));
self.db.client_registrar.put(client_id, Json(oidc_client));
}
/// Register a device in the main continuwuity database. This should only
@@ -139,17 +133,15 @@ impl Service {
let device_key = (user_id, device_id);
let device = Device {
device_id: device_id.into(),
display_name: display_name.map(|n| n.to_string()),
display_name: display_name.map(ToOwned::to_owned),
last_seen_ip: client_ip,
last_seen_ts: Some(MilliSecondsSinceUnixEpoch::now()),
};
increment(&self.db.userid_devicelistversion, user_id.as_bytes())
.await;
self.db
.userdeviceid_metadata
.put(device_key, Json(device));
increment(&self.db.userid_devicelistversion, user_id.as_bytes()).await;
self.db.userdeviceid_metadata.put(device_key, Json(device));
let mut client : OidcClient = self.db
let mut client: OidcClient = self
.db
.client_registrar
.get(client_id)
.await?
@@ -157,11 +149,11 @@ impl Service {
client.device_id = Some(device_id.to_string());
self.db
.client_registrar
.put(client_id.to_string(), Json(client));
.put(client_id.to_owned(), Json(client));
self.db
.deviceid_clientidmap
.put(device_id, client_id.to_string());
.put(device_id, client_id.to_owned());
Ok(())
}
@@ -169,14 +161,12 @@ impl Service {
fn grant_from_token(&self, token: &str) -> Option<Grant> {
let issuer = self.issuer.lock().expect("lockable issuer");
issuer.recover_token(token)
issuer
.recover_token(token)
.expect("infallible recover_token implementation")
}
pub async fn client_from_client_id(
&self,
client_id: &str,
) -> Result<Option<OidcClient>> {
pub async fn client_from_client_id(&self, client_id: &str) -> Result<Option<OidcClient>> {
self.db
.client_registrar
.get(client_id)
@@ -188,7 +178,8 @@ impl Service {
&self,
device_id: OwnedDeviceId,
) -> Result<Option<OidcClient>> {
let client_id: String = self.db
let client_id: String = self
.db
.deviceid_clientidmap
.get(&device_id)
.await?
@@ -201,19 +192,14 @@ impl Service {
.deserialized()
}
pub fn device_id_from_scope(&self, scope: Scope) -> Result<OwnedDeviceId> {
let Some(device_id) = scope
.iter()
.find(|s| s.starts_with(SCOPE_PREFIX_DEVICE)) else {
tracing::warn!("device_id not found in scope {scope:?}");
return Err(err!(Request(InvalidParam("something went wrong with the scope"))));
};
pub fn device_id_from_scope(&self, scope: &Scope) -> Result<OwnedDeviceId> {
let Some(device_id) = scope.iter().find(|s| s.starts_with(SCOPE_PREFIX_DEVICE)) else {
tracing::warn!("device_id not found in scope {scope:?}");
return Err(err!(Request(InvalidParam("something went wrong with the scope"))));
};
let device_id = device_id.replace(SCOPE_PREFIX_DEVICE, "");
OwnedDeviceId::try_from(device_id.clone())
.map_err(|err|
err!(Request(InvalidParam("invalid device_id {device_id:?}: {err}")))
)
Ok(device_id.into())
}
pub async fn user_and_device_from_token(
@@ -224,17 +210,18 @@ impl Service {
return Err(err!(Request(MissingToken("unknown token: {token:?}"))));
};
let server_name = self.services.globals.server_name();
let owner_id = UserId::parse_with_server_name(owner_id.clone(), server_name)
.map_err(|err|
let owner_id =
UserId::parse_with_server_name(owner_id.clone(), server_name).map_err(|err| {
err!(Request(InvalidUsername("invalid username {owner_id:?}: {err}")))
)?;
let client = self.client_from_client_id(&client_id)
})?;
let client = self
.client_from_client_id(&client_id)
.await?
.expect("validated client_id");
let Some(device_id) = client.device_id else {
return Err(err!(Request(Unknown("this client has no device_id yet"))));
};
let device_id = OwnedDeviceId::from(device_id.to_string());
let device_id = OwnedDeviceId::from(device_id);
Ok((owner_id, device_id))
}
@@ -264,95 +251,111 @@ async fn increment(db: &Arc<Map>, key: &[u8]) {
/// Substitute "127.0.0.1" and "[::1]" for "localhost" to let oxide-auth compare
/// them ignoring their port.
pub fn normalize_redirect_hostname(url: Url) -> Url {
let mut new_url = url.clone();
let new_host = url.host_str().map(|h|
h.replace("127.0.0.1", "localhost").replace("[::1]", "localhost")
);
new_url.set_host(new_host.as_deref()).expect("replaceable redirect hostname");
pub fn normalize_redirect_hostname(url: &mut Url) {
let new_host = url.host_str().map(|h| {
h.replace("127.0.0.1", "localhost")
.replace("[::1]", "localhost")
});
new_url
url.set_host(new_host.as_deref())
.expect("replaceable redirect hostname");
}
/// If `url` is a localhost (either 'localhost', '127.0.0.1' or '[::1]'), wrap
/// it in an `IgnorePortOnLocalhost`, so that oxide-auth ignores the port when
/// comparing it with the registered ones.
pub fn normalize_redirect(url: Url) -> RegisteredUrl {
let new_url = normalize_redirect_hostname(url);
#[must_use]
pub fn normalize_redirect(mut url: Url) -> RegisteredUrl {
normalize_redirect_hostname(&mut url);
match new_url.host_str() {
Some("localhost") => RegisteredUrl::IgnorePortOnLocalhost(new_url.into()),
_ => RegisteredUrl::Semantic(new_url)
match url.host_str() {
| Some("localhost") => RegisteredUrl::IgnorePortOnLocalhost(url.into()),
| _ => RegisteredUrl::Semantic(url),
}
}
/// Let this service act as an oxide-auth `Registrar`.
impl Registrar for Service {
fn bound_redirect<'a>(&self, bound: ClientUrl<'a>) -> Result<BoundClient<'a>, RegistrarError> {
let client_handle = self.db
fn bound_redirect<'a>(
&self,
bound: ClientUrl<'a>,
) -> Result<BoundClient<'a>, RegistrarError> {
let client_handle = self
.db
.client_registrar
.get_blocking(bound.client_id.as_ref())
.map_err(|_| RegistrarError::Unspecified)?;
let oidc_client: OidcClient = client_handle.deserialized()
let oidc_client: OidcClient = client_handle
.deserialized()
.map_err(|_| RegistrarError::Unspecified)?;
let client = oidc_client.client;
// Perform exact matching as motivated in the rfc, but substitute
// Perform exact matching as motivated in the rfc, but substitute
// "127.0.0.1" and "[::1]" for "localhost" to let oxide-auth ignore
// their port.
let redirect_uri = bound.redirect_uri;
let normalized_uri = redirect_uri
.clone()
.map(|u| normalize_redirect(u.to_url()));
let redirect_uri = match normalized_uri {
None => client.redirect_uri.clone(),
Some(url) => {
let original = std::iter::once(&client.redirect_uri);
let alternatives = client.additional_redirect_uris.iter();
if original
.chain(alternatives)
.any(|registered| *registered == url)
{
let normalized_uri = redirect_uri.clone().map(|u| normalize_redirect(u.to_url()));
let redirect_uri = match normalized_uri {
| None => client.redirect_uri,
| Some(url) => {
let original = std::iter::once(&client.redirect_uri);
let alternatives = client.additional_redirect_uris.iter();
if original
.chain(alternatives)
.any(|registered| *registered == url)
{
// If normalized_uri is Some(url), so is redirect_uri, so unwrap().
redirect_uri.unwrap().into_owned().into()
} else {
tracing::debug!("the request's redirect url didn't match any registered. bound: {:?}, in client {:#?}", url, client);
return Err(RegistrarError::Unspecified);
}
}
};
redirect_uri.unwrap().into_owned().into()
} else {
tracing::debug!(
"the request's redirect url didn't match any registered. bound: {:?}, \
in client {:#?}",
url,
client
);
return Err(RegistrarError::Unspecified);
}
},
};
Ok(BoundClient {
client_id: bound.client_id,
redirect_uri: Cow::Owned(redirect_uri),
})
Ok(BoundClient {
client_id: bound.client_id,
redirect_uri: Cow::Owned(redirect_uri),
})
}
fn negotiate(&self, bound: BoundClient<'_>, _scope: Option<Scope>) -> Result<PreGrant, RegistrarError> {
let client_handle = self.db
fn negotiate(
&self,
bound: BoundClient<'_>,
_scope: Option<Scope>,
) -> Result<PreGrant, RegistrarError> {
let client_handle = self
.db
.client_registrar
.get_blocking(bound.client_id.as_ref())
.map_err(|_| RegistrarError::Unspecified)?;
let oidc_client: OidcClient = client_handle.deserialized()
let oidc_client: OidcClient = client_handle
.deserialized()
.map_err(|_| RegistrarError::Unspecified)?;
Ok(PreGrant {
client_id: bound.client_id.into_owned(),
redirect_uri: bound.redirect_uri.into_owned(),
Ok(PreGrant {
client_id: bound.client_id.into_owned(),
redirect_uri: bound.redirect_uri.into_owned(),
// Always use the client's scope.
scope: oidc_client.client.default_scope.clone(),
})
}
scope: oidc_client.client.default_scope,
})
}
fn check(&self, client_id: &str, passphrase: Option<&[u8]>) -> Result<(), RegistrarError> {
let client_handle = self.db
fn check(&self, client_id: &str, passphrase: Option<&[u8]>) -> Result<(), RegistrarError> {
let client_handle = self
.db
.client_registrar
.get_blocking(client_id)
.map_err(|_| RegistrarError::Unspecified)?;
let oidc_client: OidcClient = client_handle.deserialized()
let oidc_client: OidcClient = client_handle
.deserialized()
.map_err(|_| RegistrarError::Unspecified)?;
let client = oidc_client.client;
RegisteredClient::new(&client, &*PASSWORD_POLICY)
.check_authentication(passphrase)
}
RegisteredClient::new(&client, &*PASSWORD_POLICY).check_authentication(passphrase)
}
}
+30 -16
View File
@@ -272,7 +272,14 @@ impl Service {
/// Find out which user an access token belongs to.
pub async fn find_from_token(&self, token: &str) -> Result<(OwnedUserId, OwnedDeviceId)> {
if self.services.server.config.auth.as_ref().is_some_and(|auth| auth.enable_oidc_login) {
if self
.services
.server
.config
.auth
.as_ref()
.is_some_and(|auth| auth.enable_oidc_login)
{
self.services.oidc.user_and_device_from_token(token).await
} else {
self.db.token_userdeviceid.get(token).await.deserialized()
@@ -542,22 +549,29 @@ impl Service {
// Only existing devices should be able to call this, but we shouldn't assert
// either...
let key = (user_id, device_id);
if self.services.server.config.auth.as_ref().is_some_and(|auth| auth.enable_oidc_login) {
if self.services.oidc.client_from_device_id(device_id.into()).await?.is_none() {
return Err!(Database(error!(
?user_id,
?device_id,
"Device has no metadata."
)));
}
} else {
if self.db.userdeviceid_metadata.qry(&key).await.is_err() {
return Err!(Database(error!(
?user_id,
?device_id,
"User does not exist or device has no metadata."
)));
if self
.services
.server
.config
.auth
.as_ref()
.is_some_and(|auth| auth.enable_oidc_login)
{
if self
.services
.oidc
.client_from_device_id(device_id.into())
.await?
.is_none()
{
return Err!(Database(error!(?user_id, ?device_id, "Device has no metadata.")));
}
} else if self.db.userdeviceid_metadata.qry(&key).await.is_err() {
return Err!(Database(error!(
?user_id,
?device_id,
"User does not exist or device has no metadata."
)));
}
let mut key = user_id.as_bytes().to_vec();
+1 -1
View File
@@ -1,9 +1,9 @@
use askama::Template;
use conduwuit_build_metadata::version_tag;
use percent_encoding::{NON_ALPHANUMERIC, utf8_percent_encode};
// Imports needed by askama templates.
use crate::{GIT_REMOTE_COMMIT_URL, GIT_REMOTE_WEB_URL};
use conduwuit_build_metadata::version_tag;
mod authorize;
mod consent;
+9 -2
View File
@@ -26,8 +26,15 @@ pub fn oidc_consent_form(hostname: &str, query: &AuthorizationQuery) -> OidcResp
/// Render the html contents of the user consent page.
fn consent_page(hostname: &str, query: &AuthorizationQuery, route: &str, nonce: &str) -> String {
let response_mode = &query.response_mode.clone().unwrap_or("fragment".to_string());
let user_id = query.username.clone().expect("user_id in authorization query");
let response_mode = query
.response_mode
.as_deref()
.unwrap_or("fragment")
.to_owned();
let user_id = query
.username
.clone()
.expect("user_id in authorization query");
let template = ConsentPageTemplate {
nonce,
hostname,
+4 -2
View File
@@ -64,7 +64,9 @@ impl TryFrom<OidcRequest> for LoginQuery {
};
// response_mode is not strictly needed : it must be the literal "fragment"
// when over https. It's required by the spec but Fractal doesn't provide it.
let response_mode = body.unique_value("response_mode").unwrap_or(Cow::Borrowed("fragment"));
let response_mode = body
.unique_value("response_mode")
.unwrap_or(Cow::Borrowed("fragment"));
let client_secret = body.unique_value("client_secret").map(|s| s.to_string());
Ok(Self {
@@ -104,7 +106,7 @@ pub fn oidc_login_form(hostname: &str, query: &AuthorizationQuery) -> OidcRespon
/// Render the html contents of the login page.
fn login_page(hostname: &str, query: &AuthorizationQuery, route: &str, nonce: &str) -> String {
let response_mode = &query.response_mode.clone().unwrap_or("fragment".to_string());
let response_mode = query.response_mode.as_deref().unwrap_or("fragment");
let template = LoginPageTemplate {
nonce,
hostname,
+2 -1
View File
@@ -28,7 +28,8 @@ impl IntoResponse for OidcResponse {
| Some(nonce) => &format!("default-src 'nonce-{nonce}';"),
| None => "default-src 'none';",
};
let csp_form_action = "form-action 'self' http://localhost http://127.0.0.1 http://[::1];";
let csp_form_action =
"form-action 'self' http://localhost http://127.0.0.1 http://[::1];";
let content_csp = format!("{csp_src} {csp_form_action}");
let content_type = match self.body {
| Some(OAuthRequestBody::Json(_)) => "application/json",