mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2025-09-10 22:52:50 +02:00
support OIDC private clients
This commit is contained in:
parent
14f6d07b27
commit
511e60b41d
7 changed files with 46 additions and 11 deletions
|
@ -2,7 +2,7 @@ use axum::{Json, extract::State};
|
|||
use conduwuit::{Result, err};
|
||||
use oxide_auth::primitives::prelude::Client;
|
||||
use reqwest::Url;
|
||||
use ruma::DeviceId;
|
||||
use ruma::{DeviceId, identifiers_validation};
|
||||
|
||||
/// The required parameters to register a new client for OAuth2 application.
|
||||
#[derive(serde::Deserialize, Clone, Debug)]
|
||||
|
@ -35,6 +35,8 @@ pub(crate) struct ClientQuery {
|
|||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) struct ClientResponse {
|
||||
client_id: String,
|
||||
client_secret: Option<String>,
|
||||
client_secret_expires_at: Option<u32>,
|
||||
client_name: String,
|
||||
client_uri: Url,
|
||||
logo_uri: Option<Url>,
|
||||
|
@ -68,18 +70,38 @@ pub(crate) async fn register_client(
|
|||
let scope = format!(
|
||||
"urn:matrix:org.matrix.msc2967.client:api:* \
|
||||
urn:matrix:org.matrix.msc2967.client:device:{device_id}"
|
||||
);
|
||||
).parse().expect("parseable default Matrix scope");
|
||||
// TODO check if the users service needs an update.
|
||||
//services.users.update_device_metadata();
|
||||
services.oidc.register_client(&Client::public(
|
||||
|
||||
// If the client cannot authenticate itself at the token endpoint, then
|
||||
// it's a public client.
|
||||
let is_private = client.token_endpoint_auth_method != "none";
|
||||
// TODO generate a device secret.
|
||||
let secret = "cacestdubonsecretmonlouou=--".to_string();
|
||||
if let Err(err) = identifiers_validation::client_secret::validate(&secret) {
|
||||
tracing::warn!("oops, we generated an invalid client_secret: {err}");
|
||||
}
|
||||
let registerable = match is_private {
|
||||
| true => &Client::confidential(
|
||||
device_id.as_ref(),
|
||||
redirect_uri.into(),
|
||||
scope
|
||||
.parse()
|
||||
.expect("device ID should parse in Matrix scope"),
|
||||
))?;
|
||||
redirect_uri,
|
||||
scope,
|
||||
secret.as_bytes(),
|
||||
).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 : {registerable:#?}");
|
||||
services.oidc.register_client(®isterable)?;
|
||||
|
||||
let client_response = ClientResponse {
|
||||
client_id: device_id.to_string(),
|
||||
client_secret: if is_private { Some(secret) } else { None },
|
||||
client_secret_expires_at: if is_private { Some(0) } else { None },
|
||||
client_name: client.client_name.clone(),
|
||||
client_uri: client.client_uri.clone(),
|
||||
redirect_uris: client.redirect_uris.clone(),
|
||||
|
|
|
@ -26,6 +26,7 @@ pub(crate) struct LoginPageTemplate<'a> {
|
|||
hostname: &'a str,
|
||||
route: &'a str,
|
||||
client_id: &'a str,
|
||||
client_secret: Option<&'a str>,
|
||||
redirect_uri: &'a str,
|
||||
scope: &'a str,
|
||||
state: &'a str,
|
||||
|
@ -43,6 +44,7 @@ pub(crate) struct ConsentPageTemplate<'a> {
|
|||
hostname: &'a str,
|
||||
route: &'a str,
|
||||
client_id: &'a str,
|
||||
client_secret: Option<&'a str>,
|
||||
redirect_uri: &'a str,
|
||||
scope: &'a str,
|
||||
state: &'a str,
|
||||
|
|
|
@ -6,6 +6,7 @@ use super::LoginQuery;
|
|||
#[derive(serde::Deserialize, Debug)]
|
||||
pub struct AuthorizationQuery {
|
||||
pub client_id: String,
|
||||
pub client_secret: Option<String>,
|
||||
pub redirect_uri: Url,
|
||||
pub scope: String,
|
||||
pub state: String,
|
||||
|
@ -19,6 +20,7 @@ impl From<LoginQuery> for AuthorizationQuery {
|
|||
fn from(value: LoginQuery) -> Self {
|
||||
let LoginQuery {
|
||||
client_id,
|
||||
client_secret,
|
||||
redirect_uri,
|
||||
scope,
|
||||
state,
|
||||
|
@ -31,6 +33,7 @@ impl From<LoginQuery> for AuthorizationQuery {
|
|||
|
||||
Self {
|
||||
client_id,
|
||||
client_secret,
|
||||
redirect_uri,
|
||||
scope,
|
||||
state,
|
||||
|
|
|
@ -37,6 +37,7 @@ fn consent_page(hostname: &str, query: &AuthorizationQuery, route: &str, nonce:
|
|||
hostname,
|
||||
route,
|
||||
client_id: &encode(query.client_id.as_str()),
|
||||
client_secret: query.client_secret.as_deref(),
|
||||
redirect_uri: &encode(query.redirect_uri.as_str()),
|
||||
scope: &encode(query.scope.as_str()),
|
||||
state: &encode(query.state.as_str()),
|
||||
|
|
|
@ -13,6 +13,7 @@ pub struct LoginQuery {
|
|||
pub username: String,
|
||||
pub password: String,
|
||||
pub client_id: String,
|
||||
pub client_secret: Option<String>,
|
||||
pub redirect_uri: Url,
|
||||
pub scope: String,
|
||||
pub state: String,
|
||||
|
@ -68,11 +69,13 @@ impl TryFrom<OidcRequest> for LoginQuery {
|
|||
| "https" => Cow::Borrowed("fragment"),
|
||||
| _ => Cow::Borrowed("query")
|
||||
});
|
||||
let client_secret = body.unique_value("client_secret").map(|s| s.to_string());
|
||||
|
||||
Ok(Self {
|
||||
username: username.to_string(),
|
||||
password: password.to_string(),
|
||||
client_id: client_id.to_string(),
|
||||
client_secret,
|
||||
redirect_uri,
|
||||
scope: scope.to_string(),
|
||||
state: state.to_string(),
|
||||
|
@ -116,6 +119,7 @@ fn login_page(hostname: &str, query: &AuthorizationQuery, route: &str, nonce: &s
|
|||
hostname,
|
||||
route,
|
||||
client_id: query.client_id.as_str(),
|
||||
client_secret: query.client_secret.as_deref(),
|
||||
redirect_uri: query.redirect_uri.as_str(),
|
||||
scope: query.scope.as_str(),
|
||||
state: query.state.as_str(),
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
'{{ client_id }}' (at {{ redirect_uri }}) is requesting permission for '{{ scope }}'
|
||||
</p>
|
||||
<form method="post">
|
||||
<input type="submit" value="Accept" formaction="{{ route }}?client_id={{ client_id }}&redirect_uri={{ redirect_uri }}&scope={{scope }}&state={{ state }}&code_challenge={{ code_challenge }}&code_challenge_method={{ code_challenge_method }}&response_type={{ response_type }}&response_mode={{ response_mode }}&allow=true">
|
||||
<input type="submit" value="Deny" formaction="{{ route }}?client_id={{ client_id }}&redirect_uri={{ redirect_uri }}&scope={{scope }}&state={{ state }}&code_challenge={{ code_challenge }}&code_challenge_method={{ code_challenge_method }}&response_type={{ response_type }}&response_mode={{ response_mode }}&deny=true">
|
||||
<input type="submit" value="Accept" formaction="{{ route }}?client_id={{ client_id }}{%- if let Some(secret) = client_secret -%}&client_secret={{ secret }}{%- endif -%}&redirect_uri={{ redirect_uri }}&scope={{ scope }}&state={{ state }}&code_challenge={{ code_challenge }}&code_challenge_method={{ code_challenge_method }}&response_type={{ response_type }}&response_mode={{ response_mode }}&allow=true">
|
||||
<input type="submit" value="Deny" formaction="{{ route }}?client_id={{ client_id }}{%- if let Some(secret) = client_secret -%}&client_secret={{ secret }}{%- endif -%}&redirect_uri={{ redirect_uri }}&scope={{scope }}&state={{ state }}&code_challenge={{ code_challenge }}&code_challenge_method={{ code_challenge_method }}&response_type={{ response_type }}&response_mode={{ response_mode }}&deny=true">
|
||||
</form>
|
||||
</div>
|
||||
{%- endblock content -%}
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
<input type="text" name="username" placeholder="Username" required>
|
||||
<input type="password" name="password" placeholder="Password" required>
|
||||
<input type="hidden" name="client_id" value="{{ client_id }}">
|
||||
{%- if let Some(secret) = client_secret -%}
|
||||
<input type="hidden" name="client_secret" value="{{ secret }}">
|
||||
{%- endif -%}
|
||||
<input type="hidden" name="redirect_uri" value="{{ redirect_uri }}">
|
||||
<input type="hidden" name="scope" value="{{ scope }}">
|
||||
<input type="hidden" name="state" value="{{ state }}">
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue