yew_oauth2/components/context/
mod.rs

1//! The main, wrapping [`OAuth2`] component
2
3mod agent;
4
5pub use agent::*;
6
7use crate::{
8    agent::{AgentConfiguration, Client, LoginOptions, LogoutOptions, OAuth2Operations},
9    context::{LatestAccessToken, OAuth2Context},
10};
11use agent::Agent as AgentContext;
12use std::time::Duration;
13use yew::prelude::*;
14
15/// Properties for the context component.
16#[derive(Clone, Debug, Properties)]
17pub struct OAuth2Properties<C: Client> {
18    /// The client configuration
19    pub config: C::Configuration,
20
21    /// Scopes to request for the session
22    #[prop_or_default]
23    pub scopes: Vec<String>,
24
25    /// The grace period for the session timeout
26    ///
27    /// The amount of time before the token expiration when the agent will refresh it.
28    #[prop_or(Duration::from_secs(30))]
29    pub grace_period: Duration,
30
31    /// A maximum expiration time.
32    ///
33    /// This can be used to limit the token timeout. If present, the token will be considered
34    /// expired at the provided expiration or the configured maximum expiration, whatever is
35    /// first.
36    #[prop_or_default]
37    pub max_expiration: Option<Duration>,
38
39    // The audience to be associated to the access tokens inside this context
40    #[prop_or_default]
41    pub audience: Option<String>,
42
43    /// Children which will have access to the [`OAuth2Context`].
44    #[prop_or_default]
45    pub children: Children,
46
47    /// Default [`LoginOptions`] that will be used unless more specific options have been requested.
48    #[prop_or_default]
49    pub login_options: Option<LoginOptions>,
50
51    /// Default [`LogoutOptions`] that will be used unless more specific options have been requested.
52    #[prop_or_default]
53    pub logout_options: Option<LogoutOptions>,
54}
55
56impl<C: Client> PartialEq for OAuth2Properties<C> {
57    fn eq(&self, other: &Self) -> bool {
58        self.config == other.config
59            && self.scopes == other.scopes
60            && self.grace_period == other.grace_period
61            && self.max_expiration == other.max_expiration
62            && self.audience == other.audience
63            && self.children == other.children
64    }
65}
66
67/// Yew component providing the OAuth2 context and configuring the agent.
68///
69/// All items making using of the OAuth2 or OpenID Connect context must be below this element.
70pub struct OAuth2<C: Client> {
71    context: OAuth2Context,
72    latest_access_token: LatestAccessToken,
73    agent: AgentContext<C>,
74    config: AgentConfiguration<C>,
75}
76
77#[doc(hidden)]
78pub enum Msg {
79    Context(OAuth2Context),
80}
81
82impl<C: Client> Component for OAuth2<C> {
83    type Message = Msg;
84    type Properties = OAuth2Properties<C>;
85
86    fn create(ctx: &Context<Self>) -> Self {
87        let config = Self::make_config(ctx.props());
88        let callback = ctx.link().callback(Msg::Context);
89
90        let agent = crate::agent::Agent::new(move |s| callback.emit(s));
91        let _ = agent.configure(config.clone());
92
93        Self {
94            context: OAuth2Context::NotInitialized,
95            latest_access_token: LatestAccessToken {
96                access_token: Default::default(),
97            },
98            agent: AgentContext::new(agent),
99            config,
100        }
101    }
102
103    fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
104        match msg {
105            Self::Message::Context(context) => {
106                if self.context != context {
107                    self.latest_access_token
108                        .set_access_token(context.access_token());
109                    self.context = context;
110                    return true;
111                }
112            }
113        }
114        false
115    }
116
117    fn changed(&mut self, ctx: &Context<Self>, _: &Self::Properties) -> bool {
118        let config = Self::make_config(ctx.props());
119        if self.config != config {
120            // only reconfigure agent when necessary
121            let _ = self.agent.configure(config.clone());
122            self.config = config;
123        }
124
125        true
126    }
127
128    fn view(&self, ctx: &Context<Self>) -> Html {
129        html!(
130            <>
131                <ContextProvider<OAuth2Context> context={self.context.clone()} >
132                    <ContextProvider<AgentContext<C>> context={self.agent.clone()}>
133                        <ContextProvider<LatestAccessToken> context={self.latest_access_token.clone()}>
134                            { for ctx.props().children.iter() }
135                        </ContextProvider<LatestAccessToken>>
136                    </ContextProvider<AgentContext<C>>>
137                </ContextProvider<OAuth2Context>>
138            </>
139        )
140    }
141}
142
143impl<C: Client> OAuth2<C> {
144    fn make_config(props: &OAuth2Properties<C>) -> AgentConfiguration<C> {
145        AgentConfiguration {
146            config: props.config.clone(),
147            scopes: props.scopes.clone(),
148            grace_period: props.grace_period,
149            max_expiration: props.max_expiration,
150            audience: props.audience.clone(),
151            default_login_options: props.login_options.clone(),
152            default_logout_options: props.logout_options.clone(),
153        }
154    }
155}
156
157#[cfg(feature = "openid")]
158pub mod openid {
159    //! Convenient access to OpenID Connect context
160    pub type OAuth2 = super::OAuth2<crate::agent::client::OpenIdClient>;
161}
162
163pub mod oauth2 {
164    //! Convenient access to OAuth2 context
165    pub type OAuth2 = super::OAuth2<crate::agent::client::OAuth2Client>;
166}