Skip to main content

rustauth_oauth/oauth2/
refresh_access_token.rs

1use 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;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct RefreshAccessTokenRequest {
11    pub refresh_token: String,
12    pub options: ProviderOptions,
13    pub authentication: ClientAuthentication,
14    pub headers: BTreeMap<String, String>,
15    pub extra_params: BTreeMap<String, String>,
16    pub resource: Vec<String>,
17}
18
19impl Default for RefreshAccessTokenRequest {
20    fn default() -> Self {
21        Self {
22            refresh_token: String::new(),
23            options: ProviderOptions::default(),
24            authentication: ClientAuthentication::Post,
25            headers: BTreeMap::new(),
26            extra_params: BTreeMap::new(),
27            resource: Vec::new(),
28        }
29    }
30}
31
32impl RefreshAccessTokenRequest {
33    pub fn try_new(
34        refresh_token: impl Into<String>,
35        options: ProviderOptions,
36    ) -> Result<Self, OAuthError> {
37        let refresh_token = refresh_token.into();
38        if refresh_token.is_empty() {
39            return Err(OAuthError::MissingTokenField("refresh_token"));
40        }
41        Ok(Self {
42            refresh_token,
43            options,
44            ..Self::default()
45        })
46    }
47
48    pub fn authentication(mut self, authentication: ClientAuthentication) -> Self {
49        self.authentication = authentication;
50        self
51    }
52
53    pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
54        self.headers.insert(key.into(), value.into());
55        self
56    }
57
58    /// Adds a non-sensitive extension form field such as `scope`.
59    ///
60    /// This is an extension-only API: security-critical keys (`grant_type`,
61    /// `refresh_token`, and client credential/authentication fields) and any
62    /// field already set by the builder are ignored when the request is built,
63    /// so a provider extension or caller-controlled value cannot replace
64    /// validated flow invariants or authenticated client credentials.
65    pub fn extra_param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
66        self.extra_params.insert(key.into(), value.into());
67        self
68    }
69
70    pub fn resource(mut self, resource: impl Into<String>) -> Self {
71        self.resource.push(resource.into());
72        self
73    }
74}
75
76pub fn create_refresh_access_token_request(
77    input: RefreshAccessTokenRequest,
78) -> Result<OAuthFormRequest, OAuthError> {
79    validate_refresh_access_token_request(&input)?;
80    let mut request = OAuthFormRequest::new();
81    for (key, value) in input.headers {
82        request.set_header(key, value);
83    }
84    request.set_body("grant_type", "refresh_token");
85    request.set_body("refresh_token", input.refresh_token);
86    if let Some(client_key) = &input.options.client_key {
87        request.set_body("client_key", client_key);
88    }
89    apply_client_authentication(&mut request, &input.options, input.authentication, false)?;
90    for resource in input.resource {
91        request.push_body("resource", resource);
92    }
93    for (key, value) in input.extra_params {
94        if is_protected_oauth_param(&key) || request.has_body(&key) {
95            continue;
96        }
97        request.push_body(key, value);
98    }
99    Ok(request)
100}
101
102fn validate_refresh_access_token_request(
103    input: &RefreshAccessTokenRequest,
104) -> Result<(), OAuthError> {
105    if input.refresh_token.is_empty() {
106        return Err(OAuthError::MissingTokenField("refresh_token"));
107    }
108    Ok(())
109}