Skip to content

Commit

Permalink
Add Login and LoginContinue support (nextrequest, choice/create still…
Browse files Browse the repository at this point in the history
… TBD)
  • Loading branch information
dauglyon committed Jul 18, 2024
1 parent e75a528 commit 7f5b3ae
Show file tree
Hide file tree
Showing 5 changed files with 298 additions and 78 deletions.
2 changes: 2 additions & 0 deletions src/app/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
} from '../common/hooks';
import ORCIDLinkFeature from '../features/orcidlink';
import { LogIn } from '../features/login/LogIn';
import { LogInContinue } from '../features/login/LogInContinue';
import ORCIDLinkCreateLink from '../features/orcidlink/CreateLink';

export const LOGIN_ROUTE = '/legacy/login';
Expand Down Expand Up @@ -54,6 +55,7 @@ const Routes: FC = () => {

{/* Log In */}
<Route path="/login" element={<LogIn />} />
<Route path="/login/continue" element={<LogInContinue />} />

{/* Navigator */}
<Route
Expand Down
86 changes: 84 additions & 2 deletions src/common/api/authService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,62 @@ interface AuthParams {
search: string;
token: string;
};
getLoginChoice: void;
postLoginPick: { id: string; policyids: string[] };
}

interface AuthResults {
getMe: Me;
getUsers: Record<string, string>;
searchUsers: Record<string, string>;
getLoginChoice: {
// cancelurl: string;
create: {
availablename: string;
id: string;
provemail: string;
provfullname: string;
provusername: string;
}[];
// createurl: string;
creationallowed: true;
expires: number;
login: {
adminonly: boolean;
disabled: boolean;
id: string;
loginallowed: true;
policyids: {
agreedon: number;
id: string;
}[];
provusernames: string[];
user: string;
}[];
// pickurl: string;
provider: string;
// redirecturl: string | null;
// suggestnameurl: string;
};
postLoginPick: {
redirecturl: null | string;
token: {
agent: string;
agentver: string;
created: number;
custom: unknown;
device: unknown;
expires: number;
id: string;
ip: string;
name: unknown;
os: unknown;
osver: unknown;
token: string;
type: string;
user: string;
};
};
}

// Auth does not use JSONRpc, so we use queryFn to make custom queries
Expand Down Expand Up @@ -102,8 +152,40 @@ export const authApi = baseApi.injectEndpoints({
method: 'DELETE',
}),
}),
getLoginChoice: builder.query<
AuthResults['getLoginChoice'],
AuthParams['getLoginChoice']
>({
query: () =>
// MUST have an in-process-login-token cookie
authService({
headers: {
accept: 'application/json',
},
method: 'GET',
url: '/login/choice',
}),
}),
postLoginPick: builder.mutation<
AuthResults['postLoginPick'],
AuthParams['postLoginPick']
>({
query: (pickedChoice) =>
authService({
url: encode`/login/pick`,
body: pickedChoice,
method: 'POST',
}),
}),
}),
});

export const { authFromToken, getMe, getUsers, searchUsers, revokeToken } =
authApi.endpoints;
export const {
authFromToken,
getMe,
getUsers,
searchUsers,
revokeToken,
getLoginChoice,
postLoginPick,
} = authApi.endpoints;
205 changes: 130 additions & 75 deletions src/features/login/LogIn.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
Alert,
Box,
Button,
Container,
Expand All @@ -13,99 +14,153 @@ import orcidLogo from '../../common/assets/orcid.png';
import globusLogo from '../../common/assets/globus.png';
import googleLogo from '../../common/assets/google.webp';
import classes from './LogIn.module.scss';
import { useAppSelector } from '../../common/hooks';
import { useAppParam } from '../params/hooks';
import { useNavigate } from 'react-router-dom';

export const useCheckLoggedIn = () => {
const { initialized, token } = useAppSelector((state) => state.auth);

const navigate = useNavigate();
if (token && initialized) {
// TODO: handle nextrequest
navigate('/narratives');
}
};

export const LogIn: FC = () => {
useCheckLoggedIn();
const nextRequest = useAppParam('nextrequest');

// OAuth Login wont work in dev mode, but send dev users to CI so they can grab their token
const loginOrigin =
process.env.NODE_ENV === 'development'
? 'https://ci.kbase.us'
: document.location.origin;

// Triggering login requires a form POST submission
const loginActionUrl = new URL(
'/services/auth/login/start/',
loginOrigin
).toString();
const loginState = encodeURIComponent(
JSON.stringify({
nextrequest: nextRequest,
origin: loginOrigin,
})
);
const loginRedirectURL = `${loginOrigin}/login/redirect?state=${loginState}`;

return (
<Container maxWidth="sm">
<Stack spacing={2} textAlign="center">
<Stack
direction="row"
spacing={2}
alignItems="center"
justifyContent="center"
>
<img
src={logoRectangle}
alt="KBase circles logo"
className={classes['logo']}
/>
</Stack>
<Typography fontStyle="italic">
A collaborative, open environment for systems biology of plants,
microbes and their communities.
</Typography>
<Paper
elevation={0}
sx={{
padding: 2,
}}
>
<Stack spacing={2}>
<Typography variant="h4" component="h1">
Log in
</Typography>
<form action={loginActionUrl} method="post">
<input hidden name="redirecturl" value={loginRedirectURL} />
<Stack spacing={2} textAlign="center">
<Stack
direction="row"
spacing={2}
alignItems="center"
justifyContent="center"
>
<img
src={logoRectangle}
alt="KBase circles logo"
className={classes['logo']}
/>
</Stack>
<Typography fontStyle="italic">
A collaborative, open environment for systems biology of plants,
microbes and their communities.
</Typography>
<Paper
elevation={0}
sx={{
padding: 2,
}}
>
<Stack spacing={2}>
<Button
variant="outlined"
color="base"
size="large"
startIcon={
<img
src={orcidLogo}
alt="ORCID logo"
className={classes['sso-logo']}
/>
}
>
Continue with ORCID
</Button>
<Box className={classes['separator']} />
<Stack spacing={1}>
<Typography variant="h4" component="h1">
Log in
</Typography>
{process.env.NODE_ENV === 'development' ? (
<Alert severity="error">
DEV MODE: Login will occur on {loginOrigin}
</Alert>
) : (
<></>
)}
<Stack spacing={2}>
<Button
name="provider"
value="Google"
type="submit"
variant="outlined"
color="base"
size="large"
startIcon={
<img
src={googleLogo}
alt="Google logo"
src={orcidLogo}
alt="ORCID logo"
className={classes['sso-logo']}
/>
}
>
Continue with Google
</Button>
<Button
variant="outlined"
color="base"
size="large"
startIcon={
<img
src={globusLogo}
alt="Globus logo"
className={classes['sso-logo']}
/>
}
>
Continue with Globus
Continue with ORCID
</Button>
<Box className={classes['separator']} />
<Stack spacing={1}>
<Button
name="provider"
value="Google"
type="submit"
variant="outlined"
color="base"
size="large"
startIcon={
<img
src={googleLogo}
alt="Google logo"
className={classes['sso-logo']}
/>
}
>
Continue with Google
</Button>
<Button
name="provider"
value="Google"
type="submit"
variant="outlined"
color="base"
size="large"
startIcon={
<img
src={globusLogo}
alt="Globus logo"
className={classes['sso-logo']}
/>
}
>
Continue with Globus
</Button>
</Stack>
</Stack>
<Box className={classes['separator']} />
<Typography>
New to KBase? <Link>Sign up</Link>
</Typography>
<Typography>
<Link
href="https://docs.kbase.us/getting-started/sign-up"
target="_blank"
>
Need help logging in?
</Link>
</Typography>
</Stack>
<Box className={classes['separator']} />
<Typography>
New to KBase? <Link>Sign up</Link>
</Typography>
<Typography>
<Link
href="https://docs.kbase.us/getting-started/sign-up"
target="_blank"
>
Need help logging in?
</Link>
</Typography>
</Stack>
</Paper>
</Stack>
</Paper>
</Stack>
</form>
</Container>
);
};
Loading

0 comments on commit 7f5b3ae

Please sign in to comment.