github.com/argoproj/argo-cd/v3@v3.2.1/ui/src/app/login/components/login.tsx (about) 1 import {FormField} from 'argo-ui'; 2 import React, {useContext, useEffect, useState} from 'react'; 3 import {Form, Text} from 'react-form'; 4 import {RouteComponentProps} from 'react-router'; 5 import {AuthSettings} from '../../shared/models'; 6 import {services} from '../../shared/services'; 7 import {Context} from '../../shared/context'; 8 9 require('./login.scss'); 10 11 export interface LoginForm { 12 username: string; 13 password: string; 14 } 15 16 export function Login(props: RouteComponentProps<{}>) { 17 const appContext = useContext(Context); 18 19 const search = new URLSearchParams(props.history.location.search); 20 const returnUrl = search.get('return_url') || ''; 21 const hasSsoLoginError = search.get('has_sso_error') === 'true'; 22 23 const [authSettings, setAuthSettings] = useState<AuthSettings | null>(null); 24 const [loginError, setLoginError] = useState<string | null>(null); 25 const [loginInProgress, setLoginInProgress] = useState<boolean>(false); 26 27 useEffect(() => { 28 (async () => { 29 const authSettings = await services.authService.settings(); 30 setAuthSettings(authSettings); 31 })(); 32 }, []); 33 34 const login = async (username: string, password: string, returnURL: string) => { 35 try { 36 setLoginError(''); 37 setLoginInProgress(true); 38 appContext.navigation.goto('.', {sso_error: null}); 39 await services.users.login(username, password); 40 setLoginInProgress(false); 41 if (returnURL) { 42 const url = new URL(returnURL); 43 let redirectURL = url.pathname + url.search; 44 // return url already contains baseHref, so we need to remove it 45 if (appContext.baseHref != '/' && redirectURL.startsWith(appContext.baseHref)) { 46 redirectURL = redirectURL.substring(appContext.baseHref.length); 47 } 48 appContext.navigation.goto(redirectURL); 49 } else { 50 appContext.navigation.goto('/applications'); 51 } 52 } catch (e) { 53 setLoginError(e.response.body.error); 54 setLoginInProgress(false); 55 } 56 }; 57 58 const ssoConfigured = authSettings && ((authSettings.dexConfig && (authSettings.dexConfig.connectors || []).length > 0) || authSettings.oidcConfig); 59 60 return ( 61 <div className='login'> 62 <div className='login__content show-for-medium'> 63 <div className='login__text'>Let's get stuff deployed!</div> 64 <div className='argo__logo' /> 65 </div> 66 <div className='login__box'> 67 <div className='login__logo width-control'> 68 <img className='logo-image' src='assets/images/argo_o.svg' alt='argo' /> 69 </div> 70 {ssoConfigured && ( 71 <div className='login__box_saml width-control'> 72 <a href={`auth/login?return_url=${encodeURIComponent(returnUrl)}`}> 73 <button className='argo-button argo-button--base argo-button--full-width argo-button--xlg'> 74 {(authSettings.oidcConfig && <span>Log in via {authSettings.oidcConfig.name}</span>) || 75 (authSettings.dexConfig.connectors.length === 1 && <span>Log in via {authSettings.dexConfig.connectors[0].name}</span>) || ( 76 <span>SSO Login</span> 77 )} 78 </button> 79 </a> 80 {hasSsoLoginError && <div className='argo-form-row__error-msg'>Login failed.</div>} 81 {authSettings && !authSettings.userLoginsDisabled && ( 82 <div className='login__saml-separator'> 83 <span>or</span> 84 </div> 85 )} 86 </div> 87 )} 88 {authSettings && !authSettings.userLoginsDisabled && ( 89 <Form 90 onSubmit={(params: LoginForm) => login(params.username, params.password, returnUrl)} 91 validateError={(params: LoginForm) => ({ 92 username: !params.username && 'Username is required', 93 password: !params.password && 'Password is required' 94 })}> 95 {formApi => ( 96 <form role='form' className='width-control' onSubmit={formApi.submitForm}> 97 <div className='argo-form-row'> 98 <FormField 99 formApi={formApi} 100 label='Username' 101 field='username' 102 component={Text} 103 componentProps={{name: 'username', autoCapitalize: 'none', autoComplete: 'username'}} 104 /> 105 </div> 106 <div className='argo-form-row'> 107 <FormField 108 formApi={formApi} 109 label='Password' 110 field='password' 111 component={Text} 112 componentProps={{name: 'password', type: 'password', autoComplete: 'password'}} 113 /> 114 {loginError && <div className='argo-form-row__error-msg'>{loginError}</div>} 115 </div> 116 <div className='login__form-row'> 117 <button disabled={loginInProgress} className='argo-button argo-button--full-width argo-button--xlg' type='submit'> 118 Sign In 119 </button> 120 </div> 121 </form> 122 )} 123 </Form> 124 )} 125 {authSettings && authSettings.userLoginsDisabled && !ssoConfigured && ( 126 <div className='argo-form-row__error-msg'>Login is disabled. Please contact your system administrator.</div> 127 )} 128 <div className='login__footer'> 129 <a href='https://argoproj.io' target='_blank'> 130 <img className='logo-image' src='assets/images/argologo.svg' alt='argo' /> 131 </a> 132 </div> 133 </div> 134 </div> 135 ); 136 }