Skip to content

Commit

Permalink
feat: ✨ generic login & flattern extra
Browse files Browse the repository at this point in the history
  • Loading branch information
holmofy committed Oct 7, 2024
1 parent 19550ab commit 4e9328d
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 122 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description = "just for oauth2 login"
edition = "2021"
license = "MIT"
name = "just-auth"
version = "0.1.1"
version = "0.1.2"

[dependencies]
async-trait = "0.1"
Expand Down
30 changes: 18 additions & 12 deletions src/baidu.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
//! https://openauth.baidu.com/doc/doc.html
use crate::error::Result;
use crate::{auth_server_builder, AuthAction, AuthConfig, AuthUrlProvider};
use crate::{auth_server_builder, AuthAction, AuthConfig, AuthUrlProvider, AuthUser};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use serde_with::{formats::SpaceSeparator, serde_as, StringWithSeparator};
use std::collections::HashMap;

pub struct AuthorizationServer {
config: AuthConfig,
Expand Down Expand Up @@ -60,6 +62,19 @@ impl AuthAction for AuthorizationServer {
})
}

async fn login(&self, callback: Self::AuthCallback) -> Result<AuthUser> {
let token = self.get_access_token(callback).await?;
let user = self.get_user_info(token.clone()).await?;
Ok(AuthUser {
user_id: user.openid,
name: user.username.unwrap_or_default(),
access_token: token.access_token,
refresh_token: token.refresh_token,
expires_in: token.expires_in,
extra: user.extra,
})
}

async fn get_access_token(&self, callback: Self::AuthCallback) -> Result<Self::AuthToken> {
let AuthConfig {
client_id,
Expand Down Expand Up @@ -166,16 +181,7 @@ pub struct GetUserInfoRequest {
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UserInfoResponse {
pub openid: String,
pub unionid: String,
pub userid: Option<u32>,
pub securemobile: Option<u32>,
pub username: Option<String>,
pub portrait: Option<String>,
pub userdetail: Option<String>,
pub birthday: Option<String>,
pub marriage: Option<String>,
pub sex: Option<String>,
pub blood: Option<String>,
pub is_bind_mobile: Option<String>,
pub is_realname: Option<String>,
#[serde(flatten)]
pub extra: HashMap<String, Value>,
}
33 changes: 23 additions & 10 deletions src/facebook.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
//! https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow
use crate::{auth_server_builder, error::Result, AuthAction, AuthConfig, AuthUrlProvider};
use crate::{
auth_server_builder, error::Result, AuthAction, AuthConfig, AuthUrlProvider, AuthUser,
};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use serde_with::{formats::CommaSeparator, serde_as, StringWithSeparator};
use std::collections::HashMap;

pub struct AuthorizationServer {
config: AuthConfig,
Expand Down Expand Up @@ -31,6 +35,7 @@ impl AuthUrlProvider for AuthorizationServer {
))
}

/// https://developers.facebook.com/docs/graph-api/overview#me
fn user_info_url(request: Self::UserInfoRequest) -> Result<String> {
let query = serde_urlencoded::to_string(request)?;
Ok(format!("https://graph.facebook.com/me?{query}"))
Expand Down Expand Up @@ -59,6 +64,19 @@ impl AuthAction for AuthorizationServer {
})
}

async fn login(&self, callback: Self::AuthCallback) -> Result<AuthUser> {
let token = self.get_access_token(callback).await?;
let user = self.get_user_info(token.clone()).await?;
Ok(AuthUser {
user_id: user.id,
name: user.name,
access_token: token.access_token,
refresh_token: token.token_type,
expires_in: token.expires_in,
extra: user.extra,
})
}

async fn get_access_token(&self, callback: Self::AuthCallback) -> Result<Self::AuthToken> {
let AuthConfig {
client_id,
Expand Down Expand Up @@ -129,13 +147,8 @@ pub struct GetUserInfoRequest {

#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UserInfoResponse {
pub openid: String,
pub nickname: String,
pub sex: i64,
pub province: String,
pub city: String,
pub country: String,
pub headimgurl: String,
pub privilege: Vec<String>,
pub unionid: String,
pub id: String,
pub name: String,
#[serde(flatten)]
pub extra: HashMap<String, Value>,
}
57 changes: 19 additions & 38 deletions src/github.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//! https://docs.github.com/zh/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps
use crate::error::Result;
use crate::{auth_server_builder, AuthAction, AuthConfig, AuthUrlProvider};
use crate::{auth_server_builder, AuthAction, AuthConfig, AuthUrlProvider, AuthUser};
use async_trait::async_trait;
use reqwest::header::ACCEPT;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use serde_with::{formats::SpaceSeparator, serde_as, StringWithSeparator};
use std::collections::HashMap;

pub struct AuthorizationServer {
config: AuthConfig,
Expand Down Expand Up @@ -59,6 +61,19 @@ impl AuthAction for AuthorizationServer {
})
}

async fn login(&self, callback: Self::AuthCallback) -> Result<AuthUser> {
let token = self.get_access_token(callback).await?;
let user = self.get_user_info(token.clone()).await?;
Ok(AuthUser {
user_id: user.id.to_string(),
name: user.name,
access_token: token.access_token,
refresh_token: token.token_type,
expires_in: i64::MAX,
extra: user.extra,
})
}

async fn get_access_token(&self, callback: Self::AuthCallback) -> Result<Self::AuthToken> {
let AuthConfig {
client_id,
Expand Down Expand Up @@ -130,45 +145,11 @@ pub struct TokenResponse {
#[derive(Debug, Serialize, Deserialize)]
pub struct GetUserInfoRequest {}

/// https://docs.github.com/en/rest/users/users
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UserInfoResponse {
pub login: String,
pub id: i64,
pub node_id: String,
pub avatar_url: String,
pub gravatar_id: String,
pub url: String,
pub html_url: String,
pub followers_url: String,
pub following_url: String,
pub gists_url: String,
pub starred_url: String,
pub subscriptions_url: String,
pub organizations_url: String,
pub repos_url: String,
pub events_url: String,
pub received_events_url: String,
#[serde(rename = "type")]
pub type_field: String,
pub site_admin: bool,
pub name: String,
pub company: String,
pub blog: String,
pub location: String,
pub email: String,
pub hireable: bool,
pub bio: String,
pub twitter_username: String,
pub public_repos: i64,
pub public_gists: i64,
pub followers: i64,
pub following: i64,
pub created_at: String,
pub updated_at: String,
pub private_gists: i64,
pub total_private_repos: i64,
pub owned_private_repos: i64,
pub disk_usage: i64,
pub collaborators: i64,
pub two_factor_authentication: bool,
#[serde(flatten)]
pub extra: HashMap<String, Value>,
}
17 changes: 13 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ pub mod weibo;

mod utils;

use std::collections::HashMap;

use crate::error::Result;
use async_trait::async_trait;
use serde_json::Value;

pub struct AuthConfig {
client_id: String,
Expand Down Expand Up @@ -96,12 +99,18 @@ pub trait AuthAction {

async fn authorize<S: Into<String> + Send>(&self, state: S) -> Result<String>;

async fn login(&self, callback: Self::AuthCallback) -> Result<Self::AuthUser> {
let token = self.get_access_token(callback).await?;
self.get_user_info(token).await
}
async fn login(&self, callback: Self::AuthCallback) -> Result<AuthUser>;

async fn get_access_token(&self, callback: Self::AuthCallback) -> Result<Self::AuthToken>;

async fn get_user_info(&self, token: Self::AuthToken) -> Result<Self::AuthUser>;
}

pub struct AuthUser {
pub user_id: String,
pub name: String,
pub access_token: String,
pub refresh_token: String,
pub expires_in: i64,
pub extra: HashMap<String, Value>,
}
62 changes: 42 additions & 20 deletions src/qq.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
//! https://wikinew.open.qq.com/index.html#/iwiki/901251864
use crate::{auth_server_builder, error::Result, utils, AuthAction, AuthConfig, AuthUrlProvider};
use crate::{
auth_server_builder, error::Result, utils, AuthAction, AuthConfig, AuthUrlProvider, AuthUser,
};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;

pub struct AuthorizationServer {
config: AuthConfig,
Expand Down Expand Up @@ -56,6 +60,27 @@ impl AuthAction for AuthorizationServer {
})
}

async fn login(&self, callback: Self::AuthCallback) -> Result<AuthUser> {
let AuthConfig { client_id, .. } = &self.config;
let token = self.get_access_token(callback).await?;
let access_token = token.access_token;
let open_id = self.get_open_id(&access_token).await?;
let user_info_url = Self::user_info_url(GetUserInfoRequest {
openid: open_id.openid.clone(),
access_token: access_token.clone(),
oauth_consumer_key: client_id.to_string(),
})?;
let user: Self::AuthUser = reqwest::get(user_info_url).await?.json().await?;
Ok(AuthUser {
user_id: open_id.openid,
name: user.nickname,
access_token: access_token,
refresh_token: token.refresh_token,
expires_in: token.expires_in.into(),
extra: user.extra,
})
}

async fn get_access_token(&self, callback: Self::AuthCallback) -> Result<Self::AuthToken> {
let AuthConfig {
client_id,
Expand All @@ -76,6 +101,18 @@ impl AuthAction for AuthorizationServer {
async fn get_user_info(&self, token: Self::AuthToken) -> Result<Self::AuthUser> {
let AuthConfig { client_id, .. } = &self.config;
let access_token = token.access_token;
let value = self.get_open_id(&access_token).await?;
let user_info_url = Self::user_info_url(GetUserInfoRequest {
openid: value.openid,
access_token: access_token,
oauth_consumer_key: client_id.to_string(),
})?;
Ok(reqwest::get(user_info_url).await?.json().await?)
}
}

impl AuthorizationServer {
async fn get_open_id(&self, access_token: &str) -> Result<OpenIdResp> {
let jsonp = reqwest::get(format!(
"https://graph.qq.com/oauth2.0/me?access_token={access_token}"
))
Expand All @@ -84,13 +121,7 @@ impl AuthAction for AuthorizationServer {
.await?;
let json =
utils::substr_between(&jsonp, "callback(", ");").expect("jsonp response is valid");
let value: OpenIdResp = serde_json::from_str(json)?;
let user_info_url = Self::user_info_url(GetUserInfoRequest {
openid: value.openid,
access_token: access_token,
oauth_consumer_key: client_id.to_string(),
})?;
Ok(reqwest::get(user_info_url).await?.json().await?)
Ok(serde_json::from_str(json)?)
}
}

Expand Down Expand Up @@ -162,20 +193,11 @@ pub struct GetUserInfoRequest {
openid: String,
}

/// https://wiki.connect.qq.com/get_user_info
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UserInfoResponse {
pub ret: i64,
pub msg: String,
pub nickname: String,
pub figureurl: String,
#[serde(rename = "figureurl_1")]
pub figureurl_1: String,
#[serde(rename = "figureurl_2")]
pub figureurl_2: String,
#[serde(rename = "figureurl_qq_1")]
pub figureurl_qq_1: String,
#[serde(rename = "figureurl_qq_2")]
pub figureurl_qq_2: String,
pub gender: String,
#[serde(flatten)]
pub extra: HashMap<String, Value>,
}
Loading

0 comments on commit 4e9328d

Please sign in to comment.