1#![warn(missing_docs)]
7
8use async_trait::async_trait;
9use serde::{Deserialize, Serialize};
10
11pub mod pkce;
13
14pub mod strategy;
16
17pub mod error;
19pub use error::AuthError;
20
21pub mod state;
23pub use state::{Identity, OAuth2State, OAuthToken};
24
25pub mod discovery;
27
28pub mod session;
30pub use session::{Session, SessionConfig, SessionStore};
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
34#[serde(tag = "type", content = "data")]
35pub enum AuthInput {
36 Password {
38 identifier: String,
40 password: String,
42 },
43 OAuthCode {
45 code: String,
47 code_verifier: Option<String>,
49 },
50 Token(String),
52 Custom(serde_json::Value),
54}
55
56#[async_trait]
58pub trait AuthMethod: Send + Sync {
59 fn name(&self) -> &str;
61
62 async fn authenticate(&self, input: AuthInput) -> Result<Identity, AuthError>;
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct ProviderConfig {
69 pub id: String,
71 pub name: String,
73 pub extra: std::collections::HashMap<String, String>,
75}
76
77#[async_trait]
80pub trait Provider: Send + Sync {
81 async fn config(&self) -> ProviderConfig;
83}
84
85#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
87pub enum SameSite {
88 Lax,
90 Strict,
92 None,
94}
95
96#[async_trait]
98pub trait OAuthProvider: Provider {
99 fn provider_id(&self) -> &str;
101
102 fn get_authorization_url(
104 &self,
105 state: &str,
106 scopes: &[&str],
107 code_challenge: Option<&str>,
108 nonce: Option<&str>,
109 ) -> String;
110
111 async fn exchange_code_for_identity(
113 &self,
114 code: &str,
115 code_verifier: Option<&str>,
116 nonce: Option<&str>,
117 ) -> Result<(Identity, OAuthToken), AuthError>;
118
119 async fn refresh_token(&self, _refresh_token: &str) -> Result<OAuthToken, AuthError> {
121 Err(AuthError::Provider(
122 "Token refresh not supported by this provider".into(),
123 ))
124 }
125
126 async fn revoke_token(&self, _token: &str) -> Result<(), AuthError> {
128 Err(AuthError::Provider(
129 "Token revocation not supported by this provider".into(),
130 ))
131 }
132}
133
134#[async_trait]
136pub trait CredentialsProvider: Send + Sync {
137 type Credentials;
139
140 async fn authenticate(&self, creds: Self::Credentials) -> Result<Identity, AuthError>;
142}
143
144#[async_trait]
146pub trait UserMapper: Send + Sync {
147 type LocalUser: Send + Sync;
149
150 async fn map_user(&self, identity: &Identity) -> Result<Self::LocalUser, AuthError>;
153}
154
155#[async_trait]
157pub trait ErasedOAuthFlow: Send + Sync {
158 fn provider_id(&self) -> String;
160 fn initiate_login(
162 &self,
163 scopes: &[&str],
164 pkce_challenge: Option<&str>,
165 ) -> (String, OAuth2State);
166 async fn finalize_login(
168 &self,
169 code: &str,
170 received_state: &str,
171 expected_state: &OAuth2State,
172 ) -> Result<(Identity, OAuthToken), AuthError>;
173}
174
175#[async_trait]
176impl UserMapper for () {
177 type LocalUser = ();
178 async fn map_user(&self, _identity: &Identity) -> Result<Self::LocalUser, AuthError> {
179 Ok(())
180 }
181}
182
183#[async_trait]
184impl<T: ErasedOAuthFlow + ?Sized> ErasedOAuthFlow for std::sync::Arc<T> {
185 fn provider_id(&self) -> String {
186 (**self).provider_id()
187 }
188
189 fn initiate_login(
190 &self,
191 scopes: &[&str],
192 pkce_challenge: Option<&str>,
193 ) -> (String, OAuth2State) {
194 (**self).initiate_login(scopes, pkce_challenge)
195 }
196
197 async fn finalize_login(
198 &self,
199 code: &str,
200 received_state: &str,
201 expected_state: &OAuth2State,
202 ) -> Result<(Identity, OAuthToken), AuthError> {
203 (**self)
204 .finalize_login(code, received_state, expected_state)
205 .await
206 }
207}
208
209#[async_trait]
210impl<T: ErasedOAuthFlow + ?Sized> ErasedOAuthFlow for Box<T> {
211 fn provider_id(&self) -> String {
212 (**self).provider_id()
213 }
214
215 fn initiate_login(
216 &self,
217 scopes: &[&str],
218 pkce_challenge: Option<&str>,
219 ) -> (String, OAuth2State) {
220 (**self).initiate_login(scopes, pkce_challenge)
221 }
222
223 async fn finalize_login(
224 &self,
225 code: &str,
226 received_state: &str,
227 expected_state: &OAuth2State,
228 ) -> Result<(Identity, OAuthToken), AuthError> {
229 (**self)
230 .finalize_login(code, received_state, expected_state)
231 .await
232 }
233}