Skip to content

Commit d162df3

Browse files
committed
redirect improvements
1 parent 4cf2068 commit d162df3

2 files changed

Lines changed: 103 additions & 150 deletions

File tree

webapp/src/Auth/AuthBanner.tsx

Lines changed: 95 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import {useState} from 'react';
1+
import { useState } from 'react';
22
import {
3-
TextInput,
4-
PasswordInput,
5-
Paper,
6-
Title,
7-
Container,
8-
Button,
9-
Group,
10-
Divider,
11-
Text,
12-
Alert,
13-
} from '@mantine/core';
3+
TextInput,
4+
PasswordInput,
5+
Paper,
6+
Title,
7+
Container,
8+
Button,
9+
Group,
10+
Divider,
11+
Text,
12+
Alert,
13+
} from '@mantine/core';
1414
import classes from './AuthBanner.module.css';
1515
import { useMutation, useQuery } from '@tanstack/react-query';
1616
import axios from 'axios';
@@ -51,119 +51,99 @@ type LoginPassword = {
5151

5252

5353
export function AuthBanner() {
54-
const [login, setLogin] = useState<string>("");
55-
const [password, setPassword] = useState<string>("");
56-
const [authError, setAuthError] = useState<string>("");
57-
const {authInfo, setAuthInfo} = useAuthContext();
58-
const [oidcRedirectError, setOidcRedirectError] = useState<string>("")
59-
const [showMFAFactors, setShowMFAFactors] = useState<Array<string>>([])
60-
const [factorResponse, setFactorResponse] = useState<FactorResponse>({name: "", code: ""})
61-
const { error, isPending, data } = useQuery<any,any,AuthMethods>({
62-
queryKey: ['authmethods'],
63-
queryFn: () =>
64-
fetch(AppSettings.url + '/authmethods')
54+
const [login, setLogin] = useState<string>("");
55+
const [password, setPassword] = useState<string>("");
56+
const [authError, setAuthError] = useState<string>("");
57+
const { authInfo, setAuthInfo } = useAuthContext();
58+
const [showMFAFactors, setShowMFAFactors] = useState<Array<string>>([])
59+
const [factorResponse, setFactorResponse] = useState<FactorResponse>({ name: "", code: "" })
60+
const { error, isPending, data } = useQuery<any, any, AuthMethods>({
61+
queryKey: ['authmethods'],
62+
queryFn: () =>
63+
fetch(AppSettings.url + '/authmethods')
6564
.then((res) =>
6665
res.json(),
6766
)
68-
})
69-
70-
const authenticate = useMutation({
71-
mutationFn: (loginPassword:LoginPassword) => {
72-
setAuthError("")
73-
return axios.post(AppSettings.url + '/auth', loginPassword)
74-
},
75-
onSuccess: (response) => {
76-
const data = response.data as LoginResponse
77-
if(data.mfaRequired) {
78-
setShowMFAFactors(data.factors)
79-
} else {
80-
setAuthInfo({...authInfo, token: data.token})
81-
}
82-
},
83-
onError: (error) => {
84-
if(error.message.includes("status code 401")) {
85-
setAuthError("Invalid credentials")
86-
} else if(error.message.includes("status code 429")) {
87-
setAuthError("too many attempts. Try again later")
88-
} else {
89-
setAuthError("Error: "+ error.message)
90-
}
91-
}
92-
})
93-
const oidcRedirect = useMutation({
94-
mutationFn: (id:string) => {
95-
return axios.get(AppSettings.url + '/authmethods/oidc/' + id)
96-
},
97-
onSuccess: (response) => {
98-
const data = response.data as OIDCProvider
99-
const redirectURI = data.redirectURI || ""
100-
if(redirectURI === "") {
101-
setOidcRedirectError("Could not redirect at this time: redirectURI is empty")
102-
} else {
103-
window.location.href = redirectURI
104-
}
105-
106-
},
107-
onError: (error) => {
108-
setOidcRedirectError("Could not redirect at this time: "+ error.message)
109-
}
110-
})
111-
const onClickOidcRedirect = (id:string) => {
112-
oidcRedirect.mutate(id)
113-
}
67+
})
11468

115-
const captureEnter = (e: React.KeyboardEvent<HTMLDivElement>) => {
116-
if (e.key === "Enter") {
117-
authenticate.mutate({login, password, factorResponse})
69+
const authenticate = useMutation({
70+
mutationFn: (loginPassword: LoginPassword) => {
71+
setAuthError("")
72+
return axios.post(AppSettings.url + '/auth', loginPassword)
73+
},
74+
onSuccess: (response) => {
75+
const data = response.data as LoginResponse
76+
if (data.mfaRequired) {
77+
setShowMFAFactors(data.factors)
78+
} else {
79+
setAuthInfo({ ...authInfo, token: data.token })
11880
}
81+
},
82+
onError: (error) => {
83+
if (error.message.includes("status code 401")) {
84+
setAuthError("Invalid credentials")
85+
} else if (error.message.includes("status code 429")) {
86+
setAuthError("too many attempts. Try again later")
87+
} else {
88+
setAuthError("Error: " + error.message)
89+
}
90+
}
91+
})
92+
const onClickOidcRedirect = (id: string) => {
93+
window.location.href = AppSettings.url + '/authmethods/oidc/' + id + "/redirect"
94+
}
95+
96+
const captureEnter = (e: React.KeyboardEvent<HTMLDivElement>) => {
97+
if (e.key === "Enter") {
98+
authenticate.mutate({ login, password, factorResponse })
11999
}
120-
const alertIcon = <TbInfoCircle />
121-
122-
if (error) return 'An backend error has occurred: ' + error.message
100+
}
101+
const alertIcon = <TbInfoCircle />
123102

124-
const authMethodsButtons = data?.oidcProviders.map((oidcProvider:OIDCProvider) => (
125-
<Container key={oidcProvider.id}><Button radius="xl" fullWidth={true} key={oidcProvider.id} onClick={() => onClickOidcRedirect
126-
(oidcProvider.id)}>Login with {oidcProvider.name}</Button></Container>
127-
))
103+
if (error) return 'An backend error has occurred: ' + error.message
128104

129-
return (
130-
<Container size={420} my={40}>
131-
<Title ta="center" className={classes.title}>
132-
VPN Server
133-
</Title>
134-
<AuthError />
135-
<Paper withBorder shadow="md" p={30} mt={30} radius="md">
136-
<Group grow mb="md" mt="md">
137-
{oidcRedirectError === "" ? "" : <p>oidcRedirectError</p>}
138-
{authMethodsButtons}
139-
</Group>
140-
{isPending || data?.localAuthDisabled ? null :
105+
const authMethodsButtons = data?.oidcProviders.map((oidcProvider: OIDCProvider) => (
106+
<Container key={oidcProvider.id}><Button radius="xl" fullWidth={true} key={oidcProvider.id} onClick={() => onClickOidcRedirect
107+
(oidcProvider.id)}>Login with {oidcProvider.name}</Button></Container>
108+
))
109+
110+
return (
111+
<Container size={420} my={40}>
112+
<Title ta="center" className={classes.title}>
113+
VPN Server
114+
</Title>
115+
<AuthError />
116+
<Paper withBorder shadow="md" p={30} mt={30} radius="md">
117+
<Group grow mb="md" mt="md">
118+
{authMethodsButtons}
119+
</Group>
120+
{isPending || data?.localAuthDisabled ? null :
121+
<>
122+
{data?.oidcProviders === undefined || data?.oidcProviders.length < 1 ? null :
123+
<Divider label="Or continue with login" labelPosition="center" my="lg" />
124+
}
125+
{authError !== "" ?
126+
<Alert variant="light" color="red" title="Error" icon={alertIcon}>{authError}</Alert>
127+
:
128+
null
129+
}
130+
{showMFAFactors.length > 0 ?
131+
<MFAInput factors={showMFAFactors} setFactorResponse={setFactorResponse} captureEnter={captureEnter} />
132+
:
141133
<>
142-
{data?.oidcProviders === undefined || data?.oidcProviders.length < 1 ? null :
143-
<Divider label="Or continue with login" labelPosition="center" my="lg" />
144-
}
145-
{authError !== "" ?
146-
<Alert variant="light" color="red" title="Error" icon={alertIcon}>{authError}</Alert>
147-
:
148-
null
149-
}
150-
{showMFAFactors.length > 0 ?
151-
<MFAInput factors={showMFAFactors} setFactorResponse={setFactorResponse} captureEnter={captureEnter} />
152-
:
153-
<>
154-
<TextInput label="Login" placeholder="Your username" required onChange={(event) => setLogin(event.currentTarget.value)} value={login} onKeyDown={(e) => captureEnter(e)} />
155-
<PasswordInput label="Password" placeholder="Your password" required mt="md" onChange={(event) => setPassword(event.currentTarget.value)} value={password} onKeyDown={(e) => captureEnter(e)} />
156-
</>
157-
}
158-
<Button fullWidth mt="xl" onClick={() => authenticate.mutate({login, password, factorResponse})}>
159-
Sign in
160-
</Button>
161-
<Text size="xs" style={{marginTop: 20}}>By clicking 'Sign in', you accept the <a href="https://in4it.com/vpn-server-terms-conditions/" target="_blank">Terms & Conditions</a></Text>
134+
<TextInput label="Login" placeholder="Your username" required onChange={(event) => setLogin(event.currentTarget.value)} value={login} onKeyDown={(e) => captureEnter(e)} />
135+
<PasswordInput label="Password" placeholder="Your password" required mt="md" onChange={(event) => setPassword(event.currentTarget.value)} value={password} onKeyDown={(e) => captureEnter(e)} />
162136
</>
163137
}
138+
<Button fullWidth mt="xl" onClick={() => authenticate.mutate({ login, password, factorResponse })}>
139+
Sign in
140+
</Button>
141+
<Text size="xs" style={{ marginTop: 20 }}>By clicking 'Sign in', you accept the <a href="https://in4it.com/vpn-server-terms-conditions/" target="_blank">Terms & Conditions</a></Text>
142+
</>
143+
}
144+
145+
</Paper>
146+
</Container>
147+
);
164148

165-
</Paper>
166-
</Container>
167-
);
168-
169149
}
Lines changed: 8 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,14 @@
1-
import { useEffect, useState } from "react";
1+
import { useEffect } from "react";
22
import { AppSettings } from "../Constants/Constants";
3-
import axios from "axios";
4-
import { useMutation } from "@tanstack/react-query";
53
import { useMatch } from "react-router-dom";
64

7-
type AuthParams = {
8-
loginType: string;
9-
loginID: string;
10-
};
11-
125
export function OIDCAndSAMLRedirect() {
13-
const [redirectError, setRedirectError] = useState<string>("")
14-
const loginMatch = useMatch("/login/:loginType/:loginID");
15-
const redirect = useMutation({
16-
mutationFn: (authParams:AuthParams) => {
17-
return axios.get(AppSettings.url + '/authmethods/'+authParams.loginType+'/' + authParams.loginID)
18-
},
19-
onSuccess: (response) => {
20-
const data = response.data as OIDCProvider
21-
const redirectURI = data.redirectURI || ""
22-
if(redirectURI === "") {
23-
setRedirectError("Could not redirect at this time: redirectURI is empty")
24-
} else {
25-
window.location.href = redirectURI
26-
}
27-
28-
},
29-
onError: (error) => {
30-
setRedirectError("Could not redirect at this time: "+ error.message)
31-
}
32-
})
6+
const loginMatch = useMatch("/login/:loginType/:loginID");
337

34-
useEffect(() => {
35-
if(loginMatch && loginMatch.params.loginType !== undefined && loginMatch.params.loginID !== undefined) {
36-
redirect.mutate({loginType:loginMatch.params.loginType, loginID: loginMatch.params.loginID})
37-
}
38-
}, []);
39-
if(redirectError !== "") return <p>RedirectError</p>
40-
return (<></>)
8+
useEffect(() => {
9+
if (loginMatch && loginMatch.params.loginType !== undefined && loginMatch.params.loginID !== undefined) {
10+
window.location.href = AppSettings.url + '/authmethods/' + loginMatch.params.loginType + '/' + loginMatch.params.loginID + "/redirect"
11+
}
12+
}, []);
13+
return (<></>)
4114
}

0 commit comments

Comments
 (0)