@@ -1103,4 +1103,198 @@ mod tests {
1103
1103
1104
1104
assert_eq ! ( email. email, "john@example.com" ) ;
1105
1105
}
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
+ }
1106
1300
}
0 commit comments