chore: fix refresh token (#292)
* chore: fix refresh token * chore: fix wasm build * chore: fix test
This commit is contained in:
parent
44d6312d42
commit
d23ad1c4de
|
|
@ -46,7 +46,10 @@ use url::Url;
|
||||||
use gotrue_entity::dto::SignUpResponse::{Authenticated, NotAuthenticated};
|
use gotrue_entity::dto::SignUpResponse::{Authenticated, NotAuthenticated};
|
||||||
use gotrue_entity::dto::{GotrueTokenResponse, UpdateGotrueUserParams, User};
|
use gotrue_entity::dto::{GotrueTokenResponse, UpdateGotrueUserParams, User};
|
||||||
|
|
||||||
pub const CLIENT_API_VERSION: &str = "0.0.3";
|
/// The API version of the client.
|
||||||
|
/// 0.0.4
|
||||||
|
/// fix refresh token issue
|
||||||
|
pub const CLIENT_API_VERSION: &str = "0.0.4";
|
||||||
pub const X_COMPRESSION_TYPE: &str = "X-Compression-Type";
|
pub const X_COMPRESSION_TYPE: &str = "X-Compression-Type";
|
||||||
pub const X_COMPRESSION_BUFFER_SIZE: &str = "X-Compression-Buffer-Size";
|
pub const X_COMPRESSION_BUFFER_SIZE: &str = "X-Compression-Buffer-Size";
|
||||||
pub const X_COMPRESSION_TYPE_BROTLI: &str = "brotli";
|
pub const X_COMPRESSION_TYPE_BROTLI: &str = "brotli";
|
||||||
|
|
@ -112,7 +115,6 @@ pub struct Client {
|
||||||
pub(crate) config: ClientConfiguration,
|
pub(crate) config: ClientConfiguration,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) type RefreshTokenRet = tokio::sync::oneshot::Receiver<Result<(), AppResponseError>>;
|
|
||||||
pub(crate) type RefreshTokenSender = tokio::sync::oneshot::Sender<Result<(), AppResponseError>>;
|
pub(crate) type RefreshTokenSender = tokio::sync::oneshot::Sender<Result<(), AppResponseError>>;
|
||||||
|
|
||||||
/// Hardcoded schema in the frontend application. Do not change this value.
|
/// Hardcoded schema in the frontend application. Do not change this value.
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use crate::http::log_request_id;
|
||||||
use crate::ws::{WSClientHttpSender, WSError};
|
use crate::ws::{WSClientHttpSender, WSError};
|
||||||
use crate::{spawn_blocking_brotli_compress, Client};
|
use crate::{spawn_blocking_brotli_compress, Client};
|
||||||
use crate::{RefreshTokenAction, RefreshTokenRetryCondition};
|
use crate::{RefreshTokenAction, RefreshTokenRetryCondition};
|
||||||
|
use anyhow::anyhow;
|
||||||
use app_error::AppError;
|
use app_error::AppError;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use database_entity::dto::CollabParams;
|
use database_entity::dto::CollabParams;
|
||||||
|
|
@ -105,20 +106,26 @@ impl Client {
|
||||||
/// using the stored refresh token. If successful, it updates the stored access token with the new one
|
/// using the stored refresh token. If successful, it updates the stored access token with the new one
|
||||||
/// received from the server.
|
/// received from the server.
|
||||||
#[instrument(level = "debug", skip_all, err)]
|
#[instrument(level = "debug", skip_all, err)]
|
||||||
pub async fn refresh_token(&self) -> Result<crate::http::RefreshTokenRet, AppResponseError> {
|
pub async fn refresh_token(&self) -> Result<(), AppResponseError> {
|
||||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||||
self.refresh_ret_txs.write().push(tx);
|
self.refresh_ret_txs.write().push(tx);
|
||||||
|
|
||||||
if !self.is_refreshing_token.load(Ordering::SeqCst) {
|
if !self.is_refreshing_token.load(Ordering::SeqCst) {
|
||||||
self.is_refreshing_token.store(true, Ordering::SeqCst);
|
self.is_refreshing_token.store(true, Ordering::SeqCst);
|
||||||
let txs = std::mem::take(&mut *self.refresh_ret_txs.write());
|
|
||||||
let result = self.inner_refresh_token().await;
|
let result = self.inner_refresh_token().await;
|
||||||
|
let txs = std::mem::take(&mut *self.refresh_ret_txs.write());
|
||||||
for tx in txs {
|
for tx in txs {
|
||||||
let _ = tx.send(result.clone());
|
let _ = tx.send(result.clone());
|
||||||
}
|
}
|
||||||
self.is_refreshing_token.store(false, Ordering::SeqCst);
|
self.is_refreshing_token.store(false, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
Ok(rx)
|
|
||||||
|
// Wait for the result of the refresh token request.
|
||||||
|
match tokio::time::timeout(Duration::from_secs(60), rx).await {
|
||||||
|
Ok(Ok(result)) => result,
|
||||||
|
Ok(Err(err)) => Err(AppError::Internal(anyhow!("refresh token error: {}", err)).into()),
|
||||||
|
Err(_) => Err(AppError::RequestTimeout("refresh token timeout".to_string()).into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn inner_refresh_token(&self) -> Result<(), AppResponseError> {
|
async fn inner_refresh_token(&self) -> Result<(), AppResponseError> {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::http::RefreshTokenRet;
|
|
||||||
use crate::ws::{WSClientHttpSender, WSError};
|
use crate::ws::{WSClientHttpSender, WSError};
|
||||||
use crate::Client;
|
use crate::Client;
|
||||||
use app_error::gotrue::GoTrueError;
|
use app_error::gotrue::GoTrueError;
|
||||||
|
|
@ -25,7 +24,7 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip_all, err)]
|
#[instrument(level = "debug", skip_all, err)]
|
||||||
pub async fn refresh_token(&self) -> Result<RefreshTokenRet, AppResponseError> {
|
pub async fn refresh_token(&self) -> Result<(), AppResponseError> {
|
||||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||||
self.refresh_ret_txs.write().push(tx);
|
self.refresh_ret_txs.write().push(tx);
|
||||||
|
|
||||||
|
|
@ -38,7 +37,10 @@ impl Client {
|
||||||
}
|
}
|
||||||
self.is_refreshing_token.store(false, Ordering::SeqCst);
|
self.is_refreshing_token.store(false, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
Ok(rx)
|
|
||||||
|
rx.await
|
||||||
|
.map_err(|err| AppResponseError::new(ErrorCode::Internal, err.to_string()))??;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn inner_refresh_token(&self) -> Result<(), AppResponseError> {
|
async fn inner_refresh_token(&self) -> Result<(), AppResponseError> {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use app_error::AppError;
|
use app_error::AppError;
|
||||||
use client_api_test_util::generate_unique_registered_user_client;
|
use client_api_test_util::generate_unique_registered_user_client;
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::SystemTime;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn refresh_success() {
|
async fn refresh_success() {
|
||||||
|
|
@ -20,7 +20,7 @@ async fn concurrent_refresh() {
|
||||||
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
||||||
|
|
||||||
let mut join_handles = vec![];
|
let mut join_handles = vec![];
|
||||||
for _ in 0..100 {
|
for _ in 0..20 {
|
||||||
let cloned_client = c.clone();
|
let cloned_client = c.clone();
|
||||||
let handle = tokio::spawn(async move {
|
let handle = tokio::spawn(async move {
|
||||||
cloned_client.refresh_token().await.unwrap();
|
cloned_client.refresh_token().await.unwrap();
|
||||||
|
|
@ -29,12 +29,11 @@ async fn concurrent_refresh() {
|
||||||
join_handles.push(handle);
|
join_handles.push(handle);
|
||||||
}
|
}
|
||||||
let results = join_all(join_handles).await;
|
let results = join_all(join_handles).await;
|
||||||
assert_eq!(results.len(), 100);
|
assert_eq!(results.len(), 20);
|
||||||
for result in results {
|
for result in results {
|
||||||
result.unwrap().unwrap();
|
result.unwrap().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
|
||||||
let new_token = c.access_token().unwrap();
|
let new_token = c.access_token().unwrap();
|
||||||
assert_ne!(old_token, new_token);
|
assert_ne!(old_token, new_token);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue