From b867016a7131c8ad872c7978c1a8d0c2cfb1648f Mon Sep 17 00:00:00 2001 From: Pierre Hubert Date: Thu, 14 Apr 2022 18:39:18 +0200 Subject: [PATCH] Start to build userinfo endpoint --- src/constants.rs | 1 + src/controllers/openid_controller.rs | 62 +++++++++++++++++++++++++++- src/main.rs | 2 + src/middlewares/auth_middleware.rs | 4 +- 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index 502f2c1..5882a50 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -46,6 +46,7 @@ pub const TEMPORARY_PASSWORDS_LEN: usize = 20; pub const AUTHORIZE_URI: &str = "/openid/authorize"; pub const TOKEN_URI: &str = "/openid/token"; pub const CERT_URI: &str = "/openid/jwks_uri"; +pub const USERINFO_URI: &str = "/openid/userinfo"; /// Open ID constants pub const OPEN_ID_SESSION_CLEANUP_INTERVAL: Duration = Duration::from_secs(60); diff --git a/src/controllers/openid_controller.rs b/src/controllers/openid_controller.rs index cf6327d..a9773f0 100644 --- a/src/controllers/openid_controller.rs +++ b/src/controllers/openid_controller.rs @@ -8,7 +8,7 @@ use askama::Template; use crate::actors::openid_sessions_actor; use crate::actors::openid_sessions_actor::{OpenIDSessionsActor, Session, SessionID}; -use crate::constants::{AUTHORIZE_URI, CERT_URI, OPEN_ID_ACCESS_TOKEN_LEN, OPEN_ID_ACCESS_TOKEN_TIMEOUT, OPEN_ID_AUTHORIZATION_CODE_LEN, OPEN_ID_AUTHORIZATION_CODE_TIMEOUT, OPEN_ID_REFRESH_TOKEN_LEN, OPEN_ID_REFRESH_TOKEN_TIMEOUT, OPEN_ID_SESSION_LEN, TOKEN_URI}; +use crate::constants::{AUTHORIZE_URI, CERT_URI, OPEN_ID_ACCESS_TOKEN_LEN, OPEN_ID_ACCESS_TOKEN_TIMEOUT, OPEN_ID_AUTHORIZATION_CODE_LEN, OPEN_ID_AUTHORIZATION_CODE_TIMEOUT, OPEN_ID_REFRESH_TOKEN_LEN, OPEN_ID_REFRESH_TOKEN_TIMEOUT, OPEN_ID_SESSION_LEN, TOKEN_URI, USERINFO_URI}; use crate::controllers::base_controller::FatalErrorPage; use crate::data::app_config::AppConfig; use crate::data::client::{ClientID, ClientManager}; @@ -26,7 +26,7 @@ pub async fn get_configuration(app_conf: web::Data) -> impl Responder issuer: app_conf.full_url("/"), authorization_endpoint: app_conf.full_url(AUTHORIZE_URI), token_endpoint: app_conf.full_url(TOKEN_URI), - userinfo_endpoint: app_conf.full_url("openid/userinfo"), + userinfo_endpoint: app_conf.full_url(USERINFO_URI), jwks_uri: app_conf.full_url(CERT_URI), scopes_supported: vec!["openid", "profile", "email"], response_types_supported: vec!["code", "id_token", "token id_token"], @@ -387,4 +387,62 @@ struct CertsResponse { pub async fn cert_uri(jwt_signer: web::Data) -> impl Responder { HttpResponse::Ok().json(CertsResponse { keys: vec![jwt_signer.get_json_web_key()] }) +} + +fn user_info_error(err: &str, description: &str) -> HttpResponse { + HttpResponse::Unauthorized() + .insert_header(("WWW-Authenticate", format!( + "Bearer error=\"{}\", error_description=\"{}\"", + err, + description + ))) + .finish() +} + +#[derive(serde::Deserialize)] +pub struct UserInfoQuery { + access_token: Option, +} + +pub async fn user_info_post(req: HttpRequest, + form: Option>, + query: web::Query) -> impl Responder { + user_info(req, + form + .map(|f| f.0.access_token) + .unwrap_or_default() + .or(query.0.access_token), + ).await +} + +pub async fn user_info_get(req: HttpRequest, query: web::Query) -> impl Responder { + user_info(req, query.0.access_token).await +} + +/// Authenticate request using RFC6750 /// +async fn user_info(req: HttpRequest, token: Option) -> impl Responder { + let token = match token { + Some(t) => t, + None => { + let token = match req.headers().get("Authorization") { + None => return user_info_error("invalid_request", "Missing access token!"), + Some(t) => t + }; + + let token = match token.to_str() { + Err(_) => return user_info_error("invalid_request", "Failed to decode token!"), + Ok(t) => t, + }; + + let token = match token.strip_prefix("Bearer ") { + None => return user_info_error("invalid_request", "Header token does not start with 'Bearer '!"), + Some(t) => t, + }; + + token.to_string() + } + }; + + // TODO : continue + HttpResponse::Ok().body(format!("token is {}", token)) } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 49f185f..33ce497 100644 --- a/src/main.rs +++ b/src/main.rs @@ -136,6 +136,8 @@ async fn main() -> std::io::Result<()> { .route(AUTHORIZE_URI, web::get().to(openid_controller::authorize)) .route(TOKEN_URI, web::post().to(openid_controller::token)) .route(CERT_URI, web::get().to(openid_controller::cert_uri)) + .route(USERINFO_URI, web::post().to(openid_controller::user_info_post)) + .route(USERINFO_URI, web::get().to(openid_controller::user_info_get)) }) .bind(listen_address)? .run() diff --git a/src/middlewares/auth_middleware.rs b/src/middlewares/auth_middleware.rs index cbe85c3..50944ae 100644 --- a/src/middlewares/auth_middleware.rs +++ b/src/middlewares/auth_middleware.rs @@ -13,7 +13,7 @@ use actix_web::body::EitherBody; use actix_web::http::{header, Method}; use askama::Template; -use crate::constants::{ADMIN_ROUTES, AUTHENTICATED_ROUTES, AUTHORIZE_URI, TOKEN_URI}; +use crate::constants::{ADMIN_ROUTES, AUTHENTICATED_ROUTES, AUTHORIZE_URI, TOKEN_URI, USERINFO_URI}; use crate::controllers::base_controller::{FatalErrorPage, redirect_user_for_login}; use crate::data::app_config::AppConfig; use crate::data::session_identity::{SessionIdentity, SessionIdentityData, SessionStatus}; @@ -91,7 +91,7 @@ impl Service for AuthInnerMiddleware // Check if POST request comes from another website (block invalid origins) let origin = req.headers().get(header::ORIGIN); - if req.method() == Method::POST && req.path() != TOKEN_URI { + if req.method() == Method::POST && req.path() != TOKEN_URI && req.path() != USERINFO_URI { if let Some(o) = origin { if !o.to_str().unwrap_or("bad").eq(&config.website_origin) { log::warn!(