@@ -19,27 +19,35 @@ export const LoginCallback = memo(({ service }: PropsT) => {
1919 useEffect ( ( ) => {
2020 const ac = new AbortController ( ) ;
2121 const { search, hash } = document . location ;
22- const nonce = storage . getItem ( 'login-nonce' ) ;
23- handleLogin ( service , nonce , { search, hash } , ac . signal )
22+ const redirectUri = document . location . href . split ( '?' ) [ 0 ] ! ;
23+ const localState = storage . getItem ( 'login-state' ) ;
24+ handleLogin ( service , localState , { search, hash, redirectUri } , ac . signal )
2425 . then ( ( redirect ) => {
2526 if ( ac . signal . aborted ) {
2627 return ;
2728 }
28- storage . removeItem ( 'login-nonce' ) ;
29+ storage . removeItem ( 'login-state' ) ;
30+ if (
31+ new URL ( redirect , document . location . href ) . host !==
32+ document . location . host
33+ ) {
34+ // possibly a malicious redirect - ignore it and substitute a safe one
35+ redirect = '/' ;
36+ }
2937 stableSetLocation ( redirect , { replace : true } ) ;
3038 } )
3139 . catch ( ( err ) => {
3240 if ( ac . signal . aborted ) {
3341 return ;
3442 }
3543 if ( ! ( err instanceof Error ) ) {
36- storage . removeItem ( 'login-nonce ' ) ;
44+ storage . removeItem ( 'login-state ' ) ;
3745 setError ( String ( err ) ) ;
3846 } else if ( err . message === 'unrecognised login details' ) {
3947 // GitLab shows a bare link to the /sso/login URL on the confirmation page
4048 stableSetLocation ( '/' , { replace : true } ) ;
4149 } else {
42- storage . removeItem ( 'login-nonce ' ) ;
50+ storage . removeItem ( 'login-state ' ) ;
4351 setError ( err . message ) ;
4452 }
4553 } ) ;
0 commit comments