1- import React , { useState , FormEvent } from 'react' ;
1+ import React , { useState , FormEvent , useEffect } from 'react' ;
22import { useNavigate , Navigate } from 'react-router-dom' ;
33import FormControl from '@material-ui/core/FormControl' ;
44import InputLabel from '@material-ui/core/InputLabel' ;
@@ -12,10 +12,10 @@ import CardBody from '../../components/Card/CardBody';
1212import CardFooter from '../../components/Card/CardFooter' ;
1313import axios , { AxiosError } from 'axios' ;
1414import logo from '../../assets/img/git-proxy.png' ;
15- import { Badge , CircularProgress , Snackbar } from '@material-ui/core' ;
16- import { getCookie } from '../../utils' ;
15+ import { Badge , CircularProgress , FormLabel , Snackbar } from '@material-ui/core' ;
1716import { useAuth } from '../../auth/AuthProvider' ;
1817import { API_BASE } from '../../apiBase' ;
18+ import { getAxiosConfig , processAuthError } from '../../services/auth' ;
1919
2020interface LoginResponse {
2121 username : string ;
@@ -34,33 +34,40 @@ const Login: React.FC = () => {
3434 const [ success , setSuccess ] = useState < boolean > ( false ) ;
3535 const [ gitAccountError , setGitAccountError ] = useState < boolean > ( false ) ;
3636 const [ isLoading , setIsLoading ] = useState < boolean > ( false ) ;
37+ const [ authMethods , setAuthMethods ] = useState < string [ ] > ( [ ] ) ;
38+ const [ usernamePasswordMethod , setUsernamePasswordMethod ] = useState < string > ( '' ) ;
39+
40+ useEffect ( ( ) => {
41+ axios . get ( `${ API_BASE } /api/auth/config` ) . then ( ( response ) => {
42+ const usernamePasswordMethod = response . data . usernamePasswordMethod ;
43+ const otherMethods = response . data . otherMethods ;
44+
45+ setUsernamePasswordMethod ( usernamePasswordMethod ) ;
46+ setAuthMethods ( otherMethods ) ;
47+
48+ // Automatically login if only one non-username/password method is enabled
49+ if ( ! usernamePasswordMethod && otherMethods . length === 1 ) {
50+ handleAuthMethodLogin ( otherMethods [ 0 ] ) ;
51+ }
52+ } ) ;
53+ } , [ ] ) ;
3754
3855 function validateForm ( ) : boolean {
3956 return (
4057 username . length > 0 && username . length < 100 && password . length > 0 && password . length < 200
4158 ) ;
4259 }
4360
44- function handleOIDCLogin ( ) : void {
45- window . location . href = `${ API_BASE } /api/auth/oidc ` ;
61+ function handleAuthMethodLogin ( authMethod : string ) : void {
62+ window . location . href = `${ API_BASE } /api/auth/${ authMethod } ` ;
4663 }
4764
4865 function handleSubmit ( event : FormEvent ) : void {
4966 event . preventDefault ( ) ;
5067 setIsLoading ( true ) ;
5168
5269 axios
53- . post < LoginResponse > (
54- loginUrl ,
55- { username, password } ,
56- {
57- withCredentials : true ,
58- headers : {
59- 'Content-Type' : 'application/json' ,
60- 'X-CSRF-TOKEN' : getCookie ( 'csrf' ) || '' ,
61- } ,
62- } ,
63- )
70+ . post < LoginResponse > ( loginUrl , { username, password } , getAxiosConfig ( ) )
6471 . then ( ( ) => {
6572 window . sessionStorage . setItem ( 'git.proxy.login' , 'success' ) ;
6673 setMessage ( 'Success!' ) ;
@@ -72,7 +79,7 @@ const Login: React.FC = () => {
7279 window . sessionStorage . setItem ( 'git.proxy.login' , 'success' ) ;
7380 setGitAccountError ( true ) ;
7481 } else if ( error . response ?. status === 403 ) {
75- setMessage ( 'You do not have the correct access permissions...' ) ;
82+ setMessage ( processAuthError ( error , false ) ) ;
7683 } else {
7784 setMessage ( 'You entered an invalid username or password...' ) ;
7885 }
@@ -113,52 +120,80 @@ const Login: React.FC = () => {
113120 />
114121 </ div >
115122 </ CardHeader >
116- < CardBody >
117- < GridContainer >
118- < GridItem xs = { 12 } sm = { 12 } md = { 12 } >
119- < FormControl fullWidth >
120- < InputLabel htmlFor = 'username' > Username</ InputLabel >
121- < Input
122- id = 'username'
123- type = 'text'
124- value = { username }
125- onChange = { ( e ) => setUsername ( e . target . value ) }
126- autoFocus
127- data-test = 'username'
128- />
129- </ FormControl >
130- </ GridItem >
131- </ GridContainer >
132- < GridContainer >
133- < GridItem xs = { 12 } sm = { 12 } md = { 12 } >
134- < FormControl fullWidth >
135- < InputLabel htmlFor = 'password' > Password</ InputLabel >
136- < Input
137- id = 'password'
138- type = 'password'
139- value = { password }
140- onChange = { ( e ) => setPassword ( e . target . value ) }
141- data-test = 'password'
142- />
143- </ FormControl >
144- </ GridItem >
145- </ GridContainer >
146- </ CardBody >
147- < CardFooter >
123+ { usernamePasswordMethod ? (
124+ < CardBody >
125+ < GridContainer >
126+ < GridItem xs = { 12 } sm = { 12 } md = { 12 } >
127+ < FormLabel component = 'legend' style = { { fontSize : '1.2rem' , marginTop : 10 } } >
128+ Login
129+ </ FormLabel >
130+ < FormControl fullWidth >
131+ < InputLabel htmlFor = 'username' > Username</ InputLabel >
132+ < Input
133+ id = 'username'
134+ type = 'text'
135+ value = { username }
136+ onChange = { ( e ) => setUsername ( e . target . value ) }
137+ autoFocus
138+ data-test = 'username'
139+ />
140+ </ FormControl >
141+ </ GridItem >
142+ </ GridContainer >
143+ < GridContainer >
144+ < GridItem xs = { 12 } sm = { 12 } md = { 12 } >
145+ < FormControl fullWidth >
146+ < InputLabel htmlFor = 'password' > Password</ InputLabel >
147+ < Input
148+ id = 'password'
149+ type = 'password'
150+ value = { password }
151+ onChange = { ( e ) => setPassword ( e . target . value ) }
152+ data-test = 'password'
153+ />
154+ </ FormControl >
155+ </ GridItem >
156+ </ GridContainer >
157+ </ CardBody >
158+ ) : (
159+ < CardBody >
160+ < FormLabel
161+ component = 'legend'
162+ style = { { fontSize : '1rem' , marginTop : 10 , marginBottom : 0 } }
163+ >
164+ Username/password authentication is not enabled at this time.
165+ </ FormLabel >
166+ </ CardBody >
167+ ) }
168+ { /* Show login buttons if available (one on top of the other) */ }
169+ < CardFooter style = { { display : 'flex' , flexDirection : 'column' , gap : 10 } } >
148170 { ! isLoading ? (
149171 < >
150- < Button
151- color = 'success'
152- block
153- disabled = { ! validateForm ( ) }
154- type = 'submit'
155- data-test = 'login'
156- >
157- Login
158- </ Button >
159- < Button color = 'warning' block onClick = { handleOIDCLogin } data-test = 'oidc-login' >
160- Login with OIDC
161- </ Button >
172+ { usernamePasswordMethod && (
173+ < Button
174+ color = 'success'
175+ block
176+ disabled = { ! validateForm ( ) }
177+ type = 'submit'
178+ data-test = 'login'
179+ >
180+ Login
181+ </ Button >
182+ ) }
183+ { authMethods . map ( ( am ) => (
184+ < Button
185+ color = 'success'
186+ block
187+ onClick = { ( ) => handleAuthMethodLogin ( am ) }
188+ data-test = { `${ am } -login` }
189+ key = { am }
190+ >
191+ Login
192+ { authMethods . length > 1 || usernamePasswordMethod
193+ ? ` with ${ am . toUpperCase ( ) } `
194+ : '' }
195+ </ Button >
196+ ) ) }
162197 </ >
163198 ) : (
164199 < div style = { { textAlign : 'center' , width : '100%' , opacity : 0.5 , color : 'green' } } >
@@ -168,7 +203,12 @@ const Login: React.FC = () => {
168203 </ CardFooter >
169204 </ Card >
170205 < div style = { { textAlign : 'center' , opacity : 0.9 , fontSize : 12 , marginTop : 20 } } >
171- < Badge overlap = 'rectangular' color = 'error' badgeContent = 'NEW' />
206+ < Badge
207+ overlap = 'rectangular'
208+ color = 'error'
209+ badgeContent = 'NEW'
210+ style = { { marginRight : 20 } }
211+ />
172212 < span style = { { paddingLeft : 20 } } >
173213 View our < a href = '/dashboard/push' > open source activity feed</ a > or{ ' ' }
174214 < a href = '/dashboard/repo' > scroll through projects</ a > we contribute to
0 commit comments