update: oauth state more understandable
This commit is contained in:
parent
e151a799ef
commit
180503a244
3 changed files with 47 additions and 48 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
|
@ -77,9 +77,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.7.6"
|
version = "0.7.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec"
|
checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum-core",
|
"axum-core",
|
||||||
|
|
@ -113,9 +113,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum-core"
|
name = "axum-core"
|
||||||
version = "0.4.4"
|
version = "0.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00"
|
checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
|
@ -1330,9 +1330,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.78"
|
version = "2.0.79"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81b9b4733a9c8b8aaa20634df36eeb68cc0c0669f2e18fb287006b496a14195d"
|
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
||||||
58
src/main.rs
58
src/main.rs
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
sync::{Arc, RwLock},
|
sync::Arc,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -14,21 +14,21 @@ use axum::{
|
||||||
async_trait,
|
async_trait,
|
||||||
extract::{FromRef, FromRequestParts, Query, State},
|
extract::{FromRef, FromRequestParts, Query, State},
|
||||||
http::{request::Parts, StatusCode},
|
http::{request::Parts, StatusCode},
|
||||||
response::{AppendHeaders, Html, IntoResponse, Redirect},
|
response::{Html, IntoResponse, Redirect},
|
||||||
routing::get,
|
routing::get,
|
||||||
Json, Router,
|
Router,
|
||||||
};
|
};
|
||||||
use axum_extra::extract::{
|
use axum_extra::extract::{
|
||||||
cookie::{Cookie, Expiration, Key, SameSite},
|
cookie::{Cookie, Key, SameSite},
|
||||||
CookieJar, PrivateCookieJar,
|
CookieJar, PrivateCookieJar,
|
||||||
};
|
};
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use color_eyre::eyre::Context;
|
use color_eyre::eyre::Context;
|
||||||
use reqwest::tls::Version;
|
use reqwest::tls::Version;
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{json, Value};
|
use serde_json::json;
|
||||||
use tokio::{io::AsyncReadExt, sync::Mutex};
|
use tokio::sync::Mutex;
|
||||||
use tracing::{error, info};
|
use tracing::{error, info, warn};
|
||||||
|
|
||||||
macro_rules! unwrap_env {
|
macro_rules! unwrap_env {
|
||||||
($name:literal) => {
|
($name:literal) => {
|
||||||
|
|
@ -78,6 +78,7 @@ async fn tutors(config: AppState) {
|
||||||
"page[number]": page_nb,
|
"page[number]": page_nb,
|
||||||
"page[size]": 100,
|
"page[size]": 100,
|
||||||
}),
|
}),
|
||||||
|
Option::<&oauth2::Token>::None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -119,7 +120,7 @@ async fn main() {
|
||||||
http.clone(),
|
http.clone(),
|
||||||
unwrap_env!("CLIENT_ID"),
|
unwrap_env!("CLIENT_ID"),
|
||||||
unwrap_env!("CLIENT_SECRET"),
|
unwrap_env!("CLIENT_SECRET"),
|
||||||
"http://local.maix.me/auth/callback",
|
"http://local.maix.me:9911/auth/callback",
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -147,7 +148,7 @@ async fn main() {
|
||||||
|
|
||||||
// run our app with hyper
|
// run our app with hyper
|
||||||
let listener = tokio::net::TcpListener::bind(format!(
|
let listener = tokio::net::TcpListener::bind(format!(
|
||||||
"127.0.0.1:{}",
|
"0.0.0.0:{}",
|
||||||
std::env::args()
|
std::env::args()
|
||||||
.nth(1)
|
.nth(1)
|
||||||
.and_then(|s| s.parse::<u16>().ok())
|
.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]
|
#[axum::debug_handler]
|
||||||
async fn oauth2_callback(
|
async fn oauth2_callback(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
|
|
@ -197,18 +195,15 @@ async fn oauth2_callback(
|
||||||
.await
|
.await
|
||||||
.wrap_err("callback")?;
|
.wrap_err("callback")?;
|
||||||
|
|
||||||
let rep = state
|
let res: User42 = state
|
||||||
.http
|
.oauth
|
||||||
.get("https://api.intra.42.fr/v2/users/me")
|
.do_request("https://api.intra.42.fr/v2/me", &(), Some(&token))
|
||||||
.bearer_auth(&token.access_token)
|
|
||||||
.send()
|
|
||||||
.await
|
.await
|
||||||
.wrap_err("Unable to get user self")?;
|
.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", res.id.to_string());
|
||||||
let mut cookie = Cookie::new("token", json.id.to_string());
|
|
||||||
cookie.set_same_site(SameSite::None);
|
cookie.set_same_site(SameSite::None);
|
||||||
cookie.set_secure(true);
|
cookie.set_secure(false);
|
||||||
cookie.set_path("/");
|
cookie.set_path("/");
|
||||||
// cookie.set_domain("localhost:3000");
|
// cookie.set_domain("localhost:3000");
|
||||||
// cookie.set_http_only(Some(false));
|
// cookie.set_http_only(Some(false));
|
||||||
|
|
@ -225,23 +220,20 @@ async fn oauth2_callback(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct UserLoggedIn {
|
struct UserLoggedIn;
|
||||||
id: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl FromRequestParts<AppState> for UserLoggedIn {
|
impl FromRequestParts<AppState> for UserLoggedIn {
|
||||||
type Rejection = (StatusCode, CookieJar, Redirect);
|
type Rejection = (StatusCode, PrivateCookieJar, Redirect);
|
||||||
|
|
||||||
async fn from_request_parts(
|
async fn from_request_parts(
|
||||||
parts: &mut Parts,
|
parts: &mut Parts,
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
) -> Result<Self, Self::Rejection> {
|
) -> Result<Self, Self::Rejection> {
|
||||||
info!("banane");
|
let jar = PrivateCookieJar::from_request_parts(parts, state)
|
||||||
let jar = CookieJar::from_request_parts(parts, state).await.unwrap();
|
.await
|
||||||
dbg!(&jar);
|
.unwrap();
|
||||||
let Some(id) = jar.get("token") else {
|
let Some(id) = jar.get("token") else {
|
||||||
info!("no cookie");
|
|
||||||
return Err((
|
return Err((
|
||||||
StatusCode::TEMPORARY_REDIRECT,
|
StatusCode::TEMPORARY_REDIRECT,
|
||||||
jar,
|
jar,
|
||||||
|
|
@ -251,7 +243,6 @@ impl FromRequestParts<AppState> for UserLoggedIn {
|
||||||
|
|
||||||
let Ok(user_id) = id.value().parse::<u64>() else {
|
let Ok(user_id) = id.value().parse::<u64>() else {
|
||||||
let jar = jar.remove("token");
|
let jar = jar.remove("token");
|
||||||
info!("not id");
|
|
||||||
return Err((
|
return Err((
|
||||||
StatusCode::TEMPORARY_REDIRECT,
|
StatusCode::TEMPORARY_REDIRECT,
|
||||||
jar,
|
jar,
|
||||||
|
|
@ -260,10 +251,8 @@ impl FromRequestParts<AppState> for UserLoggedIn {
|
||||||
};
|
};
|
||||||
|
|
||||||
if state.tutors.lock().await.contains(&user_id) {
|
if state.tutors.lock().await.contains(&user_id) {
|
||||||
info!("is tut");
|
Ok(UserLoggedIn)
|
||||||
Ok(UserLoggedIn { id: user_id })
|
|
||||||
} else {
|
} else {
|
||||||
info!("not tut");
|
|
||||||
let jar = jar.remove("token");
|
let jar = jar.remove("token");
|
||||||
Err((
|
Err((
|
||||||
StatusCode::TEMPORARY_REDIRECT,
|
StatusCode::TEMPORARY_REDIRECT,
|
||||||
|
|
@ -283,9 +272,8 @@ async fn root(_user: UserLoggedIn) -> Html<&'static str> {
|
||||||
<a href="/stop">stop</a><br>
|
<a href="/stop">stop</a><br>
|
||||||
<a href="/start">start</a><br>
|
<a href="/start">start</a><br>
|
||||||
<a href="/status">status</a><br>
|
<a href="/status">status</a><br>
|
||||||
<a href="/db">db</a><br>
|
|
||||||
<a href="/pull">git pull (ask before!)</a><br>
|
<a href="/pull">git pull (ask before!)</a><br>
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,23 @@ pub struct OauthClient {
|
||||||
pub struct Token {
|
pub struct Token {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
refresh_token: Option<String>,
|
refresh_token: Option<String>,
|
||||||
pub access_token: String,
|
access_token: String,
|
||||||
token_type: String,
|
token_type: String,
|
||||||
expires_in: u64,
|
expires_in: u64,
|
||||||
scope: String,
|
scope: String,
|
||||||
created_at: u64,
|
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 {
|
impl OauthClient {
|
||||||
async fn get_app_token(
|
async fn get_app_token(
|
||||||
client: reqwest::Client,
|
client: reqwest::Client,
|
||||||
|
|
@ -43,10 +53,7 @@ impl OauthClient {
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.wrap_err("Sending request to fetch 42 API token")?;
|
.wrap_err("Sending request to fetch 42 API token")?;
|
||||||
let body = response.bytes().await?;
|
let json: Token = response.json().await.wrap_err("API response to json")?;
|
||||||
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")?;
|
|
||||||
Ok(json)
|
Ok(json)
|
||||||
}
|
}
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
|
|
@ -77,7 +84,7 @@ impl OauthClient {
|
||||||
.scheme("https")
|
.scheme("https")
|
||||||
.authority("api.intra.42.fr")
|
.authority("api.intra.42.fr")
|
||||||
.path_and_query(format!(
|
.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)
|
self.client_id, base64::engine::general_purpose::URL_SAFE.encode(csrf)
|
||||||
))
|
))
|
||||||
.build()
|
.build()
|
||||||
|
|
@ -115,13 +122,17 @@ impl OauthClient {
|
||||||
&self,
|
&self,
|
||||||
url: impl AsRef<str>,
|
url: impl AsRef<str>,
|
||||||
qs: &impl Serialize,
|
qs: &impl Serialize,
|
||||||
|
token: Option<&impl IntoToken>,
|
||||||
) -> eyre::Result<R> {
|
) -> eyre::Result<R> {
|
||||||
let url = url.as_ref();
|
let url = url.as_ref();
|
||||||
|
let token = token
|
||||||
|
.map(IntoToken::get_token)
|
||||||
|
.unwrap_or_else(|| self.token.get_token());
|
||||||
let req = self
|
let req = self
|
||||||
.http
|
.http
|
||||||
.get(url)
|
.get(url)
|
||||||
.query(qs)
|
.query(qs)
|
||||||
.bearer_auth(&self.token.access_token)
|
.bearer_auth(token)
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.wrap_err("Failed to send request")?;
|
.wrap_err("Failed to send request")?;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue