feat: initial oauth2 token revoking
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use revolt_config::config;
|
||||
use revolt_database::{
|
||||
util::{oauth2, reference::Reference}, Database, User
|
||||
util::{oauth2, reference::Reference}, AuthorizedBot, AuthorizedBotId, Database, User
|
||||
};
|
||||
use revolt_models::v0;
|
||||
use revolt_result::{create_error, Result};
|
||||
@@ -49,7 +50,7 @@ pub async fn auth(
|
||||
return Err(create_error!(InvalidOperation));
|
||||
};
|
||||
|
||||
oauth2::encode_token(
|
||||
let token = oauth2::encode_token(
|
||||
&config.api.security.token_secret,
|
||||
oauth2::TokenType::Auth,
|
||||
user.id.clone(),
|
||||
@@ -58,7 +59,16 @@ pub async fn auth(
|
||||
info.scope.clone(),
|
||||
info.code_challenge_method,
|
||||
)
|
||||
.map_err(|_| create_error!(InternalError))?
|
||||
.map_err(|_| create_error!(InternalError))?;
|
||||
|
||||
db.insert_authorized_bot(&AuthorizedBot {
|
||||
id: AuthorizedBotId { bot: info.client_id.clone(), user: user.id.clone() },
|
||||
created_at: Timestamp::now_utc(),
|
||||
deauthorized_at: None,
|
||||
scope: info.scope.clone()
|
||||
}).await?;
|
||||
|
||||
token
|
||||
},
|
||||
// authorization code
|
||||
v0::OAuth2ResponseType::Token => {
|
||||
|
||||
20
crates/delta/src/routes/oauth2/authorized_bots.rs
Normal file
20
crates/delta/src/routes/oauth2/authorized_bots.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use revolt_models::v0;
|
||||
use rocket::{serde::json::Json, State};
|
||||
use revolt_database::{Database, User};
|
||||
use revolt_result::Result;
|
||||
|
||||
|
||||
#[openapi(tag = "OAuth2")]
|
||||
#[get("/authorized_bots")]
|
||||
pub async fn authorized_bots(
|
||||
db: &State<Database>,
|
||||
user: User
|
||||
) -> Result<Json<Vec<v0::AuthorizedBot>>> {
|
||||
let authorized_bots = db.fetch_users_authorized_bots(&user.id).await?;
|
||||
|
||||
Ok(Json(authorized_bots
|
||||
.into_iter()
|
||||
.map(|bot| bot.into())
|
||||
.collect()
|
||||
))
|
||||
}
|
||||
@@ -3,6 +3,8 @@ use rocket::Route;
|
||||
|
||||
mod authorize_auth;
|
||||
mod authorize_info;
|
||||
mod authorized_bots;
|
||||
mod revoke;
|
||||
mod token;
|
||||
|
||||
// TODO
|
||||
@@ -15,6 +17,8 @@ pub fn routes() -> (Vec<Route>, OpenApi) {
|
||||
openapi_get_routes_spec![
|
||||
authorize_auth::auth,
|
||||
authorize_info::info,
|
||||
authorized_bots::authorized_bots,
|
||||
revoke::revoke,
|
||||
token::token,
|
||||
]
|
||||
}
|
||||
|
||||
50
crates/delta/src/routes/oauth2/revoke.rs
Normal file
50
crates/delta/src/routes/oauth2/revoke.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use revolt_config::config;
|
||||
use revolt_models::v0;
|
||||
use rocket::{serde::json::Json, State};
|
||||
use revolt_database::{util::oauth2::{self, TokenType}, AuthorizedBotId, Database, User};
|
||||
use revolt_result::{create_error, Result};
|
||||
|
||||
/// Revoke an OAuth2 token
|
||||
///
|
||||
/// This can either take user authorization and a client_id,
|
||||
/// or an OAuth2 token.
|
||||
#[openapi(tag = "OAuth2")]
|
||||
#[post("/revoke?<token>&<client_id>")]
|
||||
pub async fn revoke(
|
||||
db: &State<Database>,
|
||||
user: Option<User>,
|
||||
token: Option<&str>,
|
||||
client_id: Option<&str>
|
||||
) -> Result<Json<v0::AuthorizedBot>> {
|
||||
let config = config().await;
|
||||
|
||||
let (user_id, bot_id) = match (user, client_id, token) {
|
||||
(Some(user), Some(client_id), None) => {
|
||||
(user.id.clone(), client_id.to_string())
|
||||
},
|
||||
(_, None, Some(token)) => {
|
||||
let Ok(claims) = oauth2::decode_token(&config.api.security.token_secret, token) else {
|
||||
return Err(create_error!(NotAuthenticated))
|
||||
};
|
||||
|
||||
if claims.r#type == TokenType::Auth {
|
||||
return Err(create_error!(InvalidOperation))
|
||||
}
|
||||
|
||||
(claims.sub, claims.client_id)
|
||||
},
|
||||
_ => return Err(create_error!(InvalidOperation))
|
||||
};
|
||||
|
||||
let id = AuthorizedBotId { user: user_id, bot: bot_id };
|
||||
|
||||
let authorized_bot = db.fetch_authorized_bot(&id).await?;
|
||||
|
||||
if authorized_bot.deauthorized_at.is_some() {
|
||||
return Err(create_error!(InvalidOperation))
|
||||
}
|
||||
|
||||
db.deauthorize_authorized_bot(&id)
|
||||
.await
|
||||
.map(|bot| Json(bot.into()))
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
use chrono::Utc;
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use revolt_config::config;
|
||||
use revolt_database::{
|
||||
util::{
|
||||
oauth2,
|
||||
reference::Reference,
|
||||
},
|
||||
Database,
|
||||
}, AuthorizedBot, AuthorizedBotId, Database
|
||||
};
|
||||
use revolt_models::v0;
|
||||
use revolt_result::{create_error, Result};
|
||||
@@ -64,14 +64,21 @@ pub async fn token(
|
||||
let token = oauth2::encode_token(
|
||||
&config.api.security.token_secret,
|
||||
oauth2::TokenType::Access,
|
||||
claims.sub,
|
||||
claims.client_id,
|
||||
claims.sub.clone(),
|
||||
claims.client_id.clone(),
|
||||
claims.redirect_uri,
|
||||
claims.scope.clone(),
|
||||
None,
|
||||
)
|
||||
.map_err(|_| create_error!(InternalError))?;
|
||||
|
||||
db.insert_authorized_bot(&AuthorizedBot {
|
||||
id: AuthorizedBotId { bot: claims.client_id.clone(), user: claims.sub.clone() },
|
||||
created_at: Timestamp::now_utc(),
|
||||
deauthorized_at: None,
|
||||
scope: claims.scope.clone()
|
||||
}).await?;
|
||||
|
||||
Ok(Json(v0::OAuth2TokenExchangeResponse {
|
||||
access_token: token,
|
||||
token_type: "OAuth2".to_string(),
|
||||
|
||||
@@ -12,6 +12,7 @@ mod fetch_user;
|
||||
mod fetch_user_flags;
|
||||
mod find_mutual;
|
||||
mod get_default_avatar;
|
||||
mod oauth2_connections;
|
||||
mod open_dm;
|
||||
mod remove_friend;
|
||||
mod send_friend_request;
|
||||
|
||||
0
crates/delta/src/routes/users/oauth2_connections.rs
Normal file
0
crates/delta/src/routes/users/oauth2_connections.rs
Normal file
Reference in New Issue
Block a user