From 89f2c7003bcaedb98ad05b14e93e7c55c5acf808 Mon Sep 17 00:00:00 2001 From: Zack Fu Zi Xiang Date: Mon, 2 Sep 2024 11:14:04 +0800 Subject: [PATCH] feat: support user deletion --- dev.env | 5 +++ libs/app-error/src/lib.rs | 1 + src/api/user.rs | 65 +++++++++++++++++++++++++++++++++++++++ src/config/config.rs | 11 +++++++ 4 files changed, 82 insertions(+) diff --git a/dev.env b/dev.env index ee368318..769e8cdc 100644 --- a/dev.env +++ b/dev.env @@ -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 diff --git a/libs/app-error/src/lib.rs b/libs/app-error/src/lib.rs index e2a47176..17e1ce25 100644 --- a/libs/app-error/src/lib.rs +++ b/libs/app-error/src/lib.rs @@ -317,6 +317,7 @@ pub enum ErrorCode { SqlxArgEncodingError = 1035, InvalidContentType = 1036, SingleUploadLimitExceeded = 1037, + AppleRevokeTokenError = 1038, } impl ErrorCode { diff --git a/src/api/user.rs b/src/api/user.rs index af0ff84c..025bfdee 100644 --- a/src/api/user.rs +++ b/src/api/user.rs @@ -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, ) -> Result> { 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 + ), + )) +} diff --git a/src/config/config.rs b/src/config/config.rs index 349c8ec1..274ba86c 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -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, } +#[derive(serde::Deserialize, Clone, Debug)] +pub struct AppleOAuthSetting { + pub client_id: String, + pub client_secret: Secret, +} + #[derive(serde::Deserialize, Clone, Debug)] pub struct CasbinSetting { pub pool_size: u32, @@ -204,6 +211,10 @@ pub fn get_configuration() -> Result { 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) }