update: seperate routes for login and signup added
This commit is contained in:
parent
2c4bc9adb6
commit
a638f02014
46
app/package-lock.json
generated
46
app/package-lock.json
generated
|
@ -17,10 +17,12 @@
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-is": "^17.0.2",
|
"react-is": "^17.0.2",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
|
"styled-components": "^5.3.0",
|
||||||
"typescript": "^4.3.5"
|
"typescript": "^4.3.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react-router-dom": "^5.1.8"
|
"@types/react-router-dom": "^5.1.8",
|
||||||
|
"@types/styled-components": "^5.1.11"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@authorizerdev/authorizer-js": {
|
"node_modules/@authorizerdev/authorizer-js": {
|
||||||
|
@ -271,6 +273,16 @@
|
||||||
"integrity": "sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==",
|
"integrity": "sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/hoist-non-react-statics": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"hoist-non-react-statics": "^3.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/prop-types": {
|
"node_modules/@types/prop-types": {
|
||||||
"version": "15.7.4",
|
"version": "15.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
|
||||||
|
@ -320,6 +332,17 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
||||||
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
|
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/styled-components": {
|
||||||
|
"version": "5.1.25",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.25.tgz",
|
||||||
|
"integrity": "sha512-fgwl+0Pa8pdkwXRoVPP9JbqF0Ivo9llnmsm+7TCI330kbPIFd9qv1Lrhr37shf4tnxCOSu+/IgqM7uJXLWZZNQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/hoist-non-react-statics": "*",
|
||||||
|
"@types/react": "*",
|
||||||
|
"csstype": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ansi-styles": {
|
"node_modules/ansi-styles": {
|
||||||
"version": "3.2.1",
|
"version": "3.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||||
|
@ -1016,6 +1039,16 @@
|
||||||
"integrity": "sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==",
|
"integrity": "sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/hoist-non-react-statics": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"hoist-non-react-statics": "^3.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/prop-types": {
|
"@types/prop-types": {
|
||||||
"version": "15.7.4",
|
"version": "15.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
|
||||||
|
@ -1065,6 +1098,17 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
||||||
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
|
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
|
||||||
},
|
},
|
||||||
|
"@types/styled-components": {
|
||||||
|
"version": "5.1.25",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.25.tgz",
|
||||||
|
"integrity": "sha512-fgwl+0Pa8pdkwXRoVPP9JbqF0Ivo9llnmsm+7TCI330kbPIFd9qv1Lrhr37shf4tnxCOSu+/IgqM7uJXLWZZNQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/hoist-non-react-statics": "*",
|
||||||
|
"@types/react": "*",
|
||||||
|
"csstype": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ansi-styles": {
|
"ansi-styles": {
|
||||||
"version": "3.2.1",
|
"version": "3.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||||
|
|
|
@ -19,9 +19,11 @@
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-is": "^17.0.2",
|
"react-is": "^17.0.2",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"typescript": "^4.3.5"
|
"typescript": "^4.3.5",
|
||||||
|
"styled-components": "^5.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react-router-dom": "^5.1.8"
|
"@types/react-router-dom": "^5.1.8",
|
||||||
|
"@types/styled-components": "^5.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,28 @@
|
||||||
import React, { useEffect, lazy, Suspense } from 'react';
|
import React, { useEffect, lazy, Suspense } from 'react';
|
||||||
import { Switch, Route } from 'react-router-dom';
|
import { Switch, Route } from 'react-router-dom';
|
||||||
import { useAuthorizer } from '@authorizerdev/authorizer-react';
|
import { useAuthorizer } from '@authorizerdev/authorizer-react';
|
||||||
|
import styled, { ThemeProvider } from 'styled-components';
|
||||||
import SetupPassword from './pages/setup-password';
|
import SetupPassword from './pages/setup-password';
|
||||||
|
import { hasWindow, createRandomString } from './utils/common';
|
||||||
|
import { theme } from './theme';
|
||||||
|
|
||||||
const ResetPassword = lazy(() => import('./pages/rest-password'));
|
const ResetPassword = lazy(() => import('./pages/rest-password'));
|
||||||
const Login = lazy(() => import('./pages/login'));
|
const Login = lazy(() => import('./pages/login'));
|
||||||
const Dashboard = lazy(() => import('./pages/dashboard'));
|
const Dashboard = lazy(() => import('./pages/dashboard'));
|
||||||
|
const SignUp = lazy(() => import('./pages/signup'));
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
font-family: ${(props) => props.theme.fonts.fontStack};
|
||||||
|
color: ${(props) => props.theme.colors.textColor};
|
||||||
|
font-size: ${(props) => props.theme.fonts.mediumText};
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
*,
|
||||||
|
*:before,
|
||||||
|
*:after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export default function Root({
|
export default function Root({
|
||||||
globalState,
|
globalState,
|
||||||
|
@ -14,6 +31,29 @@ export default function Root({
|
||||||
}) {
|
}) {
|
||||||
const { token, loading, config } = useAuthorizer();
|
const { token, loading, config } = useAuthorizer();
|
||||||
|
|
||||||
|
const searchParams = new URLSearchParams(
|
||||||
|
hasWindow() ? window.location.search : ``
|
||||||
|
);
|
||||||
|
const state = searchParams.get('state') || createRandomString();
|
||||||
|
const scope = searchParams.get('scope')
|
||||||
|
? searchParams.get('scope')?.toString().split(' ')
|
||||||
|
: ['openid', 'profile', 'email'];
|
||||||
|
|
||||||
|
const urlProps: Record<string, any> = {
|
||||||
|
state,
|
||||||
|
scope,
|
||||||
|
};
|
||||||
|
|
||||||
|
const redirectURL =
|
||||||
|
searchParams.get('redirect_uri') || searchParams.get('redirectURL');
|
||||||
|
if (redirectURL) {
|
||||||
|
urlProps.redirectURL = redirectURL;
|
||||||
|
} else {
|
||||||
|
urlProps.redirectURL = hasWindow() ? window.location.origin : redirectURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
urlProps.redirect_uri = urlProps.redirectURL;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (token) {
|
if (token) {
|
||||||
let redirectURL = config.redirectURL || '/app';
|
let redirectURL = config.redirectURL || '/app';
|
||||||
|
@ -54,9 +94,14 @@ export default function Root({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<></>}>
|
<Suspense fallback={<></>}>
|
||||||
|
<ThemeProvider theme={theme}>
|
||||||
|
<Wrapper>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/app" exact>
|
<Route path="/app" exact>
|
||||||
<Login />
|
<Login urlProps={urlProps} />
|
||||||
|
</Route>
|
||||||
|
<Route path="/app/signup" exact>
|
||||||
|
<SignUp urlProps={urlProps} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/app/reset-password">
|
<Route path="/app/reset-password">
|
||||||
<ResetPassword />
|
<ResetPassword />
|
||||||
|
@ -65,6 +110,8 @@ export default function Root({
|
||||||
<SetupPassword />
|
<SetupPassword />
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
</Wrapper>
|
||||||
|
</ThemeProvider>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,82 @@
|
||||||
import React, { Fragment } from 'react';
|
import React, { Fragment, useState } from 'react';
|
||||||
import { Authorizer } from '@authorizerdev/authorizer-react';
|
import {
|
||||||
|
AuthorizerBasicAuthLogin,
|
||||||
|
AuthorizerForgotPassword,
|
||||||
|
AuthorizerMagicLinkLogin,
|
||||||
|
AuthorizerSocialLogin,
|
||||||
|
useAuthorizer,
|
||||||
|
} from '@authorizerdev/authorizer-react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
export default function Login() {
|
const enum VIEW_TYPES {
|
||||||
|
LOGIN = 'login',
|
||||||
|
FORGOT_PASSWORD = 'forgot-password',
|
||||||
|
}
|
||||||
|
|
||||||
|
const Footer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 15px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const FooterContent = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
|
||||||
|
const { config } = useAuthorizer();
|
||||||
|
const [view, setView] = useState<VIEW_TYPES>(VIEW_TYPES.LOGIN);
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Authorizer />
|
{view === VIEW_TYPES.LOGIN && (
|
||||||
|
<Fragment>
|
||||||
|
<h1 style={{ textAlign: 'center' }}>Login</h1>
|
||||||
|
<br />
|
||||||
|
<AuthorizerSocialLogin urlProps={urlProps} />
|
||||||
|
{config.is_basic_authentication_enabled &&
|
||||||
|
!config.is_magic_link_login_enabled && (
|
||||||
|
<AuthorizerBasicAuthLogin urlProps={urlProps} />
|
||||||
|
)}
|
||||||
|
{config.is_magic_link_login_enabled && (
|
||||||
|
<AuthorizerMagicLinkLogin urlProps={urlProps} />
|
||||||
|
)}
|
||||||
|
<Footer>
|
||||||
|
<Link
|
||||||
|
to="#"
|
||||||
|
onClick={() => setView(VIEW_TYPES.FORGOT_PASSWORD)}
|
||||||
|
style={{ marginBottom: 10 }}
|
||||||
|
>
|
||||||
|
Forgot Password?
|
||||||
|
</Link>
|
||||||
|
</Footer>
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
{view === VIEW_TYPES.FORGOT_PASSWORD && (
|
||||||
|
<Fragment>
|
||||||
|
<h1 style={{ textAlign: 'center' }}>Forgot Password</h1>
|
||||||
|
<AuthorizerForgotPassword urlProps={urlProps} />
|
||||||
|
<Footer>
|
||||||
|
<Link
|
||||||
|
to="#"
|
||||||
|
onClick={() => setView(VIEW_TYPES.LOGIN)}
|
||||||
|
style={{ marginBottom: 10 }}
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</Link>
|
||||||
|
</Footer>
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
{config.is_sign_up_enabled && (
|
||||||
|
<FooterContent>
|
||||||
|
Don't have an account? <Link to="/app/signup"> Sign Up</Link>
|
||||||
|
</FooterContent>
|
||||||
|
)}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
28
app/src/pages/signup.tsx
Normal file
28
app/src/pages/signup.tsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import { AuthorizerSignup } from '@authorizerdev/authorizer-react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
const FooterContent = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default function SignUp({
|
||||||
|
urlProps,
|
||||||
|
}: {
|
||||||
|
urlProps: Record<string, any>;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<h1 style={{ textAlign: 'center' }}>Sign Up</h1>
|
||||||
|
<br />
|
||||||
|
<AuthorizerSignup urlProps={urlProps} />
|
||||||
|
<FooterContent>
|
||||||
|
Already have an account? <Link to="/app"> Login</Link>
|
||||||
|
</FooterContent>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
28
app/src/theme.ts
Normal file
28
app/src/theme.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// colors: https://tailwindcss.com/docs/customizing-colors
|
||||||
|
|
||||||
|
export const theme = {
|
||||||
|
colors: {
|
||||||
|
primary: '#3B82F6',
|
||||||
|
primaryDisabled: '#60A5FA',
|
||||||
|
gray: '#D1D5DB',
|
||||||
|
danger: '#DC2626',
|
||||||
|
success: '#10B981',
|
||||||
|
textColor: '#374151',
|
||||||
|
},
|
||||||
|
fonts: {
|
||||||
|
// typography
|
||||||
|
fontStack: '-apple-system, system-ui, sans-serif',
|
||||||
|
|
||||||
|
// font sizes
|
||||||
|
largeText: '18px',
|
||||||
|
mediumText: '14px',
|
||||||
|
smallText: '12px',
|
||||||
|
tinyText: '10px',
|
||||||
|
},
|
||||||
|
|
||||||
|
radius: {
|
||||||
|
card: '5px',
|
||||||
|
button: '5px',
|
||||||
|
input: '5px',
|
||||||
|
},
|
||||||
|
};
|
|
@ -20,3 +20,5 @@ export const createQueryParams = (params: any) => {
|
||||||
.map((k) => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
|
.map((k) => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
|
||||||
.join('&');
|
.join('&');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const hasWindow = (): boolean => typeof window !== 'undefined';
|
||||||
|
|
Loading…
Reference in New Issue
Block a user