Start to build userinfo endpoint
This commit is contained in:
parent
27cee8d3a0
commit
b867016a71
@ -46,6 +46,7 @@ pub const TEMPORARY_PASSWORDS_LEN: usize = 20;
|
|||||||
pub const AUTHORIZE_URI: &str = "/openid/authorize";
|
pub const AUTHORIZE_URI: &str = "/openid/authorize";
|
||||||
pub const TOKEN_URI: &str = "/openid/token";
|
pub const TOKEN_URI: &str = "/openid/token";
|
||||||
pub const CERT_URI: &str = "/openid/jwks_uri";
|
pub const CERT_URI: &str = "/openid/jwks_uri";
|
||||||
|
pub const USERINFO_URI: &str = "/openid/userinfo";
|
||||||
|
|
||||||
/// Open ID constants
|
/// Open ID constants
|
||||||
pub const OPEN_ID_SESSION_CLEANUP_INTERVAL: Duration = Duration::from_secs(60);
|
pub const OPEN_ID_SESSION_CLEANUP_INTERVAL: Duration = Duration::from_secs(60);
|
||||||
|
@ -8,7 +8,7 @@ use askama::Template;
|
|||||||
|
|
||||||
use crate::actors::openid_sessions_actor;
|
use crate::actors::openid_sessions_actor;
|
||||||
use crate::actors::openid_sessions_actor::{OpenIDSessionsActor, Session, SessionID};
|
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::controllers::base_controller::FatalErrorPage;
|
||||||
use crate::data::app_config::AppConfig;
|
use crate::data::app_config::AppConfig;
|
||||||
use crate::data::client::{ClientID, ClientManager};
|
use crate::data::client::{ClientID, ClientManager};
|
||||||
@ -26,7 +26,7 @@ pub async fn get_configuration(app_conf: web::Data<AppConfig>) -> impl Responder
|
|||||||
issuer: app_conf.full_url("/"),
|
issuer: app_conf.full_url("/"),
|
||||||
authorization_endpoint: app_conf.full_url(AUTHORIZE_URI),
|
authorization_endpoint: app_conf.full_url(AUTHORIZE_URI),
|
||||||
token_endpoint: app_conf.full_url(TOKEN_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),
|
jwks_uri: app_conf.full_url(CERT_URI),
|
||||||
scopes_supported: vec!["openid", "profile", "email"],
|
scopes_supported: vec!["openid", "profile", "email"],
|
||||||
response_types_supported: vec!["code", "id_token", "token id_token"],
|
response_types_supported: vec!["code", "id_token", "token id_token"],
|
||||||
@ -388,3 +388,61 @@ struct CertsResponse {
|
|||||||
pub async fn cert_uri(jwt_signer: web::Data<JWTSigner>) -> impl Responder {
|
pub async fn cert_uri(jwt_signer: web::Data<JWTSigner>) -> impl Responder {
|
||||||
HttpResponse::Ok().json(CertsResponse { keys: vec![jwt_signer.get_json_web_key()] })
|
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<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn user_info_post(req: HttpRequest,
|
||||||
|
form: Option<web::Form<UserInfoQuery>>,
|
||||||
|
query: web::Query<UserInfoQuery>) -> 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<UserInfoQuery>) -> impl Responder {
|
||||||
|
user_info(req, query.0.access_token).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Authenticate request using RFC6750 <https://datatracker.ietf.org/doc/html/rfc6750>///
|
||||||
|
async fn user_info(req: HttpRequest, token: Option<String>) -> 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 <https://openid.net/specs/openid-connect-core-1_0.html#RFC6749>
|
||||||
|
HttpResponse::Ok().body(format!("token is {}", token))
|
||||||
|
}
|
@ -136,6 +136,8 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.route(AUTHORIZE_URI, web::get().to(openid_controller::authorize))
|
.route(AUTHORIZE_URI, web::get().to(openid_controller::authorize))
|
||||||
.route(TOKEN_URI, web::post().to(openid_controller::token))
|
.route(TOKEN_URI, web::post().to(openid_controller::token))
|
||||||
.route(CERT_URI, web::get().to(openid_controller::cert_uri))
|
.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)?
|
.bind(listen_address)?
|
||||||
.run()
|
.run()
|
||||||
|
@ -13,7 +13,7 @@ use actix_web::body::EitherBody;
|
|||||||
use actix_web::http::{header, Method};
|
use actix_web::http::{header, Method};
|
||||||
use askama::Template;
|
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::controllers::base_controller::{FatalErrorPage, redirect_user_for_login};
|
||||||
use crate::data::app_config::AppConfig;
|
use crate::data::app_config::AppConfig;
|
||||||
use crate::data::session_identity::{SessionIdentity, SessionIdentityData, SessionStatus};
|
use crate::data::session_identity::{SessionIdentity, SessionIdentityData, SessionStatus};
|
||||||
@ -91,7 +91,7 @@ impl<S, B> Service<ServiceRequest> for AuthInnerMiddleware<S>
|
|||||||
|
|
||||||
// Check if POST request comes from another website (block invalid origins)
|
// Check if POST request comes from another website (block invalid origins)
|
||||||
let origin = req.headers().get(header::ORIGIN);
|
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 let Some(o) = origin {
|
||||||
if !o.to_str().unwrap_or("bad").eq(&config.website_origin) {
|
if !o.to_str().unwrap_or("bad").eq(&config.website_origin) {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
|
Loading…
Reference in New Issue
Block a user