Skip to main content

gestalt/
auth.rs

1use std::time::Duration;
2
3use tonic::codegen::async_trait;
4
5use crate::api::RuntimeMetadata;
6use crate::error::{Error, Result};
7
8/// Normalized user identity returned by an authentication provider.
9#[derive(Clone, Debug, Default, PartialEq, Eq)]
10pub struct AuthenticatedUser {
11    /// The `subject` field.
12    pub subject: String,
13    /// The `email` field.
14    pub email: String,
15    /// The `email_verified` field.
16    pub email_verified: bool,
17    /// The `display_name` field.
18    pub display_name: String,
19    /// The `avatar_url` field.
20    pub avatar_url: String,
21    /// The `claims` field.
22    pub claims: std::collections::BTreeMap<String, String>,
23}
24
25/// Starts an interactive login flow.
26#[derive(Clone, Debug, Default, PartialEq, Eq)]
27pub struct BeginLoginRequest {
28    /// The `callback_url` field.
29    pub callback_url: String,
30    /// The `host_state` field.
31    pub host_state: String,
32    /// The `scopes` field.
33    pub scopes: Vec<String>,
34    /// The `options` field.
35    pub options: std::collections::BTreeMap<String, String>,
36}
37
38/// Provider-managed authorization URL and opaque state for login completion.
39#[derive(Clone, Debug, Default, PartialEq, Eq)]
40pub struct BeginLoginResponse {
41    /// The `authorization_url` field.
42    pub authorization_url: String,
43    /// The `provider_state` field.
44    pub provider_state: Vec<u8>,
45}
46
47/// Finishes an interactive login flow.
48#[derive(Clone, Debug, Default, PartialEq, Eq)]
49pub struct CompleteLoginRequest {
50    /// The `query` field.
51    pub query: std::collections::BTreeMap<String, String>,
52    /// The `provider_state` field.
53    pub provider_state: Vec<u8>,
54    /// The `callback_url` field.
55    pub callback_url: String,
56}
57
58/// Host persistence settings for authenticated sessions.
59#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
60pub struct AuthSessionSettings {
61    /// The `session_ttl` field.
62    pub session_ttl: Duration,
63}
64
65pub(crate) fn begin_login_request_from_proto(
66    value: crate::generated::v1::BeginLoginRequest,
67) -> BeginLoginRequest {
68    BeginLoginRequest {
69        callback_url: value.callback_url,
70        host_state: value.host_state,
71        scopes: value.scopes,
72        options: value.options,
73    }
74}
75
76pub(crate) fn begin_login_response_to_proto(
77    value: BeginLoginResponse,
78) -> crate::generated::v1::BeginLoginResponse {
79    crate::generated::v1::BeginLoginResponse {
80        authorization_url: value.authorization_url,
81        provider_state: value.provider_state,
82    }
83}
84
85pub(crate) fn complete_login_request_from_proto(
86    value: crate::generated::v1::CompleteLoginRequest,
87) -> CompleteLoginRequest {
88    CompleteLoginRequest {
89        query: value.query,
90        provider_state: value.provider_state,
91        callback_url: value.callback_url,
92    }
93}
94
95pub(crate) fn authenticated_user_to_proto(
96    value: AuthenticatedUser,
97) -> crate::generated::v1::AuthenticatedUser {
98    crate::generated::v1::AuthenticatedUser {
99        subject: value.subject,
100        email: value.email,
101        email_verified: value.email_verified,
102        display_name: value.display_name,
103        avatar_url: value.avatar_url,
104        claims: value.claims,
105    }
106}
107
108pub(crate) fn auth_session_settings_to_proto(
109    value: AuthSessionSettings,
110) -> crate::generated::v1::AuthSessionSettings {
111    crate::generated::v1::AuthSessionSettings {
112        session_ttl_seconds: i64::try_from(value.session_ttl.as_secs()).unwrap_or(i64::MAX),
113    }
114}
115
116#[async_trait]
117/// Lifecycle and login contract for Gestalt authentication providers.
118pub trait AuthenticationProvider: Send + Sync + 'static {
119    /// Configures the provider before it starts serving requests.
120    async fn configure(
121        &self,
122        _name: &str,
123        _config: serde_json::Map<String, serde_json::Value>,
124    ) -> Result<()> {
125        Ok(())
126    }
127
128    /// Returns runtime metadata that should augment the static manifest.
129    fn metadata(&self) -> Option<RuntimeMetadata> {
130        None
131    }
132
133    /// Returns non-fatal warnings the host should surface to users.
134    fn warnings(&self) -> Vec<String> {
135        Vec::new()
136    }
137
138    /// Performs an optional health check.
139    async fn health_check(&self) -> Result<()> {
140        Ok(())
141    }
142
143    /// Starts provider-owned background work after configuration.
144    async fn start(&self) -> Result<()> {
145        Ok(())
146    }
147
148    /// Shuts the provider down before the runtime exits.
149    async fn close(&self) -> Result<()> {
150        Ok(())
151    }
152
153    /// Starts an interactive login flow.
154    async fn begin_login(&self, req: BeginLoginRequest) -> Result<BeginLoginResponse>;
155
156    /// Finishes an interactive login flow.
157    async fn complete_login(&self, req: CompleteLoginRequest) -> Result<AuthenticatedUser>;
158
159    /// Validates an externally minted token when supported.
160    async fn validate_external_token(&self, _token: &str) -> Result<Option<AuthenticatedUser>> {
161        Err(Error::unimplemented(
162            "authentication provider does not support external token validation",
163        ))
164    }
165
166    /// Returns host persistence settings for authenticated sessions.
167    fn session_settings(&self) -> Option<AuthSessionSettings> {
168        None
169    }
170
171    /// Returns the TTL the host should use for persisted sessions.
172    ///
173    /// Prefer overriding [`AuthenticationProvider::session_settings`] for new providers.
174    fn session_ttl(&self) -> Option<Duration> {
175        None
176    }
177}