Skip to content

Commit f05916f

Browse files
committed
Experimental configuration toggle for passkeys
1 parent 46eb75f commit f05916f

File tree

17 files changed

+114
-25
lines changed

17 files changed

+114
-25
lines changed

crates/cli/src/util.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,10 @@ pub fn site_config_from_config(
222222
minimum_password_complexity: password_config.minimum_complexity(),
223223
session_expiration,
224224
login_with_email_allowed: account_config.login_with_email_allowed,
225+
passkeys_enabled: experimental_config
226+
.passkeys
227+
.as_ref()
228+
.is_some_and(|c| c.enabled),
225229
})
226230
}
227231

crates/config/src/sections/experimental.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ pub struct InactiveSessionExpirationConfig {
4545
pub expire_user_sessions: bool,
4646
}
4747

48+
/// Configuration options for passkeys
49+
#[serde_as]
50+
#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)]
51+
pub struct PasskeysConfig {
52+
/// Whether passkeys are enabled or not
53+
#[serde(default)]
54+
pub enabled: bool,
55+
}
56+
4857
/// Configuration sections for experimental options
4958
///
5059
/// Do not change these options unless you know what you are doing.
@@ -75,6 +84,12 @@ pub struct ExperimentalConfig {
7584
/// Disabled by default
7685
#[serde(skip_serializing_if = "Option::is_none")]
7786
pub inactive_session_expiration: Option<InactiveSessionExpirationConfig>,
87+
88+
/// Experimental passkey support
89+
///
90+
/// Disabled by default
91+
#[serde(skip_serializing_if = "Option::is_none")]
92+
pub passkeys: Option<PasskeysConfig>,
7893
}
7994

8095
impl Default for ExperimentalConfig {
@@ -83,6 +98,7 @@ impl Default for ExperimentalConfig {
8398
access_token_ttl: default_token_ttl(),
8499
compat_token_ttl: default_token_ttl(),
85100
inactive_session_expiration: None,
101+
passkeys: None,
86102
}
87103
}
88104
}
@@ -92,6 +108,7 @@ impl ExperimentalConfig {
92108
is_default_token_ttl(&self.access_token_ttl)
93109
&& is_default_token_ttl(&self.compat_token_ttl)
94110
&& self.inactive_session_expiration.is_none()
111+
&& self.passkeys.is_none()
95112
}
96113
}
97114

crates/data-model/src/site_config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,7 @@ pub struct SiteConfig {
9090

9191
/// Whether users can log in with their email address.
9292
pub login_with_email_allowed: bool,
93+
94+
/// Whether passkeys are enabled
95+
pub passkeys_enabled: bool,
9396
}

crates/handlers/src/graphql/model/site_config.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ pub struct SiteConfig {
5656

5757
/// Whether users can log in with their email address.
5858
login_with_email_allowed: bool,
59+
60+
/// Whether passkeys are enabled
61+
passkeys_enabled: bool,
5962
}
6063

6164
#[derive(SimpleObject)]
@@ -102,6 +105,7 @@ impl SiteConfig {
102105
account_deactivation_allowed: data_model.account_deactivation_allowed,
103106
minimum_password_complexity: data_model.minimum_password_complexity,
104107
login_with_email_allowed: data_model.login_with_email_allowed,
108+
passkeys_enabled: data_model.passkeys_enabled,
105109
}
106110
}
107111
}

crates/handlers/src/test_utils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ pub fn test_site_config() -> SiteConfig {
145145
minimum_password_complexity: 1,
146146
session_expiration: None,
147147
login_with_email_allowed: true,
148+
passkeys_enabled: false,
148149
}
149150
}
150151

crates/handlers/src/views/login.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,10 @@ pub(crate) async fn get(
9999

100100
let providers = repo.upstream_oauth_provider().all_enabled().await?;
101101

102-
// If password-based login is disabled, and there is only one upstream provider,
103-
// we can directly start an authorization flow
104-
if !site_config.password_login_enabled && providers.len() == 1 {
102+
// If password-based login and passkeys are disabled, and there is only one
103+
// upstream provider, we can directly start an authorization flow
104+
if !site_config.password_login_enabled && !site_config.passkeys_enabled && providers.len() == 1
105+
{
105106
let provider = providers.into_iter().next().unwrap();
106107

107108
let mut destination = UpstreamOAuth2Authorize::new(provider.id);

crates/templates/src/context/ext.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ impl SiteConfigExt for SiteConfig {
4848
password_login: self.password_login_enabled,
4949
account_recovery: self.account_recovery_allowed,
5050
login_with_email_allowed: self.login_with_email_allowed,
51+
passkeys_enabled: self.passkeys_enabled,
5152
}
5253
}
5354
}

crates/templates/src/context/features.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ pub struct SiteFeatures {
2626

2727
/// Whether users can log in with their email address.
2828
pub login_with_email_allowed: bool,
29+
30+
/// Whether passkeys are enabled
31+
pub passkeys_enabled: bool,
2932
}
3033

3134
impl Object for SiteFeatures {
@@ -35,6 +38,7 @@ impl Object for SiteFeatures {
3538
"password_login" => Some(Value::from(self.password_login)),
3639
"account_recovery" => Some(Value::from(self.account_recovery)),
3740
"login_with_email_allowed" => Some(Value::from(self.login_with_email_allowed)),
41+
"passkeys_enabled" => Some(Value::from(self.passkeys_enabled)),
3842
_ => None,
3943
}
4044
}
@@ -45,6 +49,7 @@ impl Object for SiteFeatures {
4549
"password_login",
4650
"account_recovery",
4751
"login_with_email_allowed",
52+
"passkeys_enabled",
4853
])
4954
}
5055
}

crates/templates/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@ mod tests {
494494
password_registration: true,
495495
account_recovery: true,
496496
login_with_email_allowed: true,
497+
passkeys_enabled: true,
497498
};
498499
let vite_manifest_path =
499500
Utf8Path::new(env!("CARGO_MANIFEST_DIR")).join("../../frontend/dist/manifest.json");

docs/config.schema.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2561,6 +2561,14 @@
25612561
"$ref": "#/definitions/InactiveSessionExpirationConfig"
25622562
}
25632563
]
2564+
},
2565+
"passkeys": {
2566+
"description": "Experimental passkey support\n\nDisabled by default",
2567+
"allOf": [
2568+
{
2569+
"$ref": "#/definitions/PasskeysConfig"
2570+
}
2571+
]
25642572
}
25652573
}
25662574
},
@@ -2594,6 +2602,17 @@
25942602
"type": "boolean"
25952603
}
25962604
}
2605+
},
2606+
"PasskeysConfig": {
2607+
"description": "Configuration options for passkeys",
2608+
"type": "object",
2609+
"properties": {
2610+
"enabled": {
2611+
"description": "Whether passkeys are enabled or not",
2612+
"default": false,
2613+
"type": "boolean"
2614+
}
2615+
}
25972616
}
25982617
}
25992618
}

0 commit comments

Comments
 (0)