From 4db49d4dc6967e64fe19aeb18704eb8d09d64c11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E8=BF=90=E5=AE=B6?= Date: Wed, 1 May 2024 17:09:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0firebase=20id=20token=20?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 27 +++++++++++++++++++++++++ auth/Cargo.toml | 4 ++++ auth/src/firebase_admin.rs | 38 +++++++++++++++++++++++++++++++++++ domain/src/dto/player_info.rs | 10 +++++---- service/src/player_info.rs | 9 +++++++++ 5 files changed, 84 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 35d373e..341329b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -171,7 +171,11 @@ dependencies = [ name = "auth" version = "0.1.0" dependencies = [ + "error-stack", + "futures-executor", + "reqwest", "rs-firebase-admin-sdk", + "tracing", ] [[package]] @@ -2022,6 +2026,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", + "h2 0.4.2", "http 1.1.0", "http-body 1.0.0", "http-body-util", @@ -2041,6 +2046,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "sync_wrapper", + "system-configuration", "tokio", "tokio-native-tls", "tower-service", @@ -2910,6 +2916,27 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tagptr" version = "0.2.0" diff --git a/auth/Cargo.toml b/auth/Cargo.toml index 7c29029..9c0bcf1 100644 --- a/auth/Cargo.toml +++ b/auth/Cargo.toml @@ -7,3 +7,7 @@ edition = "2021" [dependencies] rs-firebase-admin-sdk = "2.0.0" +reqwest = "0.12.4" +futures-executor = "0.3.30" +error-stack = "0.4.1" +tracing = "0.1.40" diff --git a/auth/src/firebase_admin.rs b/auth/src/firebase_admin.rs index b6ecec4..95d1bec 100644 --- a/auth/src/firebase_admin.rs +++ b/auth/src/firebase_admin.rs @@ -1,8 +1,44 @@ +use std::sync::OnceLock; +use error_stack::Report; +use futures_executor::block_on; use rs_firebase_admin_sdk::{ auth::{FirebaseAuthService, UserIdentifiers}, client::ApiHttpClient, App, AuthenticationManager, }; +use rs_firebase_admin_sdk::auth::token::{LiveTokenVerifier, TokenVerifier}; +use rs_firebase_admin_sdk::auth::token::cache::{HttpCache, PubKeys}; +use rs_firebase_admin_sdk::auth::token::error::TokenVerificationError; +use rs_firebase_admin_sdk::auth::token::jwt::JWToken; + +static LIVE_APP: OnceLock> = OnceLock::new(); + +static ID_TOKEN_VERIFIER: OnceLock>> = OnceLock::new(); + +fn get_app() -> &'static App { + LIVE_APP.get_or_init(|| block_on(async { + let gcp_service_account = AuthenticationManager::new().await.expect("Error while creating authentication manager"); + App::live(gcp_service_account.into()).await.expect("Error while creating live app") + })) +} + +fn get_id_token_verifier() -> &'static LiveTokenVerifier> { + ID_TOKEN_VERIFIER.get_or_init(|| block_on(async { + get_app().id_token_verifier().await.expect("Error while creating id token verifier") + })) +} + +pub async fn verify_user_id_token(id_token: &str) -> Result { + match get_id_token_verifier().verify_token(id_token).await { + Ok(jwtToken) => { + Ok(jwtToken) + } + Err(error) => { + tracing::error!("Error while verifying token: {:?}", error); + Err(error.to_string()) + } + } +} async fn test() { // Load your GCP SA from env, see https://crates.io/crates/gcp_auth for more details @@ -10,6 +46,8 @@ async fn test() { // Create live (not emulated) context for Firebase app let live_app = App::live(gcp_service_account.into()).await.unwrap(); + let id_token_verifier = live_app.id_token_verifier().await.expect("Error while creating id token verifier"); + let verify_result = id_token_verifier.verify_token("").await.expect("Error while verifying token"); // Create Firebase authentication admin client let auth_admin = live_app.auth(); diff --git a/domain/src/dto/player_info.rs b/domain/src/dto/player_info.rs index 8c0c4a5..15dfc2e 100644 --- a/domain/src/dto/player_info.rs +++ b/domain/src/dto/player_info.rs @@ -5,12 +5,14 @@ use validator::Validate; pub struct PlayerInfoRegister { #[validate(required(message = "用户名称不能为空"), length(min = 1, message = "用户名称不能为空"))] pub username: Option, - #[validate(required, length(min = 1, message = "电子邮箱不能为空"))] + #[validate(required(message = "电子邮箱不能为空"), length(min = 1, message = "电子邮箱不能为空"))] pub email: Option, - #[validate(required, length(min = 1, message = "平台ID不能为空"))] + #[validate(required(message = "平台ID不能为空"), length(min = 1, message = "平台ID不能为空"))] pub platform_id: Option, - #[validate(required, length(min = 1, message = "用户类型不能为空"))] + #[validate(required(message = "用户类型不能为空"), length(min = 1, message = "用户类型不能为空"))] pub user_type: Option, - #[validate(required, length(min = 1, message = "用户所属区域不能为空"))] + #[validate(required(message = "用户所属区域不能为空"), length(min = 1, message = "用户所属区域不能为空"))] pub country_code: Option, + #[validate(required(message = "用户ID Token不能为空"), length(min = 1, message = "用户ID Token不能为空"))] + pub idToken: Option } diff --git a/service/src/player_info.rs b/service/src/player_info.rs index bd08225..8fca2de 100644 --- a/service/src/player_info.rs +++ b/service/src/player_info.rs @@ -5,6 +5,7 @@ use library::db; use library::resp::response::ResErr::{ErrPerm, ErrSystem}; use library::resp::response::{ResOK, ResResult}; use sea_orm::{ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, Set}; +use auth::firebase_admin; pub async fn register(req: PlayerInfoRegister) -> ResResult> { match PlayerInfo::find() @@ -23,6 +24,14 @@ pub async fn register(req: PlayerInfoRegister) -> ResResult> { } } + let verified_token = match firebase_admin::verify_user_id_token(req.idToken.unwrap().as_str()).await { + Ok(token) => token, + Err(err) => { + tracing::error!(error = ?err, "err verify user id token"); + return Err(ErrSystem(None)); + } + }; + let now = chrono::Local::now().naive_local(); let model = player_info::ActiveModel {