Skip to content

Commit 42134b4

Browse files
committed
add a test for linking existing account
1 parent f5a3404 commit 42134b4

File tree

1 file changed

+194
-0
lines changed
  • crates/handlers/src/upstream_oauth2

1 file changed

+194
-0
lines changed

crates/handlers/src/upstream_oauth2/link.rs

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,4 +1103,198 @@ mod tests {
11031103

11041104
assert_eq!(email.email, "john@example.com");
11051105
}
1106+
1107+
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
1108+
async fn test_link_existing_account(pool: PgPool) {
1109+
let existing_username = "john";
1110+
let existing_email = "john@example.com";
1111+
let oidc_email = "new@example.com";
1112+
1113+
setup();
1114+
let state = TestState::from_pool(pool).await.unwrap();
1115+
let mut rng = state.rng();
1116+
let cookies = CookieHelper::new();
1117+
1118+
let claims_imports = UpstreamOAuthProviderClaimsImports {
1119+
localpart: UpstreamOAuthProviderImportPreference {
1120+
action: mas_data_model::UpstreamOAuthProviderImportAction::Require,
1121+
template: None,
1122+
},
1123+
email: UpstreamOAuthProviderImportPreference {
1124+
action: mas_data_model::UpstreamOAuthProviderImportAction::Require,
1125+
template: None,
1126+
},
1127+
..UpstreamOAuthProviderClaimsImports::default()
1128+
};
1129+
1130+
let id_token = serde_json::json!({
1131+
"preferred_username": existing_username.to_owned(),
1132+
"email": oidc_email.to_owned(),
1133+
"email_verified": true,
1134+
});
1135+
1136+
// Grab a key to sign the id_token
1137+
// We could generate a key on the fly, but because we have one available here,
1138+
// why not use it?
1139+
let key = state
1140+
.key_store
1141+
.signing_key_for_algorithm(&JsonWebSignatureAlg::Rs256)
1142+
.unwrap();
1143+
1144+
let signer = key
1145+
.params()
1146+
.signing_key_for_alg(&JsonWebSignatureAlg::Rs256)
1147+
.unwrap();
1148+
let header = JsonWebSignatureHeader::new(JsonWebSignatureAlg::Rs256);
1149+
let id_token = Jwt::sign_with_rng(&mut rng, header, id_token, &signer).unwrap();
1150+
1151+
// Provision a provider and a link
1152+
let mut repo = state.repository().await.unwrap();
1153+
let provider = repo
1154+
.upstream_oauth_provider()
1155+
.add(
1156+
&mut rng,
1157+
&state.clock,
1158+
UpstreamOAuthProviderParams {
1159+
issuer: Some("https://example.com/".to_owned()),
1160+
human_name: Some("Example Ltd.".to_owned()),
1161+
brand_name: None,
1162+
scope: Scope::from_iter([OPENID]),
1163+
token_endpoint_auth_method: UpstreamOAuthProviderTokenAuthMethod::None,
1164+
token_endpoint_signing_alg: None,
1165+
id_token_signed_response_alg: JsonWebSignatureAlg::Rs256,
1166+
client_id: "client".to_owned(),
1167+
encrypted_client_secret: None,
1168+
claims_imports,
1169+
authorization_endpoint_override: None,
1170+
token_endpoint_override: None,
1171+
userinfo_endpoint_override: None,
1172+
fetch_userinfo: false,
1173+
userinfo_signed_response_alg: None,
1174+
jwks_uri_override: None,
1175+
discovery_mode: mas_data_model::UpstreamOAuthProviderDiscoveryMode::Oidc,
1176+
pkce_mode: mas_data_model::UpstreamOAuthProviderPkceMode::Auto,
1177+
response_mode: None,
1178+
allow_existing_users: true,
1179+
additional_authorization_parameters: Vec::new(),
1180+
ui_order: 0,
1181+
},
1182+
)
1183+
.await
1184+
.unwrap();
1185+
1186+
let session = repo
1187+
.upstream_oauth_session()
1188+
.add(
1189+
&mut rng,
1190+
&state.clock,
1191+
&provider,
1192+
"state".to_owned(),
1193+
None,
1194+
"nonce".to_owned(),
1195+
)
1196+
.await
1197+
.unwrap();
1198+
1199+
let link = repo
1200+
.upstream_oauth_link()
1201+
.add(
1202+
&mut rng,
1203+
&state.clock,
1204+
&provider,
1205+
"subject".to_owned(),
1206+
None,
1207+
)
1208+
.await
1209+
.unwrap();
1210+
1211+
let session = repo
1212+
.upstream_oauth_session()
1213+
.complete_with_link(
1214+
&state.clock,
1215+
session,
1216+
&link,
1217+
Some(id_token.into_string()),
1218+
None,
1219+
None,
1220+
)
1221+
.await
1222+
.unwrap();
1223+
1224+
//create a user with an email
1225+
let user = repo
1226+
.user()
1227+
.add(&mut rng, &state.clock, existing_username.to_owned())
1228+
.await
1229+
.unwrap();
1230+
1231+
let _user_email = repo
1232+
.user_email()
1233+
.add(&mut rng, &state.clock, &user, existing_email.to_owned())
1234+
.await;
1235+
1236+
repo.save().await.unwrap();
1237+
1238+
let cookie_jar = state.cookie_jar();
1239+
let upstream_sessions = UpstreamSessionsCookie::default()
1240+
.add(session.id, provider.id, "state".to_owned(), None)
1241+
.add_link_to_session(session.id, link.id)
1242+
.unwrap();
1243+
let cookie_jar = upstream_sessions.save(cookie_jar, &state.clock);
1244+
cookies.import(cookie_jar);
1245+
1246+
let request = Request::get(&*mas_router::UpstreamOAuth2Link::new(link.id).path()).empty();
1247+
let request = cookies.with_cookies(request);
1248+
let response = state.request(request).await;
1249+
cookies.save_cookies(&response);
1250+
response.assert_status(StatusCode::OK);
1251+
response.assert_header_value(CONTENT_TYPE, "text/html; charset=utf-8");
1252+
1253+
// Extract the CSRF token from the response body
1254+
let csrf_token = response
1255+
.body()
1256+
.split("name=\"csrf\" value=\"")
1257+
.nth(1)
1258+
.unwrap()
1259+
.split('\"')
1260+
.next()
1261+
.unwrap();
1262+
1263+
let request = Request::post(&*mas_router::UpstreamOAuth2Link::new(link.id).path()).form(
1264+
serde_json::json!({
1265+
"csrf": csrf_token,
1266+
"action": "register",
1267+
"import_email": "on",
1268+
"accept_terms": "on",
1269+
}),
1270+
);
1271+
let request = cookies.with_cookies(request);
1272+
let response = state.request(request).await;
1273+
cookies.save_cookies(&response);
1274+
response.assert_status(StatusCode::SEE_OTHER);
1275+
1276+
// Check that the existing user has a link
1277+
let mut repo = state.repository().await.unwrap();
1278+
1279+
let link = repo
1280+
.upstream_oauth_link()
1281+
.find_by_subject(&provider, "subject")
1282+
.await
1283+
.unwrap()
1284+
.expect("link exists");
1285+
1286+
assert_eq!(link.user_id, Some(user.id));
1287+
1288+
let page = repo
1289+
.user_email()
1290+
.list(UserEmailFilter::new().for_user(&user), Pagination::first(1))
1291+
.await
1292+
.unwrap();
1293+
1294+
//check that the existing email has been updated
1295+
assert_eq!(page.edges.len(), 1);
1296+
let email = page.edges.first().expect("email exists");
1297+
1298+
assert_eq!(email.email, oidc_email);
1299+
}
11061300
}

0 commit comments

Comments
 (0)