@@ -190,6 +190,65 @@ public async Task CompleteLogin_WhenStepUpAndNoExistingUser_Returns400()
190190 await _userRepository . DidNotReceive ( ) . UpdateUserAsync ( Arg . Any < User > ( ) , Arg . Any < CancellationToken > ( ) ) ;
191191 }
192192
193+ [ Fact ]
194+ public async Task CompleteLogin_WhenStepUpAndSafeReturnUrl_Returns200WithReturnUrl ( )
195+ {
196+ const string signingKey = "complete-login-signing-key-at-least-32-characters-long" ;
197+ _config [ "Oidc:CompleteLoginSigningKey" ] . Returns ( signingKey ) ;
198+
199+ var callbackToken = CreateValidCallbackToken ( signingKey , email : "user@example.com" ) ;
200+ var body = new CompleteLoginRequest (
201+ CoStateKey ,
202+ callbackToken ,
203+ IsStepUp : true ,
204+ ReturnUrl : "/profile/address?q=1" ) ;
205+
206+ var user = new User { Id = 1 , Email = "user@example.com" } ;
207+ _userRepository . GetUserByEmailAsync ( "user@example.com" , Arg . Any < CancellationToken > ( ) )
208+ . Returns ( user ) ;
209+ _userRepository . UpdateUserAsync ( Arg . Any < User > ( ) , Arg . Any < CancellationToken > ( ) )
210+ . Returns ( Task . CompletedTask ) ;
211+
212+ const string portalJwt = "portal-jwt-returned-by-service" ;
213+ _jwtService . GenerateToken ( Arg . Any < User > ( ) , Arg . Any < IReadOnlyDictionary < string , string > ? > ( ) )
214+ . Returns ( portalJwt ) ;
215+
216+ var result = await _controller . CompleteLogin ( body , CancellationToken . None ) ;
217+
218+ var okResult = Assert . IsType < OkObjectResult > ( result ) ;
219+ var valueType = okResult . Value ! . GetType ( ) ;
220+ Assert . Equal ( "/profile/address?q=1" , valueType . GetProperty ( "returnUrl" ) ! . GetValue ( okResult . Value ) as string ) ;
221+ Assert . Equal ( portalJwt , valueType . GetProperty ( "token" ) ! . GetValue ( okResult . Value ) as string ) ;
222+ }
223+
224+ [ Fact ]
225+ public async Task CompleteLogin_WhenStepUpAndExternalReturnUrl_OmitsReturnUrlFromResponse ( )
226+ {
227+ const string signingKey = "complete-login-signing-key-at-least-32-characters-long" ;
228+ _config [ "Oidc:CompleteLoginSigningKey" ] . Returns ( signingKey ) ;
229+
230+ var callbackToken = CreateValidCallbackToken ( signingKey , email : "user@example.com" ) ;
231+ var body = new CompleteLoginRequest (
232+ CoStateKey ,
233+ callbackToken ,
234+ IsStepUp : true ,
235+ ReturnUrl : "https://evil.example/phish" ) ;
236+
237+ var user = new User { Id = 1 , Email = "user@example.com" } ;
238+ _userRepository . GetUserByEmailAsync ( "user@example.com" , Arg . Any < CancellationToken > ( ) )
239+ . Returns ( user ) ;
240+ _userRepository . UpdateUserAsync ( Arg . Any < User > ( ) , Arg . Any < CancellationToken > ( ) )
241+ . Returns ( Task . CompletedTask ) ;
242+
243+ _jwtService . GenerateToken ( Arg . Any < User > ( ) , Arg . Any < IReadOnlyDictionary < string , string > ? > ( ) )
244+ . Returns ( "portal-jwt" ) ;
245+
246+ var result = await _controller . CompleteLogin ( body , CancellationToken . None ) ;
247+
248+ var okResult = Assert . IsType < OkObjectResult > ( result ) ;
249+ Assert . Null ( okResult . Value ! . GetType ( ) . GetProperty ( "returnUrl" ) ) ;
250+ }
251+
193252 private static string CreateValidCallbackToken ( string signingKey , string email )
194253 {
195254 return CreateCallbackTokenWithClaims ( signingKey , new Claim ( "email" , email ) ) ;
0 commit comments