shared/domain/
session.rs

1//! types for Auth / session management
2
3use std::fmt;
4
5use super::user::UserId;
6use crate::api::endpoints::PathPart;
7use macros::make_path_parts;
8use serde::{Deserialize, Serialize};
9
10/// The query key for
11pub const AUTH_QUERY_NAME: &str = "access_token";
12
13/// The name to use for auth cookies.
14pub const AUTH_COOKIE_NAME: &str = "X-AUTH";
15
16/// The name of the CSRF header.
17pub const CSRF_HEADER_NAME: &str = "X-CSRF";
18
19make_path_parts!(ImpersonatePath => "/v1/admin/session/user/{}" => UserId);
20
21#[deprecated(note = "use NewSessionResponse")]
22pub use NewSessionResponse as CreateSessionSuccess;
23
24make_path_parts!(CreateSessionPath => "/v1/session");
25
26/// Response for creating a session.
27///
28/// Notes:
29/// * When creating a `Register` session through OAuth, the API also returns user profile info
30/// given as part of the identity claims from the OAuth provider (e.g. Google).
31/// * This response *also* includes a cookie.
32///
33/// Returned cookie auth token can be passed to the API in three ways in requests.
34/// They are listed below in order of precedence (i.e. if 1 exists then 2, 3 are ignored):
35///
36/// 1. Passed as a query  `<uri-to-resource>?access_token=<token>`.
37/// 2. Passed in the request header as `Authorization: Bearer <token>`.
38/// 3. As a cookie, `X-AUTH=<token>`. This token will also be authenticated against the CSRF-prevention
39/// header.
40#[derive(Serialize, Deserialize, Debug)]
41#[serde(rename_all = "camelCase")]
42pub enum CreateSessionResponse {
43    /// A new session was successfully created and the user may use the api as normal.
44    Login(NewSessionResponse),
45
46    /// The user has no profile, a token for creating one has been returned.
47    /// * If using OAuth, then a [`Some(OAuthUserProfile)`](OAuthUserProfile) is included as well
48    /// containing known information about the user.
49    /// * If using Basic auth, then the `oauth_profile` field will be `None` and not be serialized.
50    ///
51    /// ## Json response without OAuth profile:
52    /// ```json
53    /// {
54    ///     "register": {
55    ///         "csrf": <CSRF_TOKEN>,
56    ///     }
57    /// }
58    /// ```
59    ///
60    /// ## Json response with OAuth profile:
61    /// ```json
62    /// {
63    ///     "register": {
64    ///         "csrf": <CSRF_TOKEN>,
65    ///         "oauth_profile": {
66    ///             "email": <EMAIL>,
67    ///             ... # other optional profile fields
68    ///         }
69    ///     }
70    /// }
71    /// ```
72    Register {
73        /// Csrf token. Note that this field is "flattened" into it's contents when (de)serialized. See example above.
74        #[serde(flatten)]
75        response: NewSessionResponse,
76        /// Oauth profile
77        #[serde(skip_serializing_if = "Option::is_none")]
78        oauth_profile: Option<OAuthUserProfile>,
79    },
80}
81
82/// User's profile info fetched from the OAuth service. Returned as part of the identity claims
83/// to be used as defaults for populating a `PutProfile` request.
84#[derive(Serialize, Deserialize, Debug, Clone)]
85#[serde(rename_all = "camelCase")]
86pub struct OAuthUserProfile {
87    /// The user's email
88    pub email: String,
89
90    /// The user's name
91    #[serde(default)]
92    pub name: Option<String>,
93
94    /// the user's profile picture
95    #[serde(default)]
96    pub profile_picture: Option<String>,
97
98    /// The user's given / first name
99    #[serde(default)]
100    pub given_name: Option<String>,
101
102    /// The user's family / last name
103    #[serde(default)]
104    pub family_name: Option<String>,
105
106    /// The user's locale
107    #[serde(default)]
108    pub locale: Option<String>,
109}
110
111/// Response for successfully creating a session.
112///
113/// Note: This response *also* includes a cookie.
114#[derive(Serialize, Deserialize, Debug)]
115#[serde(rename_all = "camelCase")]
116pub struct NewSessionResponse {
117    /// A transparent CSRF token to use for this Session.
118    pub csrf: String,
119}
120
121/// Which URL to use for OAuth callback.
122#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
123#[serde(rename_all = "camelCase")]
124pub enum OAuthUrlKind {
125    /// Get OAuth Url for login
126    Login,
127    /// Get OAuth Url for register
128    Register,
129}
130impl PathPart for OAuthUrlKind {
131    fn get_path_string(&self) -> String {
132        match self {
133            Self::Login => String::from("login"),
134            Self::Register => String::from("register"),
135        }
136    }
137}
138
139/// Which *service* to use for OAuth Url generation.
140#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
141#[serde(rename_all = "camelCase")]
142#[non_exhaustive]
143pub enum GetOAuthUrlServiceKind {
144    /// Google OAuth v2
145    Google,
146}
147impl PathPart for GetOAuthUrlServiceKind {
148    fn get_path_string(&self) -> String {
149        match self {
150            Self::Google => String::from("google"),
151        }
152    }
153}
154
155/// OAuth provider for emails
156#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
157#[serde(rename_all = "camelCase")]
158pub enum OAuthProvider {
159    /// Google OAuth v2
160    Google,
161}
162
163impl OAuthProvider {
164    #[allow(missing_docs)]
165    pub fn as_str(&self) -> &'static str {
166        match self {
167            Self::Google => "Google",
168        }
169    }
170}
171
172make_path_parts!(GetOAuthPath => "/v1/session/oauth/url/{}/{}" => GetOAuthUrlServiceKind, OAuthUrlKind);
173
174/// Response for what URL to use for OAuth callback.
175#[derive(Serialize, Deserialize)]
176#[serde(rename_all = "camelCase")]
177pub struct GetOAuthUrlResponse {
178    ///  URL to use for OAuth callback
179    pub url: String,
180}
181
182make_path_parts!(CreateSessionOAuthPath => "/v1/session/oauth");
183
184/// Request for Creating a Session / signing in via oauth.
185#[derive(Serialize, Deserialize)]
186#[serde(rename_all = "camelCase")]
187#[non_exhaustive]
188pub enum CreateSessionOAuthRequest {
189    /// OAuth with google
190    Google {
191        /// The google OAuth Code
192        code: String,
193
194        /// Which OAuth url was used
195        /// Not sure if this is needed anymore
196        redirect_kind: OAuthUrlKind,
197    },
198}
199
200/// Optional query used as the first option for authentication with the API
201#[derive(Serialize, Deserialize)]
202pub struct SessionTokenQuery {
203    /// The token to be used for authentication
204    pub access_token: Option<String>,
205}
206
207impl fmt::Debug for CreateSessionOAuthRequest {
208    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209        match self {
210            // todo: replace with `finish_non_exhaustive`
211            Self::Google { .. } => f.debug_struct("Google").finish(),
212        }
213    }
214}
215
216make_path_parts!(DeleteSessionPath => "/v1/session");