raos/authorize/
request.rs1use crate::common::{
2 frontend::{FrontendRequest, FrontendRequestMethod, OAuthValidationError},
3 model::CodeChallenge,
4 syntax::{ValidateSyntax, CLIENT_ID_SYNTAX, STATE_SYNTAX},
5 util::NoneIfEmpty,
6};
7
8#[derive(Debug, PartialEq)]
10pub enum ResponseType {
11 Code,
13}
14
15#[derive(Debug)]
19pub struct AuthorizationRequest {
20 pub response_type: ResponseType,
22 pub client_id: String,
24 pub code_challenge: CodeChallenge,
26 pub redirect_uri: Option<String>,
28 pub scope: Option<String>,
30 pub state: Option<String>,
32}
33
34impl TryFrom<&dyn FrontendRequest> for AuthorizationRequest {
35 type Error = OAuthValidationError;
36
37 fn try_from(request: &dyn FrontendRequest) -> Result<Self, Self::Error> {
38 if !matches!(
39 request.request_method(),
40 FrontendRequestMethod::GET | FrontendRequestMethod::POST
41 ) {
42 return Err(OAuthValidationError::InvalidRequestMethod {
43 expected: FrontendRequestMethod::GET,
44 actual: request.request_method(),
45 });
46 }
47
48 let param = |key| {
50 request.query_param(key).none_if_empty().or_else(|| {
51 if let FrontendRequestMethod::POST = request.request_method() {
52 request.body_param(key).none_if_empty()
53 } else {
54 None
55 }
56 })
57 };
58
59 let response_type = match param("response_type") {
61 Some(str) => str.try_into()?,
62 None => return Err(OAuthValidationError::MissingRequiredParameter("response_type")),
63 };
64 let Some(client_id) = param("client_id") else {
65 return Err(OAuthValidationError::MissingRequiredParameter("client_id"));
66 };
67 client_id.validate_syntax("client_id", &CLIENT_ID_SYNTAX)?;
68 let code_challenge =
69 (param("code_challenge"), param("code_challenge_method")).try_into()?;
70
71 let state = param("state");
72 state.validate_syntax("state", &STATE_SYNTAX)?;
73
74 Ok(Self {
76 response_type,
77 client_id,
78 code_challenge,
79 state,
80 redirect_uri: param("redirect_uri"),
81 scope: param("scope"),
82 })
83 }
84}
85
86impl TryFrom<String> for ResponseType {
87 type Error = OAuthValidationError;
88
89 fn try_from(value: String) -> Result<Self, Self::Error> {
90 Ok(match value.to_lowercase().as_str() {
92 "code" => Self::Code,
93 _ => {
94 return Err(OAuthValidationError::InvalidParameterValue(
95 "response_type",
96 value.to_string(),
97 ))
98 }
99 })
100 }
101}
102
103impl TryFrom<(Option<String>, Option<String>)> for CodeChallenge {
104 type Error = OAuthValidationError;
105
106 fn try_from(
107 (code_challenge, code_challenge_method): (Option<String>, Option<String>),
108 ) -> Result<Self, Self::Error> {
109 let Some(code_challenge) = code_challenge else {
110 return Ok(Self::None);
111 };
112 let Some(method) = code_challenge_method else {
113 return Ok(Self::Plain { code_challenge });
114 };
115
116 Ok(match method.to_lowercase().as_str() {
118 "plain" => Self::Plain { code_challenge },
119 "s256" => Self::S256 { code_challenge },
120 _ => {
121 return Err(OAuthValidationError::InvalidParameterValue(
122 "code_challenge_method",
123 method,
124 ))
125 }
126 })
127 }
128}