mobile update
This commit is contained in:
@@ -14,7 +14,7 @@ use utoipa::ToSchema;
|
||||
use crate::auth::AuthBackend;
|
||||
use crate::models::User;
|
||||
use crate::services::OAuthService;
|
||||
use crate::MobileTokenStore;
|
||||
use crate::{MobileStoreEntry, MobileTokenStore};
|
||||
|
||||
const CSRF_TOKEN_KEY: &str = "oauth_csrf_token";
|
||||
const FRONTEND_URL_KEY: &str = "oauth_frontend_url";
|
||||
@@ -49,28 +49,30 @@ pub struct OAuthUrlResponse {
|
||||
)]
|
||||
pub async fn google_auth(
|
||||
session: Session,
|
||||
Extension(token_store): Extension<MobileTokenStore>,
|
||||
Query(query): Query<GoogleAuthQuery>,
|
||||
) -> Result<Json<OAuthUrlResponse>, StatusCode> {
|
||||
let oauth_service = OAuthService::new();
|
||||
let (auth_url, csrf_token) = oauth_service.get_auth_url();
|
||||
|
||||
session
|
||||
.insert(CSRF_TOKEN_KEY, csrf_token.secret().clone())
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
if let Some(redirect_url) = query.redirect_url {
|
||||
session
|
||||
.insert(FRONTEND_URL_KEY, redirect_url)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
}
|
||||
|
||||
if query.mobile.unwrap_or(false) {
|
||||
let mut store = token_store.lock().unwrap();
|
||||
store.insert(
|
||||
format!("csrf:{}", csrf_token.secret()),
|
||||
MobileStoreEntry::Csrf { created_at: std::time::Instant::now() },
|
||||
);
|
||||
} else {
|
||||
session
|
||||
.insert("oauth_mobile", true)
|
||||
.insert(CSRF_TOKEN_KEY, csrf_token.secret().clone())
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
if let Some(redirect_url) = query.redirect_url {
|
||||
session
|
||||
.insert(FRONTEND_URL_KEY, redirect_url)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Json(OAuthUrlResponse { url: auth_url }))
|
||||
@@ -92,29 +94,38 @@ pub async fn google_callback(
|
||||
Extension(token_store): Extension<MobileTokenStore>,
|
||||
Query(query): Query<GoogleCallbackQuery>,
|
||||
) -> Result<Response, StatusCode> {
|
||||
let stored_csrf: Option<String> = session
|
||||
let session_csrf: Option<String> = session
|
||||
.get(CSRF_TOKEN_KEY)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
.unwrap_or(None);
|
||||
|
||||
let is_mobile;
|
||||
let csrf_valid;
|
||||
|
||||
if let Some(csrf) = session_csrf {
|
||||
is_mobile = false;
|
||||
csrf_valid = csrf == query.state;
|
||||
session.remove::<String>(CSRF_TOKEN_KEY).await.ok();
|
||||
} else {
|
||||
let key = format!("csrf:{}", &query.state);
|
||||
let mut store = token_store.lock().unwrap();
|
||||
csrf_valid = matches!(
|
||||
store.get(&key),
|
||||
Some(MobileStoreEntry::Csrf { created_at }) if created_at.elapsed().as_secs() < 300
|
||||
);
|
||||
store.remove(&key);
|
||||
is_mobile = csrf_valid;
|
||||
}
|
||||
|
||||
if !csrf_valid {
|
||||
return Err(StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
let frontend_url: Option<String> = session
|
||||
.get(FRONTEND_URL_KEY)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
let is_mobile: bool = session
|
||||
.get("oauth_mobile")
|
||||
.await
|
||||
.unwrap_or(None)
|
||||
.unwrap_or(false);
|
||||
|
||||
session.remove::<String>(CSRF_TOKEN_KEY).await.ok();
|
||||
.unwrap_or(None);
|
||||
session.remove::<String>(FRONTEND_URL_KEY).await.ok();
|
||||
session.remove::<bool>("oauth_mobile").await.ok();
|
||||
|
||||
if stored_csrf.as_deref() != Some(&query.state) {
|
||||
return Err(StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
let oauth_service = OAuthService::new();
|
||||
|
||||
@@ -133,6 +144,23 @@ pub async fn google_callback(
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
if is_mobile {
|
||||
let token = uuid::Uuid::new_v4().to_string();
|
||||
{
|
||||
let mut store = token_store.lock().unwrap();
|
||||
store.insert(
|
||||
token.clone(),
|
||||
MobileStoreEntry::AuthToken { user_id: user.id, created_at: std::time::Instant::now() },
|
||||
);
|
||||
}
|
||||
let deep_link = format!("com.arrelin.family-budget-android://auth?token={}", token);
|
||||
let html = format!(
|
||||
r#"<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0;url={0}"></head><body><script>window.location="{0}"</script></body></html>"#,
|
||||
deep_link
|
||||
);
|
||||
return Ok(Html(html).into_response());
|
||||
}
|
||||
|
||||
auth_session
|
||||
.login(&user)
|
||||
.await
|
||||
@@ -146,28 +174,10 @@ pub async fn google_callback(
|
||||
.unwrap_or_default();
|
||||
if !authorized_families.contains(&family_id) {
|
||||
authorized_families.push(family_id);
|
||||
session
|
||||
.insert("authorized_families", authorized_families)
|
||||
.await
|
||||
.ok();
|
||||
session.insert("authorized_families", authorized_families).await.ok();
|
||||
}
|
||||
}
|
||||
|
||||
if is_mobile {
|
||||
let token = uuid::Uuid::new_v4().to_string();
|
||||
{
|
||||
let mut store = token_store.lock().unwrap();
|
||||
store.insert(token.clone(), (user.id, std::time::Instant::now()));
|
||||
}
|
||||
let deep_link = format!("com.arrelin.family-budget-android://auth?token={}", token);
|
||||
let html = format!(
|
||||
r#"<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0;url={0}"></head>
|
||||
<body><script>window.location="{0}"</script></body></html>"#,
|
||||
deep_link
|
||||
);
|
||||
return Ok(Html(html).into_response());
|
||||
}
|
||||
|
||||
let redirect_url = frontend_url.unwrap_or_else(|| "http://localhost:3000".to_string());
|
||||
Ok(Redirect::temporary(&redirect_url).into_response())
|
||||
}
|
||||
@@ -186,8 +196,10 @@ pub async fn mobile_callback(
|
||||
let user_id = {
|
||||
let mut store = token_store.lock().unwrap();
|
||||
match store.get(&query.token) {
|
||||
Some((uid, created_at)) if created_at.elapsed().as_secs() < 300 => {
|
||||
let uid = *uid;
|
||||
Some(MobileStoreEntry::AuthToken { user_id, created_at })
|
||||
if created_at.elapsed().as_secs() < 300 =>
|
||||
{
|
||||
let uid = *user_id;
|
||||
store.remove(&query.token);
|
||||
uid
|
||||
}
|
||||
@@ -210,4 +222,4 @@ pub async fn mobile_callback(
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
Ok(Json(serde_json::json!({"success": true})))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user