@@ -1103,4 +1103,198 @@ mod tests {
11031103
11041104 assert_eq ! ( email
. email
, "[email protected] " ) ; 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 =
"[email protected] " ; 1111+ let oidc_email =
"[email protected] " ; 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