Skip to content
This repository was archived by the owner on Sep 10, 2024. It is now read-only.

Commit ae3213f

Browse files
committed
Make the email verification state more configurable on upstream OAuth 2.0 registration
This also marks the email as primary
1 parent 8e5ebcd commit ae3213f

File tree

8 files changed

+162
-18
lines changed

8 files changed

+162
-18
lines changed

crates/cli/src/commands/config.rs

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ use tracing::{info, info_span, warn};
2626

2727
use crate::util::database_connection_from_config;
2828

29-
fn map_import_preference(
30-
config: &mas_config::UpstreamOAuth2ImportPreference,
31-
) -> mas_data_model::UpstreamOAuthProviderImportPreference {
32-
let action = match &config.action {
29+
fn map_import_action(
30+
config: &mas_config::UpstreamOAuth2ImportAction,
31+
) -> mas_data_model::UpstreamOAuthProviderImportAction {
32+
match config {
3333
mas_config::UpstreamOAuth2ImportAction::Ignore => {
3434
mas_data_model::UpstreamOAuthProviderImportAction::Ignore
3535
}
@@ -42,9 +42,15 @@ fn map_import_preference(
4242
mas_config::UpstreamOAuth2ImportAction::Require => {
4343
mas_data_model::UpstreamOAuthProviderImportAction::Require
4444
}
45-
};
45+
}
46+
}
4647

47-
mas_data_model::UpstreamOAuthProviderImportPreference { action }
48+
fn map_import_preference(
49+
config: &mas_config::UpstreamOAuth2ImportPreference,
50+
) -> mas_data_model::UpstreamOAuthProviderImportPreference {
51+
mas_data_model::UpstreamOAuthProviderImportPreference {
52+
action: map_import_action(&config.action),
53+
}
4854
}
4955

5056
fn map_claims_imports(
@@ -64,7 +70,25 @@ fn map_claims_imports(
6470
email: config
6571
.email
6672
.as_ref()
67-
.map(map_import_preference)
73+
.map(|c| mas_data_model::UpstreamOAuthProviderImportPreference {
74+
action: map_import_action(&c.action),
75+
})
76+
.unwrap_or_default(),
77+
// XXX: this is a bit ugly
78+
verify_email: config
79+
.email
80+
.as_ref()
81+
.map(|c| match c.set_email_verification {
82+
mas_config::UpstreamOAuth2SetEmailVerification::Always => {
83+
mas_data_model::UpsreamOAuthProviderSetEmailVerification::Always
84+
}
85+
mas_config::UpstreamOAuth2SetEmailVerification::Never => {
86+
mas_data_model::UpsreamOAuthProviderSetEmailVerification::Never
87+
}
88+
mas_config::UpstreamOAuth2SetEmailVerification::Import => {
89+
mas_data_model::UpsreamOAuthProviderSetEmailVerification::Import
90+
}
91+
})
6892
.unwrap_or_default(),
6993
}
7094
}

crates/config/src/sections/mod.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,11 @@ pub use self::{
4949
},
5050
templates::TemplatesConfig,
5151
upstream_oauth2::{
52-
ClaimsImports as UpstreamOAuth2ClaimsImports, ImportAction as UpstreamOAuth2ImportAction,
53-
ImportPreference as UpstreamOAuth2ImportPreference, UpstreamOAuth2Config,
52+
ClaimsImports as UpstreamOAuth2ClaimsImports,
53+
EmailImportPreference as UpstreamOAuth2EmailImportPreference,
54+
ImportAction as UpstreamOAuth2ImportAction,
55+
ImportPreference as UpstreamOAuth2ImportPreference,
56+
SetEmailVerification as UpstreamOAuth2SetEmailVerification, UpstreamOAuth2Config,
5457
},
5558
};
5659
use crate::util::ConfigurationSection;

crates/config/src/sections/upstream_oauth2.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,34 @@ pub struct ImportPreference {
104104
pub action: ImportAction,
105105
}
106106

107+
/// Should the email address be marked as verified
108+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
109+
#[serde(rename_all = "lowercase")]
110+
pub enum SetEmailVerification {
111+
/// Mark the email address as verified
112+
Always,
113+
114+
/// Don't mark the email address as verified
115+
Never,
116+
117+
/// Mark the email address as verified if the upstream provider says it is
118+
/// through the `email_verified` claim
119+
#[default]
120+
Import,
121+
}
122+
123+
/// What should be done with the email claim
124+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
125+
pub struct EmailImportPreference {
126+
/// How to handle the claim
127+
#[serde(default)]
128+
pub action: ImportAction,
129+
130+
/// Should the email address be marked as verified
131+
#[serde(default)]
132+
pub set_email_verification: SetEmailVerification,
133+
}
134+
107135
/// How claims should be imported
108136
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
109137
pub struct ClaimsImports {
@@ -118,7 +146,7 @@ pub struct ClaimsImports {
118146
/// Import the email address of the user based on the `email` and
119147
/// `email_verified` claims
120148
#[serde(default)]
121-
pub email: Option<ImportPreference>,
149+
pub email: Option<EmailImportPreference>,
122150
}
123151

124152
#[skip_serializing_none]

crates/data-model/src/lib.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,10 @@ pub use self::{
4646
AccessToken, AccessTokenState, RefreshToken, RefreshTokenState, TokenFormatError, TokenType,
4747
},
4848
upstream_oauth2::{
49-
UpstreamOAuthAuthorizationSession, UpstreamOAuthAuthorizationSessionState,
50-
UpstreamOAuthLink, UpstreamOAuthProvider, UpstreamOAuthProviderClaimsImports,
51-
UpstreamOAuthProviderImportAction, UpstreamOAuthProviderImportPreference,
49+
UpsreamOAuthProviderSetEmailVerification, UpstreamOAuthAuthorizationSession,
50+
UpstreamOAuthAuthorizationSessionState, UpstreamOAuthLink, UpstreamOAuthProvider,
51+
UpstreamOAuthProviderClaimsImports, UpstreamOAuthProviderImportAction,
52+
UpstreamOAuthProviderImportPreference,
5253
},
5354
users::{
5455
Authentication, AuthenticationMethod, BrowserSession, Password, User, UserEmail,

crates/data-model/src/upstream_oauth2/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ pub use self::{
2121
provider::{
2222
ClaimsImports as UpstreamOAuthProviderClaimsImports,
2323
ImportAction as UpstreamOAuthProviderImportAction,
24-
ImportPreference as UpstreamOAuthProviderImportPreference, UpstreamOAuthProvider,
24+
ImportPreference as UpstreamOAuthProviderImportPreference,
25+
SetEmailVerification as UpsreamOAuthProviderSetEmailVerification, UpstreamOAuthProvider,
2526
},
2627
session::{UpstreamOAuthAuthorizationSession, UpstreamOAuthAuthorizationSessionState},
2728
};

crates/data-model/src/upstream_oauth2/provider.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,32 @@ pub struct UpstreamOAuthProvider {
3131
pub claims_imports: ClaimsImports,
3232
}
3333

34+
/// Whether to set the email as verified when importing it from the upstream
35+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
36+
#[serde(rename_all = "lowercase")]
37+
pub enum SetEmailVerification {
38+
/// Set the email as verified
39+
Always,
40+
41+
/// Never set the email as verified
42+
Never,
43+
44+
/// Set the email as verified if the upstream provider claims it is verified
45+
#[default]
46+
Import,
47+
}
48+
49+
impl SetEmailVerification {
50+
#[must_use]
51+
pub fn should_mark_as_verified(&self, upstream_verified: bool) -> bool {
52+
match self {
53+
Self::Always => true,
54+
Self::Never => false,
55+
Self::Import => upstream_verified,
56+
}
57+
}
58+
}
59+
3460
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
3561
pub struct ClaimsImports {
3662
#[serde(default)]
@@ -41,6 +67,9 @@ pub struct ClaimsImports {
4167

4268
#[serde(default)]
4369
pub email: ImportPreference,
70+
71+
#[serde(default)]
72+
pub verify_email: SetEmailVerification,
4473
}
4574

4675
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]

crates/handlers/src/upstream_oauth2/link.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -527,11 +527,19 @@ pub(crate) async fn post(
527527
.add(&mut rng, &clock, &user, email)
528528
.await?;
529529

530-
// Mark the email as verified if the upstream provider says it is.
531-
if payload.email_verified {
532-
repo.user_email()
530+
// Mark the email as verified according to the policy and whether the provider
531+
// claims it is, and make it the primary email.
532+
if provider
533+
.claims_imports
534+
.verify_email
535+
.should_mark_as_verified(payload.email_verified)
536+
{
537+
let user_email = repo
538+
.user_email()
533539
.mark_as_verified(&clock, user_email)
534540
.await?;
541+
542+
repo.user_email().set_as_primary(&user_email).await?;
535543
}
536544
}
537545

docs/config.schema.json

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@
307307
"default": null,
308308
"allOf": [
309309
{
310-
"$ref": "#/definitions/ImportPreference"
310+
"$ref": "#/definitions/EmailImportPreference"
311311
}
312312
]
313313
},
@@ -687,6 +687,30 @@
687687
}
688688
}
689689
},
690+
"EmailImportPreference": {
691+
"description": "What should be done with the email claim",
692+
"type": "object",
693+
"properties": {
694+
"action": {
695+
"description": "How to handle the claim",
696+
"default": "ignore",
697+
"allOf": [
698+
{
699+
"$ref": "#/definitions/ImportAction"
700+
}
701+
]
702+
},
703+
"set_email_verification": {
704+
"description": "Should the email address be marked as verified",
705+
"default": "import",
706+
"allOf": [
707+
{
708+
"$ref": "#/definitions/SetEmailVerification"
709+
}
710+
]
711+
}
712+
}
713+
},
690714
"EmailSmtpMode": {
691715
"description": "Encryption mode to use",
692716
"oneOf": [
@@ -1752,6 +1776,32 @@
17521776
}
17531777
}
17541778
},
1779+
"SetEmailVerification": {
1780+
"description": "Should the email address be marked as verified",
1781+
"oneOf": [
1782+
{
1783+
"description": "Mark the email address as verified",
1784+
"type": "string",
1785+
"enum": [
1786+
"always"
1787+
]
1788+
},
1789+
{
1790+
"description": "Don't mark the email address as verified",
1791+
"type": "string",
1792+
"enum": [
1793+
"never"
1794+
]
1795+
},
1796+
{
1797+
"description": "Mark the email address as verified if the upstream provider says it is through the `email_verified` claim",
1798+
"type": "string",
1799+
"enum": [
1800+
"import"
1801+
]
1802+
}
1803+
]
1804+
},
17551805
"TelemetryConfig": {
17561806
"description": "Configuration related to sending monitoring data",
17571807
"type": "object",

0 commit comments

Comments
 (0)