Files
komodo/core/src/auth/github.rs
T
2022-12-29 07:59:50 +00:00

96 lines
2.9 KiB
Rust

use std::sync::Arc;
use anyhow::{anyhow, Context};
use axum::{extract::Query, response::Redirect, routing::get, Extension, Router};
use axum_oauth2::github::{GithubOauthClient, GithubOauthExtension};
use helpers::handle_anyhow_error;
use mungos::{doc, Deserialize};
use types::{monitor_timestamp, CoreConfig, User};
use crate::{response, state::StateExtension};
use super::JwtExtension;
pub fn router(config: &CoreConfig) -> Router {
let client = GithubOauthClient::new(
config.github_oauth.id.clone(),
config.github_oauth.secret.clone(),
format!("{}/auth/github/callback", config.host),
&[],
"monitor".to_string(),
);
Router::new()
.route(
"/login",
get(|Extension(client): GithubOauthExtension| async move {
Redirect::to(&client.get_login_redirect_url())
}),
)
.route(
"/callback",
get(|client, jwt, state, query| async {
let redirect = callback(client, jwt, state, query)
.await
.map_err(handle_anyhow_error)?;
response!(redirect)
}),
)
.layer(Extension(Arc::new(client)))
}
#[derive(Deserialize)]
struct CallbackQuery {
state: String,
code: String,
}
async fn callback(
Extension(client): GithubOauthExtension,
Extension(jwt_client): JwtExtension,
Extension(state): StateExtension,
Query(query): Query<CallbackQuery>,
) -> anyhow::Result<Redirect> {
if !client.check_state(&query.state) {
return Err(anyhow!("state mismatch"));
}
let token = client.get_access_token(&query.code).await?;
let github_user = client.get_github_user(&token.access_token).await?;
let github_id = github_user.id.to_string();
let user = state
.db
.users
.find_one(doc! { "github_id": &github_id }, None)
.await
.context("failed at find user query from mongo")?;
let jwt = match user {
Some(user) => jwt_client
.generate(user.id)
.context("failed to generate jwt")?,
None => {
let ts = monitor_timestamp();
let user = User {
username: github_user.login,
avatar: github_user.avatar_url.into(),
github_id: github_id.into(),
created_at: ts.clone(),
updated_at: ts,
..Default::default()
};
let user_id = state
.db
.users
.create_one(user)
.await
.context("failed to create user on mongo")?;
jwt_client
.generate(user_id)
.context("failed to generate jwt")?
}
};
let exchange_token = jwt_client.create_exchange_token(jwt);
Ok(Redirect::to(&format!(
"{}?token={exchange_token}",
state.config.host
)))
}