oauth2_client/device_authorization_grant/
flow.rs

1use http_api_client::{
2    Client, ClientRespondEndpointError, RetryableClient,
3    RetryableClientRespondEndpointUntilDoneError,
4};
5use oauth2_core::{
6    device_authorization_grant::{
7        device_access_token_response::{
8            ErrorBody as DAT_RES_ErrorBody, SuccessfulBody as DAT_RES_SuccessfulBody,
9        },
10        device_authorization_response::{
11            ErrorBody as DA_RES_ErrorBody, UserCode, VerificationUri, VerificationUriComplete,
12        },
13    },
14    serde::{de::DeserializeOwned, Serialize},
15    types::Scope,
16};
17
18use crate::ProviderExtDeviceAuthorizationGrant;
19
20use super::{
21    DeviceAccessTokenEndpoint, DeviceAccessTokenEndpointError, DeviceAuthorizationEndpoint,
22    DeviceAuthorizationEndpointError,
23};
24
25//
26//
27//
28#[derive(Debug, Clone)]
29pub struct Flow<C1, C2>
30where
31    C1: Client,
32    C2: RetryableClient,
33{
34    pub client_with_auth: C1,
35    pub client_with_token: C2,
36}
37impl<C1, C2> Flow<C1, C2>
38where
39    C1: Client,
40    C2: RetryableClient,
41{
42    pub fn new(client_with_auth: C1, client_with_token: C2) -> Self {
43        Self {
44            client_with_auth,
45            client_with_token,
46        }
47    }
48}
49
50impl<C1, C2> Flow<C1, C2>
51where
52    C1: Client + Send + Sync,
53    C2: RetryableClient + Send + Sync,
54{
55    pub async fn execute<SCOPE, UI>(
56        &self,
57        provider: &(dyn ProviderExtDeviceAuthorizationGrant<Scope = SCOPE> + Send + Sync),
58        scopes: impl Into<Option<Vec<SCOPE>>>,
59        user_interaction: UI,
60    ) -> Result<DAT_RES_SuccessfulBody<SCOPE>, FlowExecuteError>
61    where
62        SCOPE: Scope + Serialize + DeserializeOwned + Send + Sync,
63        UI: FnOnce(UserCode, VerificationUri, Option<VerificationUriComplete>),
64    {
65        // Step 1
66        let scopes = scopes.into().or_else(|| provider.scopes_default());
67
68        let device_authorization_endpoint = DeviceAuthorizationEndpoint::new(provider, scopes);
69
70        let device_authorization_ret = self
71            .client_with_auth
72            .respond_endpoint(&device_authorization_endpoint)
73            .await
74            .map_err(|err| match err {
75                ClientRespondEndpointError::RespondFailed(err) => {
76                    FlowExecuteError::DeviceAuthorizationEndpointRespondFailed(Box::new(err))
77                }
78                ClientRespondEndpointError::EndpointRenderRequestFailed(err) => {
79                    FlowExecuteError::DeviceAuthorizationEndpointError(err)
80                }
81                ClientRespondEndpointError::EndpointParseResponseFailed(err) => {
82                    FlowExecuteError::DeviceAuthorizationEndpointError(err)
83                }
84            })?;
85
86        let device_authorization_successful_body =
87            device_authorization_ret.map_err(FlowExecuteError::DeviceAuthorizationFailed)?;
88
89        // Step 2
90        user_interaction(
91            device_authorization_successful_body.user_code.to_owned(),
92            device_authorization_successful_body
93                .verification_uri
94                .to_owned(),
95            device_authorization_successful_body
96                .verification_uri_complete
97                .to_owned(),
98        );
99
100        // Step 3
101        let device_access_token_endpoint =
102            DeviceAccessTokenEndpoint::new(provider, device_authorization_successful_body);
103
104        let device_access_token_ret = self
105            .client_with_token
106            .respond_endpoint_until_done(&device_access_token_endpoint)
107            .await
108            .map_err(|err| match err {
109                RetryableClientRespondEndpointUntilDoneError::RespondFailed(err) => {
110                    FlowExecuteError::DeviceAccessTokenEndpointRespondFailed(Box::new(err))
111                }
112                RetryableClientRespondEndpointUntilDoneError::EndpointRenderRequestFailed(err) => {
113                    FlowExecuteError::DeviceAccessTokenEndpointError(err)
114                }
115                RetryableClientRespondEndpointUntilDoneError::EndpointParseResponseFailed(err) => {
116                    FlowExecuteError::DeviceAccessTokenEndpointError(err)
117                }
118                RetryableClientRespondEndpointUntilDoneError::ReachedMaxRetries => {
119                    FlowExecuteError::DeviceAccessTokenEndpointErrorWithReachedMaxRetries
120                }
121            })?;
122
123        let device_access_token_successful_body =
124            device_access_token_ret.map_err(FlowExecuteError::DeviceAccessTokenFailed)?;
125
126        Ok(device_access_token_successful_body)
127    }
128}
129
130#[derive(thiserror::Error, Debug)]
131pub enum FlowExecuteError {
132    #[error("DeviceAuthorizationEndpointRespondFailed {0}")]
133    DeviceAuthorizationEndpointRespondFailed(Box<dyn std::error::Error + Send + Sync>),
134    #[error("DeviceAuthorizationEndpointError {0}")]
135    DeviceAuthorizationEndpointError(DeviceAuthorizationEndpointError),
136    #[error("DeviceAuthorizationFailed {0:?}")]
137    DeviceAuthorizationFailed(DA_RES_ErrorBody),
138    //
139    #[error("DeviceAccessTokenEndpointRespondFailed {0}")]
140    DeviceAccessTokenEndpointRespondFailed(Box<dyn std::error::Error + Send + Sync>),
141    #[error("DeviceAccessTokenEndpointError {0}")]
142    DeviceAccessTokenEndpointError(DeviceAccessTokenEndpointError),
143    #[error("DeviceAccessTokenEndpointErrorWithReachedMaxRetries")]
144    DeviceAccessTokenEndpointErrorWithReachedMaxRetries,
145    #[error("DeviceAccessTokenFailed {0:?}")]
146    DeviceAccessTokenFailed(DAT_RES_ErrorBody),
147}