Skip to main content

pray_core/
auth_store_verify.rs

1use super::secrets::*;
2use super::support::*;
3use super::RegistryAuthStore;
4use crate::auth::{AuthSessionKind, AuthVerificationResponse};
5use crate::{PrayError, PrayResult};
6use rusqlite::OptionalExtension;
7
8impl RegistryAuthStore {
9    pub fn verify_email(&self, email: &str, code: &str) -> PrayResult<AuthVerificationResponse> {
10        validate_email(email)?;
11        let connection = self.connection()?;
12        let stored: Option<(String, u64, i64)> = connection
13            .query_row(
14                "SELECT code, created_at, failed_attempts FROM email_verification_codes WHERE email = ?1",
15                rusqlite::params![email],
16                |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
17            )
18            .optional()?;
19        let Some((stored_code, created_at, failed_attempts)) = stored else {
20            return verification_failed();
21        };
22        if failed_attempts >= i64::from(MAX_VERIFICATION_ATTEMPTS)
23            || record_expired(created_at, VERIFICATION_CODE_TTL_SECONDS)?
24            || code.trim().is_empty()
25            || !constant_time_eq(&stored_code, &stored_token(code))
26        {
27            connection.execute(
28                "UPDATE email_verification_codes SET failed_attempts = failed_attempts + 1 WHERE email = ?1",
29                rusqlite::params![email],
30            )?;
31            return verification_failed();
32        }
33        let timestamp = current_unix_timestamp()?;
34        connection.execute(
35            "UPDATE users SET email_verified = 1 WHERE email = ?1",
36            rusqlite::params![email],
37        )?;
38        connection.execute(
39            "UPDATE email_verification_codes SET verified_at = ?2 WHERE email = ?1",
40            rusqlite::params![email, timestamp],
41        )?;
42        let session = self.issue_session(email, AuthSessionKind::Email)?;
43        Ok(AuthVerificationResponse {
44            email: email.to_string(),
45            verified: true,
46            token: session.token,
47            kind: AuthSessionKind::Email,
48        })
49    }
50}
51
52fn verification_failed() -> PrayResult<AuthVerificationResponse> {
53    Err(PrayError::Resolution(VERIFICATION_FAILED.to_string()))
54}