rustauth_oauth/oauth2/
validate_authorization_code.rs1use std::collections::BTreeMap;
2
3use super::error::OAuthError;
4use super::request::{
5 apply_client_authentication, is_protected_oauth_param, ClientAuthentication, OAuthFormRequest,
6};
7use super::tokens::ProviderOptions;
8use super::utils::validate_code_verifier;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct AuthorizationCodeRequest {
12 pub code: String,
13 pub redirect_uri: String,
14 pub options: ProviderOptions,
15 pub code_verifier: Option<String>,
16 pub device_id: Option<String>,
17 pub authentication: ClientAuthentication,
18 pub headers: BTreeMap<String, String>,
19 pub additional_params: BTreeMap<String, String>,
20 pub override_params: BTreeMap<String, String>,
21 pub resource: Vec<String>,
22}
23
24impl Default for AuthorizationCodeRequest {
25 fn default() -> Self {
26 Self {
27 code: String::new(),
28 redirect_uri: String::new(),
29 options: ProviderOptions::default(),
30 code_verifier: None,
31 device_id: None,
32 authentication: ClientAuthentication::Post,
33 headers: BTreeMap::new(),
34 additional_params: BTreeMap::new(),
35 override_params: BTreeMap::new(),
36 resource: Vec::new(),
37 }
38 }
39}
40
41impl AuthorizationCodeRequest {
42 pub fn try_new(
43 code: impl Into<String>,
44 redirect_uri: impl Into<String>,
45 options: ProviderOptions,
46 ) -> Result<Self, OAuthError> {
47 let code = code.into();
48 if code.is_empty() {
49 return Err(OAuthError::InvalidConfiguration(
50 "authorization code cannot be empty".to_owned(),
51 ));
52 }
53 let redirect_uri = redirect_uri.into();
54 url::Url::parse(options.redirect_uri.as_deref().unwrap_or(&redirect_uri))?;
55 Ok(Self {
56 code,
57 redirect_uri,
58 options,
59 ..Self::default()
60 })
61 }
62
63 pub fn code_verifier(mut self, code_verifier: impl Into<String>) -> Self {
64 self.code_verifier = Some(code_verifier.into());
65 self
66 }
67
68 pub fn authentication(mut self, authentication: ClientAuthentication) -> Self {
69 self.authentication = authentication;
70 self
71 }
72
73 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
74 self.headers.insert(key.into(), value.into());
75 self
76 }
77
78 pub fn additional_param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
82 self.additional_params.insert(key.into(), value.into());
83 self
84 }
85
86 pub fn override_param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
91 self.override_params.insert(key.into(), value.into());
92 self
93 }
94
95 pub fn resource(mut self, resource: impl Into<String>) -> Self {
96 self.resource.push(resource.into());
97 self
98 }
99}
100
101pub fn create_authorization_code_request(
102 input: AuthorizationCodeRequest,
103) -> Result<OAuthFormRequest, OAuthError> {
104 validate_authorization_code_request(&input)?;
105 let mut request = OAuthFormRequest::new();
106 for (key, value) in input.headers {
107 request.set_header(key, value);
108 }
109 request.set_body("grant_type", "authorization_code");
110 request.set_body("code", input.code);
111 if let Some(code_verifier) = input.code_verifier {
112 request.set_body("code_verifier", code_verifier);
113 }
114 if let Some(client_key) = &input.options.client_key {
115 request.set_body("client_key", client_key);
116 }
117 if let Some(device_id) = input.device_id {
118 request.set_body("device_id", device_id);
119 }
120 request.set_body(
121 "redirect_uri",
122 input
123 .options
124 .redirect_uri
125 .as_deref()
126 .unwrap_or(&input.redirect_uri),
127 );
128 for resource in input.resource {
129 request.push_body("resource", resource);
130 }
131 apply_client_authentication(&mut request, &input.options, input.authentication, false)?;
132 for (key, value) in input.additional_params {
133 if is_protected_oauth_param(&key) || request.has_body(&key) {
134 continue;
135 }
136 request.push_body(key, value);
137 }
138 for (key, value) in input.override_params {
139 if is_protected_oauth_param(&key) {
140 continue;
141 }
142 request.set_body(key, value);
143 }
144 Ok(request)
145}
146
147fn validate_authorization_code_request(input: &AuthorizationCodeRequest) -> Result<(), OAuthError> {
148 if input.code.is_empty() {
149 return Err(OAuthError::InvalidConfiguration(
150 "authorization code cannot be empty".to_owned(),
151 ));
152 }
153 let redirect_uri = input
154 .options
155 .redirect_uri
156 .as_deref()
157 .unwrap_or(&input.redirect_uri);
158 url::Url::parse(redirect_uri)?;
159 if let Some(code_verifier) = &input.code_verifier {
160 validate_code_verifier(code_verifier)?;
161 }
162 Ok(())
163}