use std::sync::Arc;
use cookie::Cookie;
use http::header::{ACCEPT, USER_AGENT};
use mime::Mime;
use oauth2::basic::BasicClient;
use oauth2::reqwest::async_http_client;
use oauth2::{
AuthUrl, AuthorizationCode, ClientId, ClientSecret, CsrfToken, RedirectUrl, Scope,
TokenResponse, TokenUrl,
};
use tracing::debug;
use serde::Deserialize;
use uuid::Uuid;
use crate::auth::login::log_in_user_id;
use crate::auth::TokenMeta;
use crate::config;
use crate::db::{decode, encode, Database};
use crate::error::Result;
use crate::user::User;
use crate::Config;
use super::UserInfo;
pub const AUTH_URL: &str = "https://github.com/login/oauth/authorize";
pub const TOKEN_URL: &str = "https://github.com/login/oauth/access_token";
#[derive(Clone, Debug, Default, Deserialize)]
#[serde(default)]
pub struct GitHubUserInfo {
login: String,
email: Option<String>,
name: Option<String>,
location: Option<String>,
avatar_url: Option<String>,
}
impl Into<UserInfo> for GitHubUserInfo {
fn into(self) -> UserInfo {
UserInfo {
email: self.email.unwrap(),
handle: Some(self.login),
full_name: self.name,
location: self.location,
avatar_url: self.avatar_url,
..Default::default()
}
}
}
pub async fn get_user_info<'c>(
auth_code: String,
config: &Config,
db: &Database,
) -> Result<UserInfo> {
let client = crate::oauth::client(
&config.oauth.github,
config.domain.clone(),
"github".to_string(),
AUTH_URL.to_string(),
TOKEN_URL.to_string(),
)?;
let token = client
.exchange_code(AuthorizationCode::new(auth_code.clone()))
.request_async(async_http_client)
.await
.map_err(|e| crate::ErrorKind::AuthFailed(e.to_string()))?;
let mime: Mime = "application/vnd.github.v3+json"
.parse()
.expect("parse GitHub MIME type");
let client = reqwest::Client::new();
let response = client
.get("https://api.github.com/user")
.bearer_auth(token.access_token().secret())
.header(ACCEPT, mime.essence_str())
.header(USER_AGENT, format!("{}", config.name))
.send()
.await?;
let mut user_info: GitHubUserInfo = response.json::<GitHubUserInfo>().await?;
println!("user_info: {:?}", user_info);
if user_info.email.is_none() {
#[derive(Deserialize)]
struct Email {
email: String,
verified: bool,
primary: bool,
visibility: Option<String>,
}
let response = client
.get("https://api.github.com/user/emails")
.bearer_auth(token.access_token().secret())
.header(ACCEPT, mime.essence_str())
.header(USER_AGENT, format!("{}", config.name))
.send()
.await?;
let emails = response.json::<Vec<Email>>().await.unwrap();
for email in emails {
if email.verified && email.primary {
user_info.email = Some(email.email);
}
}
}
Ok(user_info.into())
}