update: oauth state more understandable

This commit is contained in:
maix0 2024-09-28 12:19:02 +02:00
parent e151a799ef
commit 180503a244
3 changed files with 47 additions and 48 deletions

12
Cargo.lock generated
View file

@ -77,9 +77,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "axum"
version = "0.7.6"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec"
checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae"
dependencies = [
"async-trait",
"axum-core",
@ -113,9 +113,9 @@ dependencies = [
[[package]]
name = "axum-core"
version = "0.4.4"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00"
checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
dependencies = [
"async-trait",
"bytes",
@ -1330,9 +1330,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.78"
version = "2.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81b9b4733a9c8b8aaa20634df36eeb68cc0c0669f2e18fb287006b496a14195d"
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
dependencies = [
"proc-macro2",
"quote",

View file

@ -6,7 +6,7 @@
use std::{
collections::{HashMap, HashSet},
sync::{Arc, RwLock},
sync::Arc,
time::Duration,
};
@ -14,21 +14,21 @@ use axum::{
async_trait,
extract::{FromRef, FromRequestParts, Query, State},
http::{request::Parts, StatusCode},
response::{AppendHeaders, Html, IntoResponse, Redirect},
response::{Html, IntoResponse, Redirect},
routing::get,
Json, Router,
Router,
};
use axum_extra::extract::{
cookie::{Cookie, Expiration, Key, SameSite},
cookie::{Cookie, Key, SameSite},
CookieJar, PrivateCookieJar,
};
use base64::Engine;
use color_eyre::eyre::Context;
use reqwest::tls::Version;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::{json, Value};
use tokio::{io::AsyncReadExt, sync::Mutex};
use tracing::{error, info};
use serde::{Deserialize, Serialize};
use serde_json::json;
use tokio::sync::Mutex;
use tracing::{error, info, warn};
macro_rules! unwrap_env {
($name:literal) => {
@ -78,6 +78,7 @@ async fn tutors(config: AppState) {
"page[number]": page_nb,
"page[size]": 100,
}),
Option::<&oauth2::Token>::None,
)
.await
.unwrap();
@ -119,7 +120,7 @@ async fn main() {
http.clone(),
unwrap_env!("CLIENT_ID"),
unwrap_env!("CLIENT_SECRET"),
"http://local.maix.me/auth/callback",
"http://local.maix.me:9911/auth/callback",
)
.await
.unwrap();
@ -147,7 +148,7 @@ async fn main() {
// run our app with hyper
let listener = tokio::net::TcpListener::bind(format!(
"127.0.0.1:{}",
"0.0.0.0:{}",
std::env::args()
.nth(1)
.and_then(|s| s.parse::<u16>().ok())
@ -175,9 +176,6 @@ async fn oauth2_login(State(state): State<AppState>) -> Result<Redirect, StatusC
))
}
use time::Duration as TDuration;
use time::OffsetDateTime;
#[axum::debug_handler]
async fn oauth2_callback(
State(state): State<AppState>,
@ -197,18 +195,15 @@ async fn oauth2_callback(
.await
.wrap_err("callback")?;
let rep = state
.http
.get("https://api.intra.42.fr/v2/users/me")
.bearer_auth(&token.access_token)
.send()
let res: User42 = state
.oauth
.do_request("https://api.intra.42.fr/v2/me", &(), Some(&token))
.await
.wrap_err("Unable to get user self")?;
let json: User42 = rep.json().await.wrap_err("unable to parse api reply")?;
let mut cookie = Cookie::new("token", json.id.to_string());
let mut cookie = Cookie::new("token", res.id.to_string());
cookie.set_same_site(SameSite::None);
cookie.set_secure(true);
cookie.set_secure(false);
cookie.set_path("/");
// cookie.set_domain("localhost:3000");
// cookie.set_http_only(Some(false));
@ -225,23 +220,20 @@ async fn oauth2_callback(
}
#[derive(Clone, Debug)]
struct UserLoggedIn {
id: u64,
}
struct UserLoggedIn;
#[async_trait]
impl FromRequestParts<AppState> for UserLoggedIn {
type Rejection = (StatusCode, CookieJar, Redirect);
type Rejection = (StatusCode, PrivateCookieJar, Redirect);
async fn from_request_parts(
parts: &mut Parts,
state: &AppState,
) -> Result<Self, Self::Rejection> {
info!("banane");
let jar = CookieJar::from_request_parts(parts, state).await.unwrap();
dbg!(&jar);
let jar = PrivateCookieJar::from_request_parts(parts, state)
.await
.unwrap();
let Some(id) = jar.get("token") else {
info!("no cookie");
return Err((
StatusCode::TEMPORARY_REDIRECT,
jar,
@ -251,7 +243,6 @@ impl FromRequestParts<AppState> for UserLoggedIn {
let Ok(user_id) = id.value().parse::<u64>() else {
let jar = jar.remove("token");
info!("not id");
return Err((
StatusCode::TEMPORARY_REDIRECT,
jar,
@ -260,10 +251,8 @@ impl FromRequestParts<AppState> for UserLoggedIn {
};
if state.tutors.lock().await.contains(&user_id) {
info!("is tut");
Ok(UserLoggedIn { id: user_id })
Ok(UserLoggedIn)
} else {
info!("not tut");
let jar = jar.remove("token");
Err((
StatusCode::TEMPORARY_REDIRECT,
@ -283,9 +272,8 @@ async fn root(_user: UserLoggedIn) -> Html<&'static str> {
<a href="/stop">stop</a><br>
<a href="/start">start</a><br>
<a href="/status">status</a><br>
<a href="/db">db</a><br>
<a href="/pull">git pull (ask before!)</a><br>
"#,
"#,
)
}

View file

@ -17,13 +17,23 @@ pub struct OauthClient {
pub struct Token {
#[serde(default)]
refresh_token: Option<String>,
pub access_token: String,
access_token: String,
token_type: String,
expires_in: u64,
scope: String,
created_at: u64,
}
pub trait IntoToken {
fn get_token(&self) -> &str;
}
impl IntoToken for Token {
fn get_token(&self) -> &str {
&self.access_token
}
}
impl OauthClient {
async fn get_app_token(
client: reqwest::Client,
@ -43,10 +53,7 @@ impl OauthClient {
.send()
.await
.wrap_err("Sending request to fetch 42 API token")?;
let body = response.bytes().await?;
let text = String::from_utf8_lossy(&body);
println!("{}", text);
let json: Token = serde_json::from_slice(&body).unwrap(); // response.json().await.wrap_err("API response to json")?;
let json: Token = response.json().await.wrap_err("API response to json")?;
Ok(json)
}
pub async fn new(
@ -77,7 +84,7 @@ impl OauthClient {
.scheme("https")
.authority("api.intra.42.fr")
.path_and_query(format!(
"/oauth/authorize?client_id={}&scope=public&response_type=code&redirect_uri={redirect_uri}&code={}",
"/oauth/authorize?client_id={}&scope=public&response_type=code&redirect_uri={redirect_uri}&state={}",
self.client_id, base64::engine::general_purpose::URL_SAFE.encode(csrf)
))
.build()
@ -115,13 +122,17 @@ impl OauthClient {
&self,
url: impl AsRef<str>,
qs: &impl Serialize,
token: Option<&impl IntoToken>,
) -> eyre::Result<R> {
let url = url.as_ref();
let token = token
.map(IntoToken::get_token)
.unwrap_or_else(|| self.token.get_token());
let req = self
.http
.get(url)
.query(qs)
.bearer_auth(&self.token.access_token)
.bearer_auth(token)
.send()
.await
.wrap_err("Failed to send request")?;