summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKjetil Orbekk <kj@orbekk.com>2022-10-13 08:12:59 -0400
committerKjetil Orbekk <kj@orbekk.com>2022-10-13 08:12:59 -0400
commitaccb9032b9abe595020a27dd2f7b666cb7028f67 (patch)
tree4c92937ad368e93e9bb9ddf9a0ebb31e8288c04b
parent8b5d16152ffb7d55811a7a558f67620a94e4cbf0 (diff)
Add AuthenticatedSession request extractor
-rw-r--r--server/src/auth.rs51
-rw-r--r--server/src/error.rs13
-rw-r--r--server/src/main.rs46
-rw-r--r--server/src/server.rs13
4 files changed, 75 insertions, 48 deletions
diff --git a/server/src/auth.rs b/server/src/auth.rs
index 0be1b85..98a0000 100644
--- a/server/src/auth.rs
+++ b/server/src/auth.rs
@@ -6,9 +6,13 @@ use std::{
sync::{Arc, Mutex},
};
-use crate::error::BridgeError;
+use crate::{error::BridgeError, server::ContextExtension};
use async_trait::async_trait;
-use axum::{extract::FromRequest, http};
+use axum::{
+ extract::FromRequest,
+ response::{IntoResponse, Response},
+ Json,
+};
use chrono::{DateTime, Utc};
use lru::LruCache;
use openidconnect::{
@@ -22,7 +26,7 @@ use openidconnect::{
use protocol::UserInfo;
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
-use tower_cookies::Cookies;
+use tower_cookies::{Cookie, Cookies};
use tracing::{debug, error, info};
use uuid::Uuid;
@@ -335,25 +339,42 @@ pub async fn fetch_authenticated_session(
}
}
-#[derive(Clone, Debug, Default)]
-pub struct LoggedInUser {
- _priv: (),
-}
-
#[async_trait]
-impl<B> FromRequest<B> for LoggedInUser
+impl<B> FromRequest<B> for AuthenticatedSession
where
B: Send,
{
- type Rejection = (http::StatusCode, &'static str);
+ type Rejection = Response;
async fn from_request(
req: &mut axum::extract::RequestParts<B>,
) -> Result<Self, Self::Rejection> {
- info!(
- "Creating LoggedInUser; found cookies: {:?}",
- req.extensions().get::<Cookies>().cloned()
- );
- Ok(LoggedInUser { _priv: () })
+ let cookies = Cookies::from_request(req)
+ .await
+ .map_err(|e| e.into_response())?;
+ let extension = ContextExtension::from_request(req)
+ .await
+ .map_err(|e| e.into_response())?;
+ let cookie = match cookies.get("user-id") {
+ None => return Err(BridgeError::NotLoggedIn.into_response()),
+ Some(v) => v,
+ };
+
+ let session_id: SessionId = match SessionId::from_str(cookie.value()) {
+ Err(e) => {
+ info!("Clearing cookie that failed to parse {cookie:?}: {e}");
+ cookies.remove(cookie.into_owned());
+ return Err(BridgeError::NotLoggedIn.into_response());
+ }
+ Ok(s) => s,
+ };
+ let session = match crate::auth::fetch_authenticated_session(&extension.db, &session_id)
+ .await
+ .map_err(|e| e.into_response())?
+ {
+ None => return Err(BridgeError::NotLoggedIn.into_response()),
+ Some(v) => v,
+ };
+ Ok(session)
}
}
diff --git a/server/src/error.rs b/server/src/error.rs
index cea23e7..aef2687 100644
--- a/server/src/error.rs
+++ b/server/src/error.rs
@@ -1,4 +1,4 @@
-use axum::{http::StatusCode, response::IntoResponse};
+use axum::{http::{StatusCode, self}, response::IntoResponse};
use openidconnect::{core::CoreErrorResponseType, ClaimsVerificationError, StandardErrorResponse};
use tracing::error;
@@ -26,6 +26,9 @@ pub enum BridgeError {
#[error("Unexpected authorization error")]
UnexpectedInvalidAuthorization(#[from] ClaimsVerificationError),
+ #[error("User is not logged in")]
+ NotLoggedIn,
+
#[error("Authentication error")]
SigningFailed(#[from] openidconnect::SigningError),
@@ -42,9 +45,15 @@ pub enum BridgeError {
DurationOutOfRange(#[from] time::OutOfRangeError),
}
+impl BridgeError {
+ pub fn as_rejection(&self) -> (http::StatusCode, String) {
+ (StatusCode::INTERNAL_SERVER_ERROR, format!("Error: {self}"))
+ }
+}
+
impl IntoResponse for BridgeError {
fn into_response(self) -> axum::response::Response {
error!("Error occurred: {self:?}");
- (StatusCode::INTERNAL_SERVER_ERROR, format!("Error: {self}")).into_response()
+ self.as_rejection().into_response()
}
}
diff --git a/server/src/main.rs b/server/src/main.rs
index fd28789..b961c17 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -1,6 +1,6 @@
use std::{collections::HashMap, env, str::FromStr, sync::Arc};
-use auth::{AuthenticatedSession, LoggedInUser};
+use auth::AuthenticatedSession;
use axum::{
extract::{Extension, Query},
response::Redirect,
@@ -8,23 +8,21 @@ use axum::{
Json, Router,
};
use protocol::{Table, UserInfo};
+use server::ContextExtension;
use tower_cookies::{Cookie, CookieManagerLayer, Cookies};
use tower_http::trace::TraceLayer;
use tracing::info;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
mod auth;
mod error;
-use crate::auth::{Authenticator, SessionId};
+mod server;
use crate::error::BridgeError;
+use crate::{
+ auth::{Authenticator, SessionId},
+ server::ServerContext,
+};
use sqlx::{postgres::PgPoolOptions, PgPool};
-pub struct ServerContext {
- pub app_url: String,
- pub authenticator: Authenticator,
- pub db: PgPool,
-}
-type ContextExtension = Extension<Arc<ServerContext>>;
-
#[tokio::main]
async fn main() {
dotenv::dotenv().ok();
@@ -60,6 +58,7 @@ async fn main() {
let app = Router::new()
.route("/api/user/info", get(user_info))
+ // .route("/api/user/table", get(user_table))
.route("/api/login", get(login))
.route(auth::LOGIN_CALLBACK, get(login_callback))
.layer(CookieManagerLayer::new())
@@ -73,36 +72,21 @@ async fn main() {
}
async fn user_info(
- _user: LoggedInUser,
- cookies: Cookies,
+ session: Option<AuthenticatedSession>,
extension: ContextExtension,
) -> Result<Json<Option<UserInfo>>, BridgeError> {
- let cookie = match cookies.get("user-id") {
+ let mut session = match session {
None => return Ok(Json(None)),
- Some(v) => v,
+ Some(s) => s,
};
-
- let session_id: SessionId = match SessionId::from_str(cookie.value()) {
- Err(e) => {
- info!("Clearing cookie that failed to parse {cookie:?}: {e}");
- cookies.remove(cookie.into_owned());
- return Ok(Json(None));
- }
- Ok(s) => s,
- };
- let mut session =
- match crate::auth::fetch_authenticated_session(&extension.db, &session_id).await? {
- None => return Ok(Json(None)),
- Some(v) => v,
- };
Ok(Json(Some(UserInfo {
username: extension.authenticator.user_info(&mut session).await?,
- table: get_table(&extension.db, &session).await?,
+ table: user_table(extension, &session).await?,
})))
}
-async fn get_table(
- db: &PgPool,
+async fn user_table(
+ extension: ContextExtension,
session: &AuthenticatedSession,
) -> Result<Option<Table>, BridgeError> {
Ok(sqlx::query_as!(
@@ -115,7 +99,7 @@ async fn get_table(
"#,
session.player_id
)
- .fetch_optional(db)
+ .fetch_optional(&extension.db)
.await?)
}
diff --git a/server/src/server.rs b/server/src/server.rs
new file mode 100644
index 0000000..eddba94
--- /dev/null
+++ b/server/src/server.rs
@@ -0,0 +1,13 @@
+use sqlx::PgPool;
+use std::sync::Arc;
+
+use axum::Extension;
+
+use crate::auth::Authenticator;
+
+pub struct ServerContext {
+ pub app_url: String,
+ pub authenticator: Authenticator,
+ pub db: PgPool,
+}
+pub type ContextExtension = Extension<Arc<ServerContext>>;