mirror of
https://github.com/moghtech/komodo.git
synced 2026-07-20 15:20:27 -05:00
google oauth working
This commit is contained in:
Generated
+1
@@ -158,6 +158,7 @@ dependencies = [
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
|
||||
+36
-16
@@ -1,26 +1,26 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use axum::{Router, Extension, routing::get, response::Redirect, extract::Query};
|
||||
use axum::{extract::Query, response::Redirect, routing::get, Extension, Router};
|
||||
use axum_oauth2::google::{GoogleOauthClient, GoogleOauthExtension};
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{Deserialize, doc};
|
||||
use types::{CoreConfig, monitor_timestamp, User};
|
||||
use mungos::{doc, Deserialize};
|
||||
use types::{monitor_timestamp, CoreConfig, User};
|
||||
|
||||
use crate::{state::StateExtension, response};
|
||||
use crate::{response, state::StateExtension};
|
||||
|
||||
use super::JwtExtension;
|
||||
|
||||
pub fn router(config: &CoreConfig) -> Router {
|
||||
let client = GoogleOauthClient::new(
|
||||
let client = GoogleOauthClient::new(
|
||||
config.google_oauth.id.clone(),
|
||||
config.google_oauth.secret.clone(),
|
||||
format!("{}/auth/google/callback", config.host),
|
||||
&[],
|
||||
&["https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email"],
|
||||
"monitor".to_string(),
|
||||
);
|
||||
Router::new()
|
||||
.route(
|
||||
Router::new()
|
||||
.route(
|
||||
"/login",
|
||||
get(|Extension(client): GoogleOauthExtension| async move {
|
||||
Redirect::to(&client.get_login_redirect_url())
|
||||
@@ -35,13 +35,14 @@ pub fn router(config: &CoreConfig) -> Router {
|
||||
response!(redirect)
|
||||
}),
|
||||
)
|
||||
.layer(Extension(Arc::new(client)))
|
||||
.layer(Extension(Arc::new(client)))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct CallbackQuery {
|
||||
state: String,
|
||||
code: String,
|
||||
state: Option<String>,
|
||||
code: Option<String>,
|
||||
error: Option<String>,
|
||||
}
|
||||
|
||||
async fn callback(
|
||||
@@ -50,11 +51,24 @@ async fn callback(
|
||||
Extension(state): StateExtension,
|
||||
Query(query): Query<CallbackQuery>,
|
||||
) -> anyhow::Result<Redirect> {
|
||||
if !client.check_state(&query.state) {
|
||||
if let Some(error) = query.error {
|
||||
return Err(anyhow!("auth error from google: {error}"));
|
||||
}
|
||||
if !client.check_state(
|
||||
&query
|
||||
.state
|
||||
.ok_or(anyhow!("callback query does not contain state"))?,
|
||||
) {
|
||||
return Err(anyhow!("state mismatch"));
|
||||
}
|
||||
let token = client.get_access_token(&query.code).await?;
|
||||
let google_user = client.get_google_user(&token.access_token)?;
|
||||
let token = client
|
||||
.get_access_token(
|
||||
&query
|
||||
.code
|
||||
.ok_or(anyhow!("callback query does not contain code"))?,
|
||||
)
|
||||
.await?;
|
||||
let google_user = client.get_google_user(&token.id_token)?;
|
||||
let google_id = google_user.id.to_string();
|
||||
let user = state
|
||||
.db
|
||||
@@ -69,7 +83,13 @@ async fn callback(
|
||||
None => {
|
||||
let ts = monitor_timestamp();
|
||||
let user = User {
|
||||
username: google_user.email.split("@").collect::<Vec<&str>>().get(0).unwrap().to_string(),
|
||||
username: google_user
|
||||
.email
|
||||
.split("@")
|
||||
.collect::<Vec<&str>>()
|
||||
.get(0)
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
avatar: google_user.picture.into(),
|
||||
google_id: google_id.into(),
|
||||
created_at: ts.clone(),
|
||||
@@ -92,4 +112,4 @@ async fn callback(
|
||||
"{}?token={exchange_token}",
|
||||
state.config.host
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg height="800px" width="800px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 210 210" xml:space="preserve">
|
||||
<path fill="#fceade" d="M0,105C0,47.103,47.103,0,105,0c23.383,0,45.515,7.523,64.004,21.756l-24.4,31.696C133.172,44.652,119.477,40,105,40
|
||||
c-35.841,0-65,29.159-65,65s29.159,65,65,65c28.867,0,53.398-18.913,61.852-45H105V85h105v20c0,57.897-47.103,105-105,105
|
||||
S0,162.897,0,105z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 706 B |
@@ -83,6 +83,12 @@ const Login: Component<{}> = (p) => {
|
||||
>
|
||||
log in with github <Icon type="github" />
|
||||
</button>
|
||||
<button
|
||||
class={combineClasses(s.LoginItem, "red")}
|
||||
onClick={() => location.replace(`${URL}/auth/google/login`)}
|
||||
>
|
||||
log in with google <Icon type="google" />
|
||||
</button>
|
||||
</Grid>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -35,6 +35,7 @@ export type IconType =
|
||||
| "cut"
|
||||
| "fullscreen"
|
||||
| "github"
|
||||
| "google"
|
||||
| "edit"
|
||||
| "clipboard"
|
||||
| "check"
|
||||
|
||||
@@ -11,6 +11,7 @@ reqwest = { version = "0.11", features = ["json"] }
|
||||
anyhow = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
urlencoding = "2.1"
|
||||
rand = "0.8"
|
||||
jwt = "0.16"
|
||||
@@ -1,10 +1,11 @@
|
||||
use std::sync::{Mutex, Arc};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use axum::Extension;
|
||||
use jwt::{Header, Token};
|
||||
use jwt::Token;
|
||||
use reqwest::StatusCode;
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::random_string;
|
||||
|
||||
@@ -77,35 +78,29 @@ impl GoogleOauthClient {
|
||||
("grant_type", "authorization_code"),
|
||||
],
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to get google access token using code")
|
||||
}
|
||||
|
||||
pub fn get_google_user(&self, token: &str) -> anyhow::Result<GoogleUser> {
|
||||
let token: Token<Header, GoogleUser, jwt::Unverified> = Token::parse_unverified(token)?;
|
||||
Ok(token.claims().to_owned())
|
||||
pub fn get_google_user(&self, id_token: &str) -> anyhow::Result<GoogleUser> {
|
||||
let t: Token<Value, GoogleUser, jwt::Unverified> = Token::parse_unverified(id_token).context("failed to parse id_token")?;
|
||||
Ok(t.claims().to_owned())
|
||||
}
|
||||
|
||||
async fn post<B: Serialize, R: DeserializeOwned>(
|
||||
&self,
|
||||
endpoint: &str,
|
||||
query: &[(&str, &str)],
|
||||
body: Option<&B>,
|
||||
body: &[(&str, &str)],
|
||||
bearer_token: Option<&str>,
|
||||
) -> anyhow::Result<R> {
|
||||
let mut req = self
|
||||
.http
|
||||
.post(endpoint)
|
||||
.query(query)
|
||||
.form(body)
|
||||
.header("Accept", "application/json")
|
||||
.header("User-Agent", &self.user_agent);
|
||||
|
||||
if let Some(body) = body {
|
||||
req = req.json(body);
|
||||
}
|
||||
|
||||
if let Some(bearer_token) = bearer_token {
|
||||
req = req.header("Authorization", format!("Bearer {bearer_token}"));
|
||||
}
|
||||
@@ -132,6 +127,7 @@ impl GoogleOauthClient {
|
||||
#[derive(Deserialize)]
|
||||
pub struct AccessTokenResponse {
|
||||
pub access_token: String,
|
||||
pub id_token: String,
|
||||
pub scope: String,
|
||||
pub token_type: String,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user