feat: initial oauth2 token revoking
This commit is contained in:
5
crates/core/database/src/models/authorized_bots/mod.rs
Normal file
5
crates/core/database/src/models/authorized_bots/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
mod model;
|
||||||
|
mod ops;
|
||||||
|
|
||||||
|
pub use model::*;
|
||||||
|
pub use ops::*;
|
||||||
27
crates/core/database/src/models/authorized_bots/model.rs
Normal file
27
crates/core/database/src/models/authorized_bots/model.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use iso8601_timestamp::Timestamp;
|
||||||
|
|
||||||
|
auto_derived! (
|
||||||
|
/// Unique id of the user and bot
|
||||||
|
pub struct AuthorizedBotId {
|
||||||
|
/// User id
|
||||||
|
pub user: String,
|
||||||
|
|
||||||
|
/// Bot Id
|
||||||
|
pub bot: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AuthorizedBot {
|
||||||
|
/// Unique Id
|
||||||
|
#[serde(rename = "_id")]
|
||||||
|
pub id: AuthorizedBotId,
|
||||||
|
|
||||||
|
/// When the authorized oauth2 bot connection was created at
|
||||||
|
pub created_at: Timestamp,
|
||||||
|
|
||||||
|
/// If and when the authorized oauth2 bot connection was revoked at
|
||||||
|
pub deauthorized_at: Option<Timestamp>,
|
||||||
|
|
||||||
|
/// Scopes the bot has access to
|
||||||
|
pub scope: String
|
||||||
|
}
|
||||||
|
);
|
||||||
27
crates/core/database/src/models/authorized_bots/ops.rs
Normal file
27
crates/core/database/src/models/authorized_bots/ops.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use revolt_result::Result;
|
||||||
|
|
||||||
|
use crate::{AuthorizedBot, AuthorizedBotId};
|
||||||
|
|
||||||
|
mod mongodb;
|
||||||
|
mod reference;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AbstractAuthorizedBots: Sync + Send {
|
||||||
|
/// Insert emoji into database.
|
||||||
|
async fn insert_authorized_bot(&self, authorised_bot: &AuthorizedBot) -> Result<()>;
|
||||||
|
|
||||||
|
/// Fetch an emoji by its id
|
||||||
|
async fn fetch_authorized_bot(&self, id: &AuthorizedBotId) -> Result<AuthorizedBot>;
|
||||||
|
|
||||||
|
/// Fetch all authorized bots for a user
|
||||||
|
async fn fetch_users_authorized_bots(&self, user_id: &str) -> Result<Vec<AuthorizedBot>>;
|
||||||
|
|
||||||
|
/// Deletes an authorized bot
|
||||||
|
async fn delete_authorized_bot(&self, id: &AuthorizedBotId) -> Result<()>;
|
||||||
|
|
||||||
|
/// Deauthorizes a bot access to a user's information
|
||||||
|
async fn deauthorize_authorized_bot(&self, id: &AuthorizedBotId) -> Result<AuthorizedBot>;
|
||||||
|
|
||||||
|
/// Fetches all deauthorized bots
|
||||||
|
async fn fetch_deauthorized_authorized_bots(&self) -> Result<Vec<AuthorizedBot>>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
use bson::{Document, to_bson};
|
||||||
|
use revolt_result::Result;
|
||||||
|
use iso8601_timestamp::Timestamp;
|
||||||
|
|
||||||
|
use crate::{MongoDb, AuthorizedBot, AuthorizedBotId};
|
||||||
|
|
||||||
|
use super::AbstractAuthorizedBots;
|
||||||
|
|
||||||
|
static COL: &str = "authorized_bots";
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractAuthorizedBots for MongoDb {
|
||||||
|
/// Insert emoji into database.
|
||||||
|
async fn insert_authorized_bot(&self, authorised_bot: &AuthorizedBot) -> Result<()> {
|
||||||
|
query!(self, insert_one, COL, &authorised_bot).map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch an emoji by its id
|
||||||
|
async fn fetch_authorized_bot(&self, id: &AuthorizedBotId) -> Result<AuthorizedBot> {
|
||||||
|
query!(self, find_one, COL, doc! { "id.user": &id.user, "id.bot": &id.bot })?.ok_or_else(|| create_error!(NotFound))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch an emoji by its id
|
||||||
|
async fn fetch_users_authorized_bots(&self, user_id: &str) -> Result<Vec<AuthorizedBot>> {
|
||||||
|
query!(self, find, COL, doc! { "id.user": &user_id })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes an authori
|
||||||
|
async fn delete_authorized_bot(&self, id: &AuthorizedBotId) -> Result<()> {
|
||||||
|
query!(self, delete_one, COL, doc! { "id.user": &id.user, "id.bot": &id.bot }).map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn deauthorize_authorized_bot(&self, id: &AuthorizedBotId) -> Result<AuthorizedBot> {
|
||||||
|
self.col::<AuthorizedBot>(COL)
|
||||||
|
.find_one_and_update(
|
||||||
|
doc! {
|
||||||
|
"id.user": &id.user,
|
||||||
|
"id.bot": &id.bot
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$set": {
|
||||||
|
"unauthorized_at": to_bson(&Timestamp::now_utc()).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| create_database_error!("find_one_and_update", COL))
|
||||||
|
.and_then(|opt| opt.ok_or_else(|| create_database_error!("find_one_and_update", COL)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_deauthorized_authorized_bots(&self) -> Result<Vec<AuthorizedBot>> {
|
||||||
|
query!(
|
||||||
|
self,
|
||||||
|
find,
|
||||||
|
COL,
|
||||||
|
doc! {
|
||||||
|
"deauthorized_at": { "$exists": true }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
use bson::Document;
|
||||||
|
use revolt_result::Result;
|
||||||
|
|
||||||
|
use crate::{ReferenceDb, AuthorizedBot, AuthorizedBotId};
|
||||||
|
|
||||||
|
use super::AbstractAuthorizedBots;
|
||||||
|
|
||||||
|
static COL: &str = "authorized_bots";
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractAuthorizedBots for ReferenceDb {
|
||||||
|
/// Insert emoji into database.
|
||||||
|
async fn insert_authorized_bot(&self, authorised_bot: &AuthorizedBot) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch an emoji by its id
|
||||||
|
async fn fetch_authorized_bot(&self, id: &AuthorizedBotId) -> Result<AuthorizedBot> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_users_authorized_bots(&self, user_id: &str) -> Result<Vec<AuthorizedBot>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes an authori
|
||||||
|
async fn delete_authorized_bot(&self, id: &AuthorizedBotId) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn deauthorize_authorized_bot(&self, id: &AuthorizedBotId) -> Result<AuthorizedBot> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_deauthorized_authorized_bots(&self) -> Result<Vec<AuthorizedBot>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
mod admin_migrations;
|
mod admin_migrations;
|
||||||
|
mod authorized_bots;
|
||||||
mod bots;
|
mod bots;
|
||||||
mod channel_invites;
|
mod channel_invites;
|
||||||
mod channel_unreads;
|
mod channel_unreads;
|
||||||
@@ -19,6 +20,7 @@ mod user_settings;
|
|||||||
mod users;
|
mod users;
|
||||||
|
|
||||||
pub use admin_migrations::*;
|
pub use admin_migrations::*;
|
||||||
|
pub use authorized_bots::*;
|
||||||
pub use bots::*;
|
pub use bots::*;
|
||||||
pub use channel_invites::*;
|
pub use channel_invites::*;
|
||||||
pub use channel_unreads::*;
|
pub use channel_unreads::*;
|
||||||
@@ -46,6 +48,7 @@ use crate::MongoDb;
|
|||||||
pub trait AbstractDatabase:
|
pub trait AbstractDatabase:
|
||||||
Sync
|
Sync
|
||||||
+ Send
|
+ Send
|
||||||
|
+ authorized_bots::AbstractAuthorizedBots
|
||||||
+ admin_migrations::AbstractMigrations
|
+ admin_migrations::AbstractMigrations
|
||||||
+ bots::AbstractBots
|
+ bots::AbstractBots
|
||||||
+ channels::AbstractChannels
|
+ channels::AbstractChannels
|
||||||
|
|||||||
@@ -1454,3 +1454,43 @@ impl From<FieldsMessage> for crate::FieldsMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<AuthorizedBotId> for crate::AuthorizedBotId {
|
||||||
|
fn from(value: AuthorizedBotId) -> Self {
|
||||||
|
crate::AuthorizedBotId {
|
||||||
|
user: value.user,
|
||||||
|
bot: value.bot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::AuthorizedBotId> for AuthorizedBotId {
|
||||||
|
fn from(value: crate::AuthorizedBotId) -> Self {
|
||||||
|
AuthorizedBotId {
|
||||||
|
user: value.user,
|
||||||
|
bot: value.bot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AuthorizedBot> for crate::AuthorizedBot {
|
||||||
|
fn from(value: AuthorizedBot) -> Self {
|
||||||
|
crate::AuthorizedBot {
|
||||||
|
id: value.id.into(),
|
||||||
|
created_at: value.created_at,
|
||||||
|
deauthorized_at: value.deauthorized_at,
|
||||||
|
scope: value.scope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::AuthorizedBot> for AuthorizedBot {
|
||||||
|
fn from(value: crate::AuthorizedBot) -> Self {
|
||||||
|
AuthorizedBot {
|
||||||
|
id: value.id.into(),
|
||||||
|
created_at: value.created_at,
|
||||||
|
deauthorized_at: value.deauthorized_at,
|
||||||
|
scope: value.scope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
crates/core/models/src/v0/authorized_bots.rs
Normal file
27
crates/core/models/src/v0/authorized_bots.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use iso8601_timestamp::Timestamp;
|
||||||
|
|
||||||
|
auto_derived!(
|
||||||
|
/// Unique id of the user and bot
|
||||||
|
pub struct AuthorizedBotId {
|
||||||
|
/// User id
|
||||||
|
pub user: String,
|
||||||
|
|
||||||
|
/// Bot Id
|
||||||
|
pub bot: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AuthorizedBot {
|
||||||
|
/// Unique Id
|
||||||
|
#[serde(rename = "_id")]
|
||||||
|
pub id: AuthorizedBotId,
|
||||||
|
|
||||||
|
/// When the authorized oauth2 bot connection was created at
|
||||||
|
pub created_at: Timestamp,
|
||||||
|
|
||||||
|
/// If and when the authorized oauth2 bot connection was revoked at
|
||||||
|
pub deauthorized_at: Option<Timestamp>,
|
||||||
|
|
||||||
|
/// Scopes the bot has access to
|
||||||
|
pub scope: String
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
mod authorized_bots;
|
||||||
mod bots;
|
mod bots;
|
||||||
mod channel_invites;
|
mod channel_invites;
|
||||||
mod channel_unreads;
|
mod channel_unreads;
|
||||||
@@ -16,6 +17,7 @@ mod servers;
|
|||||||
mod user_settings;
|
mod user_settings;
|
||||||
mod users;
|
mod users;
|
||||||
|
|
||||||
|
pub use authorized_bots::*;
|
||||||
pub use bots::*;
|
pub use bots::*;
|
||||||
pub use channel_invites::*;
|
pub use channel_invites::*;
|
||||||
pub use channel_unreads::*;
|
pub use channel_unreads::*;
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ use revolt_result::Result;
|
|||||||
use tasks::{file_deletion, prune_dangling_files};
|
use tasks::{file_deletion, prune_dangling_files};
|
||||||
use tokio::try_join;
|
use tokio::try_join;
|
||||||
|
|
||||||
|
use crate::tasks::prune_authorized_bots;
|
||||||
|
|
||||||
pub mod tasks;
|
pub mod tasks;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@@ -13,7 +15,8 @@ async fn main() -> Result<()> {
|
|||||||
let db = DatabaseInfo::Auto.connect().await.expect("database");
|
let db = DatabaseInfo::Auto.connect().await.expect("database");
|
||||||
try_join!(
|
try_join!(
|
||||||
file_deletion::task(db.clone()),
|
file_deletion::task(db.clone()),
|
||||||
prune_dangling_files::task(db)
|
prune_dangling_files::task(db.clone()),
|
||||||
|
prune_authorized_bots::task(db.clone()),
|
||||||
)
|
)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
pub mod file_deletion;
|
pub mod file_deletion;
|
||||||
pub mod prune_dangling_files;
|
pub mod prune_dangling_files;
|
||||||
|
pub mod prune_authorized_bots;
|
||||||
24
crates/daemons/crond/src/tasks/prune_authorized_bots.rs
Normal file
24
crates/daemons/crond/src/tasks/prune_authorized_bots.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
use revolt_database::{iso8601_timestamp::{Duration, Timestamp}, util::oauth2::TokenType, Database};
|
||||||
|
use revolt_result::Result;
|
||||||
|
use tokio::time::sleep;
|
||||||
|
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
pub async fn task(db: Database) -> Result<()> {
|
||||||
|
let lifetime = Duration::new(TokenType::Access.lifetime().num_seconds(), 0);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let deauthorized_bots = db.fetch_deauthorized_authorized_bots().await?;
|
||||||
|
|
||||||
|
for bot in deauthorized_bots {
|
||||||
|
if let Some(deauthorized_at) = &bot.deauthorized_at {
|
||||||
|
if deauthorized_at.saturating_add(lifetime) < Timestamp::now_utc() {
|
||||||
|
db.delete_authorized_bot(&bot.id).await?;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1 hour
|
||||||
|
sleep(std::time::Duration::from_secs(60 * 60)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
|
use iso8601_timestamp::Timestamp;
|
||||||
use revolt_config::config;
|
use revolt_config::config;
|
||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
util::{oauth2, reference::Reference}, Database, User
|
util::{oauth2, reference::Reference}, AuthorizedBot, AuthorizedBotId, Database, User
|
||||||
};
|
};
|
||||||
use revolt_models::v0;
|
use revolt_models::v0;
|
||||||
use revolt_result::{create_error, Result};
|
use revolt_result::{create_error, Result};
|
||||||
@@ -49,7 +50,7 @@ pub async fn auth(
|
|||||||
return Err(create_error!(InvalidOperation));
|
return Err(create_error!(InvalidOperation));
|
||||||
};
|
};
|
||||||
|
|
||||||
oauth2::encode_token(
|
let token = oauth2::encode_token(
|
||||||
&config.api.security.token_secret,
|
&config.api.security.token_secret,
|
||||||
oauth2::TokenType::Auth,
|
oauth2::TokenType::Auth,
|
||||||
user.id.clone(),
|
user.id.clone(),
|
||||||
@@ -58,7 +59,16 @@ pub async fn auth(
|
|||||||
info.scope.clone(),
|
info.scope.clone(),
|
||||||
info.code_challenge_method,
|
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
|
// authorization code
|
||||||
v0::OAuth2ResponseType::Token => {
|
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_auth;
|
||||||
mod authorize_info;
|
mod authorize_info;
|
||||||
|
mod authorized_bots;
|
||||||
|
mod revoke;
|
||||||
mod token;
|
mod token;
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
@@ -15,6 +17,8 @@ pub fn routes() -> (Vec<Route>, OpenApi) {
|
|||||||
openapi_get_routes_spec![
|
openapi_get_routes_spec![
|
||||||
authorize_auth::auth,
|
authorize_auth::auth,
|
||||||
authorize_info::info,
|
authorize_info::info,
|
||||||
|
authorized_bots::authorized_bots,
|
||||||
|
revoke::revoke,
|
||||||
token::token,
|
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 chrono::Utc;
|
||||||
|
use iso8601_timestamp::Timestamp;
|
||||||
use revolt_config::config;
|
use revolt_config::config;
|
||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
util::{
|
util::{
|
||||||
oauth2,
|
oauth2,
|
||||||
reference::Reference,
|
reference::Reference,
|
||||||
},
|
}, AuthorizedBot, AuthorizedBotId, Database
|
||||||
Database,
|
|
||||||
};
|
};
|
||||||
use revolt_models::v0;
|
use revolt_models::v0;
|
||||||
use revolt_result::{create_error, Result};
|
use revolt_result::{create_error, Result};
|
||||||
@@ -64,14 +64,21 @@ pub async fn token(
|
|||||||
let token = oauth2::encode_token(
|
let token = oauth2::encode_token(
|
||||||
&config.api.security.token_secret,
|
&config.api.security.token_secret,
|
||||||
oauth2::TokenType::Access,
|
oauth2::TokenType::Access,
|
||||||
claims.sub,
|
claims.sub.clone(),
|
||||||
claims.client_id,
|
claims.client_id.clone(),
|
||||||
claims.redirect_uri,
|
claims.redirect_uri,
|
||||||
claims.scope.clone(),
|
claims.scope.clone(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.map_err(|_| create_error!(InternalError))?;
|
.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 {
|
Ok(Json(v0::OAuth2TokenExchangeResponse {
|
||||||
access_token: token,
|
access_token: token,
|
||||||
token_type: "OAuth2".to_string(),
|
token_type: "OAuth2".to_string(),
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ mod fetch_user;
|
|||||||
mod fetch_user_flags;
|
mod fetch_user_flags;
|
||||||
mod find_mutual;
|
mod find_mutual;
|
||||||
mod get_default_avatar;
|
mod get_default_avatar;
|
||||||
|
mod oauth2_connections;
|
||||||
mod open_dm;
|
mod open_dm;
|
||||||
mod remove_friend;
|
mod remove_friend;
|
||||||
mod send_friend_request;
|
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