115 lines
3.5 KiB
Rust
115 lines
3.5 KiB
Rust
use admin_frontend::models::OAuthRedirect;
|
|
use admin_frontend::models::OAuthRedirectToken;
|
|
use base64::engine::Engine;
|
|
use base64::prelude::BASE64_STANDARD_NO_PAD;
|
|
use gotrue_entity::dto::GotrueTokenResponse;
|
|
use reqwest::StatusCode;
|
|
use reqwest::Url;
|
|
use sha2::Digest;
|
|
|
|
use crate::utils::AdminFrontendClient;
|
|
|
|
#[tokio::test]
|
|
async fn oauth_sign_in() {
|
|
let mut af_client = AdminFrontendClient::new();
|
|
af_client
|
|
.web_api_sign_in("admin@example.com", "password")
|
|
.await;
|
|
|
|
let code_challenge_orginal = "hello123";
|
|
let code_challenge_sha256 = {
|
|
let mut hasher = sha2::Sha256::new();
|
|
hasher.update(code_challenge_orginal.as_bytes());
|
|
hasher.finalize().to_vec()
|
|
};
|
|
|
|
// OAuth Param
|
|
let code_challenge = BASE64_STANDARD_NO_PAD.encode(code_challenge_sha256);
|
|
let client_id = "appflowy_cloud";
|
|
let state = "state123";
|
|
|
|
{
|
|
// redirect url not in allowed list
|
|
let resp = af_client
|
|
.web_api_oauth_redirect(&OAuthRedirect {
|
|
client_id: client_id.to_string(),
|
|
state: state.to_string(),
|
|
redirect_uri: "https://mywebsite.com".to_string(),
|
|
response_type: "code".to_string(),
|
|
code_challenge: Some(code_challenge.clone()),
|
|
code_challenge_method: Some("S256".to_string()),
|
|
})
|
|
.await;
|
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
|
assert_eq!(
|
|
resp.text().await.unwrap(),
|
|
"invalid redirect_uri: https://mywebsite.com, allowable_uris: http://localhost:3000"
|
|
);
|
|
}
|
|
|
|
{
|
|
let resp = af_client
|
|
.web_api_oauth_redirect(&OAuthRedirect {
|
|
client_id: client_id.to_string(),
|
|
state: state.to_string(),
|
|
redirect_uri: "http://localhost:3000".to_string(),
|
|
response_type: "code".to_string(),
|
|
code_challenge: Some(code_challenge.clone()),
|
|
code_challenge_method: Some("S256".to_string()),
|
|
})
|
|
.await;
|
|
assert_eq!(resp.status(), StatusCode::SEE_OTHER);
|
|
|
|
let redirect_url = resp.headers().get("location").unwrap().to_str().unwrap();
|
|
let (code, ret_state) = extract_code_and_state(redirect_url);
|
|
assert_eq!(ret_state, state);
|
|
|
|
{
|
|
// did not provide code_verifier
|
|
let resp = af_client
|
|
.web_api_oauth_redirect_token(&OAuthRedirectToken {
|
|
code: code.clone(),
|
|
grant_type: "authorization_code".to_string(),
|
|
..Default::default()
|
|
})
|
|
.await;
|
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
|
assert_eq!(resp.text().await.unwrap(), "missing code_verifier");
|
|
}
|
|
|
|
{
|
|
let resp = af_client
|
|
.web_api_oauth_redirect_token(&OAuthRedirectToken {
|
|
code,
|
|
grant_type: "authorization_code".to_string(),
|
|
code_verifier: Some(code_challenge_orginal.to_string()),
|
|
..Default::default()
|
|
})
|
|
.await;
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
let token_str = resp.text().await.unwrap();
|
|
let _gotrue_token: GotrueTokenResponse = serde_json::from_str(&token_str).unwrap();
|
|
}
|
|
}
|
|
}
|
|
|
|
fn extract_code_and_state(url_str: &str) -> (String, String) {
|
|
// Parse the URL
|
|
let url = Url::parse(url_str).expect("Failed to parse URL");
|
|
|
|
// Extract the query parameters
|
|
let code = url
|
|
.query_pairs()
|
|
.find(|(key, _)| key == "code")
|
|
.map(|(_, value)| value.to_string())
|
|
.unwrap_or_else(|| "code not found".to_string());
|
|
|
|
let state = url
|
|
.query_pairs()
|
|
.find(|(key, _)| key == "state")
|
|
.map(|(_, value)| value.to_string())
|
|
.unwrap_or_else(|| "state not found".to_string());
|
|
|
|
(code, state)
|
|
}
|