Userinfo endpoint is working
This commit is contained in:
		| @@ -50,6 +50,10 @@ pub struct FindSessionByAuthorizationCode(pub String); | ||||
| #[rtype(result = "Option<Session>")] | ||||
| pub struct FindSessionByRefreshToken(pub String); | ||||
|  | ||||
| #[derive(Message)] | ||||
| #[rtype(result = "Option<Session>")] | ||||
| pub struct FindSessionByAccessToken(pub String); | ||||
|  | ||||
| #[derive(Message)] | ||||
| #[rtype(result = "()")] | ||||
| pub struct MarkAuthorizationCodeUsed(pub String); | ||||
| @@ -111,6 +115,17 @@ impl Handler<FindSessionByRefreshToken> for OpenIDSessionsActor { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Handler<FindSessionByAccessToken> for OpenIDSessionsActor { | ||||
|     type Result = Option<Session>; | ||||
|  | ||||
|     fn handle(&mut self, msg: FindSessionByAccessToken, _ctx: &mut Self::Context) -> Self::Result { | ||||
|         self.session | ||||
|             .iter() | ||||
|             .find(|f| f.access_token.eq(&msg.0)) | ||||
|             .cloned() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Handler<MarkAuthorizationCodeUsed> for OpenIDSessionsActor { | ||||
|     type Result = (); | ||||
|  | ||||
|   | ||||
| @@ -6,8 +6,9 @@ use actix_web::{HttpRequest, HttpResponse, Responder, web}; | ||||
| use actix_web::error::ErrorUnauthorized; | ||||
| use askama::Template; | ||||
|  | ||||
| use crate::actors::openid_sessions_actor; | ||||
| use crate::actors::{openid_sessions_actor, users_actor}; | ||||
| use crate::actors::openid_sessions_actor::{OpenIDSessionsActor, Session, SessionID}; | ||||
| use crate::actors::users_actor::UsersActor; | ||||
| 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; | ||||
| @@ -16,8 +17,10 @@ use crate::data::code_challenge::CodeChallenge; | ||||
| use crate::data::current_user::CurrentUser; | ||||
| use crate::data::id_token::IdToken; | ||||
| use crate::data::jwt_signer::{JsonWebKey, JWTSigner}; | ||||
| use crate::data::open_id_user_info::OpenIDUserInfo; | ||||
| use crate::data::openid_config::OpenIDConfig; | ||||
| use crate::data::session_identity::SessionIdentity; | ||||
| use crate::data::user::User; | ||||
| use crate::utils::string_utils::rand_str; | ||||
| use crate::utils::time::time; | ||||
|  | ||||
| @@ -406,21 +409,29 @@ pub struct UserInfoQuery { | ||||
|  | ||||
| pub async fn user_info_post(req: HttpRequest, | ||||
|                             form: Option<web::Form<UserInfoQuery>>, | ||||
|                             query: web::Query<UserInfoQuery>) -> impl Responder { | ||||
|                             query: web::Query<UserInfoQuery>, | ||||
|                             sessions: web::Data<Addr<OpenIDSessionsActor>>, | ||||
|                             users: web::Data<Addr<UsersActor>>) -> impl Responder { | ||||
|     user_info(req, | ||||
|               form | ||||
|                   .map(|f| f.0.access_token) | ||||
|                   .unwrap_or_default() | ||||
|                   .or(query.0.access_token), | ||||
|               sessions, | ||||
|               users, | ||||
|     ).await | ||||
| } | ||||
|  | ||||
| pub async fn user_info_get(req: HttpRequest, query: web::Query<UserInfoQuery>) -> impl Responder { | ||||
|     user_info(req, query.0.access_token).await | ||||
| pub async fn user_info_get(req: HttpRequest, query: web::Query<UserInfoQuery>, | ||||
|                            sessions: web::Data<Addr<OpenIDSessionsActor>>, | ||||
|                            users: web::Data<Addr<UsersActor>>) -> impl Responder { | ||||
|     user_info(req, query.0.access_token, sessions, users).await | ||||
| } | ||||
|  | ||||
| /// Authenticate request using RFC6750 <https://datatracker.ietf.org/doc/html/rfc6750>/// | ||||
| async fn user_info(req: HttpRequest, token: Option<String>) -> impl Responder { | ||||
| async fn user_info(req: HttpRequest, token: Option<String>, | ||||
|                    sessions: web::Data<Addr<OpenIDSessionsActor>>, | ||||
|                    users: web::Data<Addr<UsersActor>>) -> impl Responder { | ||||
|     let token = match token { | ||||
|         Some(t) => t, | ||||
|         None => { | ||||
| @@ -443,6 +454,35 @@ async fn user_info(req: HttpRequest, token: Option<String>) -> impl Responder { | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     // TODO : continue <https://openid.net/specs/openid-connect-core-1_0.html#RFC6749> | ||||
|     HttpResponse::Ok().body(format!("token is {}", token)) | ||||
|     let session: Option<Session> = sessions | ||||
|         .send(openid_sessions_actor::FindSessionByAccessToken(token)).await.unwrap(); | ||||
|     let session = match session { | ||||
|         None => { | ||||
|             return user_info_error("invalid_request", "Session not found!"); | ||||
|         } | ||||
|         Some(s) => s, | ||||
|     }; | ||||
|  | ||||
|     if session.access_token_expire_at < time() { | ||||
|         return user_info_error("invalid_request", "Access token has expired!"); | ||||
|     } | ||||
|  | ||||
|     let user: Option<User> = users.send(users_actor::GetUserRequest(session.user)).await.unwrap().0; | ||||
|     let user = match user { | ||||
|         None => { | ||||
|             return user_info_error("invalid_request", "Failed to extract user information!"); | ||||
|         } | ||||
|         Some(u) => u, | ||||
|     }; | ||||
|  | ||||
|     HttpResponse::Ok() | ||||
|         .json(OpenIDUserInfo { | ||||
|             name: user.full_name(), | ||||
|             sub: user.uid, | ||||
|             given_name: user.first_name, | ||||
|             family_name: user.last_name, | ||||
|             preferred_username: user.username, | ||||
|             email: user.email, | ||||
|             email_verified: true, | ||||
|         }) | ||||
| } | ||||
| @@ -8,4 +8,5 @@ pub mod current_user; | ||||
| pub mod openid_config; | ||||
| pub mod jwt_signer; | ||||
| pub mod id_token; | ||||
| pub mod code_challenge; | ||||
| pub mod code_challenge; | ||||
| pub mod open_id_user_info; | ||||
							
								
								
									
										24
									
								
								src/data/open_id_user_info.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/data/open_id_user_info.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| /// Refer to <https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims> for more information | ||||
| #[derive(Debug, serde::Serialize)] | ||||
| pub struct OpenIDUserInfo { | ||||
|     /// Subject - Identifier for the End-User at the Issuer | ||||
|     pub sub: String, | ||||
|  | ||||
|     ///  End-User's full name in displayable form including all name parts, possibly including titles and suffixes, ordered according to the End-User's locale and preferences. | ||||
|     pub name: String, | ||||
|  | ||||
|     /// Given name(s) or first name(s) of the End-User. Note that in some cultures, people can have multiple given names; all can be present, with the names being separated by space characters. | ||||
|     pub given_name: String, | ||||
|  | ||||
|     ///  Surname(s) or last name(s) of the End-User. Note that in some cultures, people can have multiple family names or no family name; all can be present, with the names being separated by space characters. | ||||
|     pub family_name: String, | ||||
|  | ||||
|     /// Shorthand name by which the End-User wishes to be referred to at the RP, such as janedoe or j.doe. This value MAY be any valid JSON string including special characters such as @, /, or whitespace. The RP MUST NOT rely upon this value being unique, as discussed in | ||||
|     pub preferred_username: String, | ||||
|  | ||||
|     ///  End-User's preferred e-mail address. Its value MUST conform to the RFC 5322 RFC5322 addr-spec syntax. The RP MUST NOT rely upon this value being unique, as discussed in Section 5.7. | ||||
|     pub email: String, | ||||
|  | ||||
|     ///  True if the End-User's e-mail address has been verified; otherwise false. When this Claim Value is true, this means that the OP took affirmative steps to ensure that this e-mail address was controlled by the End-User at the time the verification was performed. The means by which an e-mail address is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating. | ||||
|     pub email_verified: bool, | ||||
| } | ||||
		Reference in New Issue
	
	Block a user