feat: support user deletion

This commit is contained in:
Zack Fu Zi Xiang 2024-09-02 11:14:04 +08:00
parent a2af9c3b63
commit 89f2c7003b
No known key found for this signature in database
4 changed files with 82 additions and 0 deletions

View File

@ -68,6 +68,11 @@ GOTRUE_EXTERNAL_DISCORD_ENABLED=false
GOTRUE_EXTERNAL_DISCORD_CLIENT_ID=
GOTRUE_EXTERNAL_DISCORD_SECRET=
GOTRUE_EXTERNAL_DISCORD_REDIRECT_URI=http://localhost:9999/callback
# Apple OAuth2
GOTRUE_EXTERNAL_APPLE_ENABLED=false
GOTRUE_EXTERNAL_APPLE_CLIENT_ID=
GOTRUE_EXTERNAL_APPLE_SECRET=
GOTRUE_EXTERNAL_APPLE_REDIRECT_URI=http://localhost:9999/callback
# File Storage
APPFLOWY_S3_USE_MINIO=true

View File

@ -317,6 +317,7 @@ pub enum ErrorCode {
SqlxArgEncodingError = 1035,
InvalidContentType = 1036,
SingleUploadLimitExceeded = 1037,
AppleRevokeTokenError = 1038,
}
impl ErrorCode {

View File

@ -4,8 +4,10 @@ use crate::state::AppState;
use actix_web::web::{Data, Json};
use actix_web::Result;
use actix_web::{web, Scope};
use app_error::ErrorCode;
use authentication::jwt::{Authorization, UserUuid};
use database_entity::dto::{AFUserProfile, AFUserWorkspaceInfo};
use secrecy::ExposeSecret;
use shared_entity::dto::auth_dto::{SignInTokenResponse, UpdateUserParams};
use shared_entity::response::AppResponseError;
use shared_entity::response::{AppResponse, JsonAppResponse};
@ -69,5 +71,68 @@ async fn delete_user_handler(
state: Data<AppState>,
) -> Result<JsonAppResponse<()>> {
delete_user(&state.pg_pool, auth.uuid()?).await?;
if is_apple_user(auth) {
if let Err(err) = revoke_apple_token(
&state.config.apple_oauth.client_id,
state.config.apple_oauth.client_secret.expose_secret(),
"TODO: get original apple during oauth",
)
.await
{
tracing::warn!("revoke apple token failed: {:?}", err);
};
}
Ok(AppResponse::Ok().into())
}
fn is_apple_user(auth: Authorization) -> bool {
if let Some(provider) = auth.claims.app_metadata.get("provider") {
if provider == "apple" {
return true;
}
};
if let Some(providers) = auth.claims.app_metadata.get("providers") {
if let Some(providers) = providers.as_array() {
for provider in providers {
if provider == "apple" {
return true;
}
}
}
}
false
}
/// Based on: https://developer.apple.com/documentation/sign_in_with_apple/revoke_tokens
async fn revoke_apple_token(
apple_client_id: &str,
apple_client_secret: &str,
apple_user_token: &str,
) -> Result<(), AppResponseError> {
let resp = reqwest::Client::new()
.post("https://appleid.apple.com/auth/revoke")
.form(&[
("client_id", apple_client_id),
("client_secret", apple_client_secret),
("token", apple_user_token),
])
.send()
.await?;
let status = resp.status();
if status.is_success() {
return Ok(());
}
let payload = resp.text().await?;
Err(AppResponseError::new(
ErrorCode::AppleRevokeTokenError,
format!(
"calling apple revoke, code: {}, message: {}",
status, payload
),
))
}

View File

@ -22,6 +22,7 @@ pub struct Config {
pub grpc_history: GrpcHistorySetting,
pub collab: CollabSetting,
pub mailer: MailerSetting,
pub apple_oauth: AppleOAuthSetting,
}
#[derive(serde::Deserialize, Clone, Debug)]
@ -32,6 +33,12 @@ pub struct MailerSetting {
pub smtp_password: Secret<String>,
}
#[derive(serde::Deserialize, Clone, Debug)]
pub struct AppleOAuthSetting {
pub client_id: String,
pub client_secret: Secret<String>,
}
#[derive(serde::Deserialize, Clone, Debug)]
pub struct CasbinSetting {
pub pool_size: u32,
@ -204,6 +211,10 @@ pub fn get_configuration() -> Result<Config, anyhow::Error> {
smtp_username: get_env_var("APPFLOWY_MAILER_SMTP_USERNAME", "sender@example.com"),
smtp_password: get_env_var("APPFLOWY_MAILER_SMTP_PASSWORD", "password").into(),
},
apple_oauth: AppleOAuthSetting {
client_id: get_env_var("APPFLOWY_APPLE_OAUTH_CLIENT_ID", ""),
client_secret: get_env_var("APPFLOWY_APPLE_OAUTH_CLIENT_SECRET", "").into(),
},
};
Ok(config)
}