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]]
|
||||
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",
|
||||
|
|
|
|||
56
src/main.rs
56
src/main.rs
|
|
@ -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,7 +272,6 @@ 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>
|
||||
"#,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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")?;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue