openauth_core/context/
secrets.rs1use std::fmt;
2
3use crate::crypto::{JweSecretSource, SecretConfig, SecretSource};
4use crate::error::OpenAuthError;
5use crate::options::OpenAuthOptions;
6
7use super::AuthEnvironment;
8
9pub(super) const DEFAULT_SECRET: &str = "better-auth-secret-12345678901234567890";
10
11#[derive(Clone, PartialEq, Eq)]
12pub enum SecretMaterial {
13 Single(String),
14 Rotating(SecretConfig),
15}
16
17impl fmt::Debug for SecretMaterial {
18 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
19 match self {
20 Self::Single(_) => formatter
21 .debug_tuple("Single")
22 .field(&"<redacted>")
23 .finish(),
24 Self::Rotating(config) => formatter.debug_tuple("Rotating").field(config).finish(),
25 }
26 }
27}
28
29impl JweSecretSource for SecretMaterial {
30 fn current_jwe_secret(&self) -> Result<String, OpenAuthError> {
31 match self {
32 Self::Single(secret) => secret.current_jwe_secret(),
33 Self::Rotating(config) => config.current_jwe_secret(),
34 }
35 }
36
37 fn all_jwe_secrets(&self) -> Result<Vec<crate::crypto::jwe::JweSecret>, OpenAuthError> {
38 match self {
39 Self::Single(secret) => secret.all_jwe_secrets(),
40 Self::Rotating(config) => config.all_jwe_secrets(),
41 }
42 }
43}
44
45impl SecretSource for &SecretMaterial {
46 fn encrypt_current(&self, data: &str) -> Result<String, OpenAuthError> {
47 match self {
48 SecretMaterial::Single(secret) => secret.encrypt_current(data),
49 SecretMaterial::Rotating(config) => config.encrypt_current(data),
50 }
51 }
52
53 fn decrypt_payload(&self, data: &str) -> Result<String, OpenAuthError> {
54 match self {
55 SecretMaterial::Single(secret) => secret.decrypt_payload(data),
56 SecretMaterial::Rotating(config) => config.decrypt_payload(data),
57 }
58 }
59}
60
61pub(super) fn resolve_legacy_secret(
62 options: &OpenAuthOptions,
63 environment: &AuthEnvironment,
64) -> Option<String> {
65 options
66 .secret
67 .clone()
68 .or_else(|| environment.better_auth_secret.clone())
69 .or_else(|| environment.auth_secret.clone())
70}
71
72pub(super) fn validate_secret(secret: &str, production: bool) -> Result<(), OpenAuthError> {
73 if secret.is_empty() {
74 return Err(OpenAuthError::InvalidConfig(
75 "OpenAuth secret is missing".to_owned(),
76 ));
77 }
78 if production && secret == DEFAULT_SECRET {
79 return Err(OpenAuthError::InvalidConfig(
80 "default secret cannot be used in production".to_owned(),
81 ));
82 }
83 Ok(())
84}