feat: allow connecting to ws via oauth2

This commit is contained in:
Zomatree
2025-07-13 23:51:09 +01:00
parent ef65223c89
commit f895ca7b23
18 changed files with 214 additions and 70 deletions

View File

@@ -33,7 +33,7 @@ pub async fn auth(
return Err(create_error!(InvalidOperation));
};
if scopes.into_iter().any(|scope| !oauth2.allowed_scopes.contains_key(&scope.into()))
if scopes.iter().any(|&scope| !oauth2.allowed_scopes.contains_key(&scope.into()))
|| !oauth2.redirects.contains(&info.redirect_uri)
|| v0::OAuth2Scope::scopes_from_str(&info.scope).is_none()
{
@@ -55,7 +55,7 @@ pub async fn auth(
user.id.clone(),
info.client_id.clone(),
info.redirect_uri.clone(),
info.scope.clone(),
scopes.clone(),
info.code_challenge_method,
)
.map_err(|_| create_error!(InternalError))?;
@@ -64,7 +64,7 @@ pub async fn auth(
id: AuthorizedBotId { bot: info.client_id.clone(), user: user.id.clone() },
created_at: Timestamp::now_utc(),
deauthorized_at: None,
scope: info.scope.clone()
scope: scopes.iter().map(|&scope| scope.into()).collect()
}).await?;
token
@@ -87,11 +87,11 @@ pub async fn auth(
let token = oauth2::encode_token(
&config.api.security.token_secret,
oauth2::TokenType::Access,
oauth2::TokenType::Auth,
user.id.clone(),
info.client_id.clone(),
info.redirect_uri.clone(),
info.scope.clone(),
scopes.clone(),
info.code_challenge_method,
)
.map_err(|_| create_error!(InternalError))?;

View File

@@ -45,10 +45,10 @@ pub async fn token(
return Err(create_error!(NotAuthenticated))
};
let authorized_bot = db.fetch_authorized_bot(&AuthorizedBotId { user: claims.sub.clone(), bot: claims.client_id.clone() }).await?;
if authorized_bot.deauthorized_at.is_some() {
return Err(create_error!(NotAuthenticated));
if let Ok(authorized_bot) = db.fetch_authorized_bot(&AuthorizedBotId { user: claims.sub.clone(), bot: claims.client_id.clone() }).await {
if authorized_bot.deauthorized_at.is_some() {
return Err(create_error!(NotAuthenticated));
}
};
// TODO: track used tokens and dont allow them to be used twice
@@ -88,7 +88,7 @@ pub async fn token(
claims.sub.clone(),
claims.client_id.clone(),
claims.redirect_uri.clone(),
claims.scope.clone(),
claims.scopes.clone(),
None,
)
.map_err(|_| create_error!(InternalError))?;
@@ -99,19 +99,23 @@ pub async fn token(
claims.sub.clone(),
claims.client_id.clone(),
claims.redirect_uri.clone(),
claims.scope.clone(),
claims.scopes.clone(),
None,
)
.map_err(|_| create_error!(InternalError))?;
let authorized_bot_id = AuthorizedBotId { bot: claims.client_id.clone(), user: claims.sub.clone() };
let auth_bot = db.fetch_authorized_bot(&authorized_bot_id).await;
println!("{auth_bot:?}");
if auth_bot.is_err_and(|err| err.error_type == ErrorType::NotFound) {
println!("inserting");
if db.fetch_authorized_bot(&authorized_bot_id).await.is_err_and(|err| err.error_type == ErrorType::NotFound) {
db.insert_authorized_bot(&AuthorizedBot {
id: authorized_bot_id,
created_at: Timestamp::now_utc(),
deauthorized_at: None,
scope: claims.scope.clone()
scope: claims.scopes.iter().map(|&scope| scope.into()).collect()
}).await?;
}
@@ -119,7 +123,7 @@ pub async fn token(
access_token: token,
refresh_token: Some(refresh_token),
token_type: "OAuth2".to_string(),
scope: claims.scope,
scope: claims.scopes.iter().map(|scope| scope.to_string()).collect::<Vec<_>>().join(" "),
}))
},
v0::OAuth2GrantType::RefreshToken => {
@@ -133,7 +137,7 @@ pub async fn token(
claims.sub.clone(),
claims.client_id.clone(),
claims.redirect_uri.clone(),
claims.scope.clone(),
claims.scopes.clone(),
None,
)
.map_err(|_| create_error!(InternalError))?;
@@ -144,7 +148,7 @@ pub async fn token(
claims.sub.clone(),
claims.client_id.clone(),
claims.redirect_uri.clone(),
claims.scope.clone(),
claims.scopes.clone(),
None,
)
.map_err(|_| create_error!(InternalError))?;
@@ -153,7 +157,7 @@ pub async fn token(
access_token: token,
refresh_token: Some(refresh_token),
token_type: "OAuth2".to_string(),
scope: claims.scope,
scope: claims.scopes.iter().map(|scope| scope.to_string()).collect::<Vec<_>>().join(" "),
}))
}
v0::OAuth2GrantType::Implicit => {

View File

@@ -29,7 +29,7 @@ pub fn routes() -> (Vec<Route>, OpenApi) {
openapi_get_routes_spec![
server_create::create_server,
server_delete::delete,
server_fetch::fetch,
server_fetch::fetch_server,
server_edit::edit,
server_ack::ack,
channel_create::create_server_channel,

View File

@@ -12,7 +12,7 @@ use rocket::{serde::json::Json, State};
/// Fetch a server by its id.
#[openapi(tag = "Server Information")]
#[get("/<target>?<options..>")]
pub async fn fetch(
pub async fn fetch_server(
db: &State<Database>,
user: User,
target: Reference<'_>,

View File

@@ -8,6 +8,6 @@ use rocket::serde::json::Json;
/// Retrieve your user information.
#[openapi(tag = "User Information")]
#[get("/@me")]
pub async fn fetch(user: User) -> Result<Json<v0::User>> {
pub async fn fetch_self(user: User) -> Result<Json<v0::User>> {
Ok(Json(user.into_self(false).await))
}

View File

@@ -0,0 +1,53 @@
use revolt_database::{util::permissions::DatabasePermissionQuery, Database, User};
use revolt_models::v0;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::Result;
use rocket::{serde::json::Json, State};
/// # Fetch current user's servers
///
/// Retrieve all servers the current user is in.
#[openapi(tag = "User Information")]
#[get("/@me/servers?<options..>")]
pub async fn fetch_self_servers(
db: &State<Database>,
user: User,
options: v0::OptionsFetchServer,
) -> Result<Json<Vec<v0::FetchServerResponse>>> {
let members = db.fetch_all_memberships(&user.id).await?;
let server_ids = members
.iter()
.map(|x| x.id.server.clone())
.collect::<Vec<_>>();
let mut servers: Vec<v0::FetchServerResponse> = Vec::new();
for server in db.fetch_servers(&server_ids).await? {
if let Some(true) = options.include_channels {
let query = DatabasePermissionQuery::new(db, &user).server(&server);
let all_channels = db.fetch_channels(&server.channels).await?;
let mut visible_channels: Vec<v0::Channel> = vec![];
for channel in all_channels {
let mut channel_query = query.clone().channel(&channel);
if calculate_channel_permissions(&mut channel_query)
.await
.has_channel_permission(ChannelPermission::ViewChannel)
{
visible_channels.push(channel.into());
}
}
servers.push(v0::FetchServerResponse::ServerWithChannels {
server: server.into(),
channels: visible_channels,
});
} else {
servers.push(v0::FetchServerResponse::JustServer(server.into()))
}
}
Ok(Json(servers))
}

View File

@@ -7,6 +7,7 @@ mod change_username;
mod edit_user;
mod fetch_dms;
mod fetch_profile;
mod fetch_self_servers;
mod fetch_self;
mod fetch_user;
mod fetch_user_flags;
@@ -20,7 +21,8 @@ mod unblock_user;
pub fn routes() -> (Vec<Route>, OpenApi) {
openapi_get_routes_spec![
// User Information
fetch_self::fetch,
fetch_self::fetch_self,
fetch_self_servers::fetch_self_servers,
fetch_user::fetch,
fetch_user_flags::fetch_user_flags,
edit_user::edit,