Skip to content

Commit

Permalink
[refactor] migrate JWT verification from CSR to SSR (#201)
Browse files Browse the repository at this point in the history
  • Loading branch information
TechQuery committed Nov 6, 2023
1 parent bd35ed3 commit e5bbc9f
Show file tree
Hide file tree
Showing 43 changed files with 2,124 additions and 2,219 deletions.
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
auto-install-peers = false
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Open-source Hackathon Platform with Git-based Cloud Development Environment
## Technology stack

- Language: [TypeScript v5][2]
- Component engine: [Nextjs v12][3]
- Component engine: [Nextjs v14][3]
- Component suite: [Bootstrap v5][4]
- State management: [MobX v6][6]
- PWA framework: [Workbox v6][5]
Expand Down Expand Up @@ -60,7 +60,7 @@ The easiest way to deploy your Next.js app is to use the [Vercel Platform][12] f

Check out our [Next.js deployment documentation][17] for more details.

[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fkaiyuanshe%2FOpenHackathon-Web.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fkaiyuanshe%2FOpenHackathon-Web?ref=badge_large)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fkaiyuanshe%2FOpenHackathon-Web.svg?type=large)][18]

[1]: https://react.dev/
[2]: https://www.typescriptlang.org/
Expand All @@ -76,3 +76,4 @@ Check out our [Next.js deployment documentation][17] for more details.
[15]: https://nextjs.org/learn
[16]: https://github.com/vercel/next.js/
[17]: https://nextjs.org/docs/deployment
[18]: https://app.fossa.com/projects/git%2Bgithub.com%2Fkaiyuanshe%2FOpenHackathon-Web?ref=badge_large
5 changes: 3 additions & 2 deletions components/Activity/ActivityCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import classNames from 'classnames';
import Link from 'next/link';
import { Card, Col, Row } from 'react-bootstrap';

import { Activity } from '../../models/Activity';
Expand Down Expand Up @@ -42,10 +41,12 @@ export function ActivityCard({
<Card className={classNames('border-success', className)}>
<Card.Body>
<Card.Title
as="a"
className="text-primary text-truncate text-wrap"
title={displayName}
href={`/activity/${name}`}
>
<Link href={`/activity/${name}`}>{displayName}</Link>
{displayName}
</Card.Title>
<Row as="small" className="g-4" xs={1}>
<Col
Expand Down
30 changes: 9 additions & 21 deletions components/Activity/ActivityManageFrame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,19 @@ import {
faUserSecret,
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { computed, reaction } from 'mobx';
import { computed } from 'mobx';
import { observer } from 'mobx-react';
import { Fragment, PureComponent } from 'react';
import { Container, Nav } from 'react-bootstrap';

import { menus } from '../../configuration/menu';
import activityStore from '../../models/Activity';
import { i18n } from '../../models/Base/Translation';
import sessionStore from '../../models/User/Session';
import { findDeep } from '../../utils/data';
import { MainBreadcrumb } from '../layout/MainBreadcrumb';
import PageHead from '../layout/PageHead';
import { PageHead } from '../layout/PageHead';
import { PlatformAdminFrameProps } from '../PlatformAdmin/PlatformAdminFrame';
import { SessionBox } from '../User/SessionBox';
import { ServerSessionBox } from '../User/ServerSessionBox';

const { t } = i18n;

Expand All @@ -54,19 +53,8 @@ export interface ActivityManageFrameProps extends PlatformAdminFrameProps {

@observer
export class ActivityManageFrame extends PureComponent<ActivityManageFrameProps> {
sessionDisposer = reaction(
() => sessionStore.user,
user => user && this.componentDidMount(),
);

async componentDidMount() {
await activityStore.getOne(this.props.name);

if (!activityStore.currentOne.roles) sessionStore.signOut();
}

componentWillUnmount() {
this.sessionDisposer();
componentDidMount() {
activityStore.getOne(this.props.name);
}

get currentRoute() {
Expand Down Expand Up @@ -138,11 +126,11 @@ export class ActivityManageFrame extends PureComponent<ActivityManageFrameProps>

render() {
const { authorized, currentRoute } = this,
{ children, name, title } = this.props;
{ children, name, title, ...props } = this.props;

return (
<SessionBox
auto
<ServerSessionBox
{...props}
className="d-flex justify-content-center align-items-center"
style={{ height: 'calc(100vh - 3.5rem)' }}
>
Expand All @@ -160,7 +148,7 @@ export class ActivityManageFrame extends PureComponent<ActivityManageFrameProps>
) : (
<div className="display-3">{t('no_permission')}</div>
)}
</SessionBox>
</ServerSessionBox>
);
}
}
13 changes: 8 additions & 5 deletions components/Activity/QuestionnairePreview.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { observer } from 'mobx-react';
import Link from 'next/link';
import { PureComponent } from 'react';
import { Container, Form, Row } from 'react-bootstrap';

Expand Down Expand Up @@ -49,10 +48,14 @@ export class QuestionnaireForm extends PureComponent<QuestionnaireFormProps> {
<legend className="text-center">{t('questionnaire')}</legend>
<small className="text-muted mt-2">
{t('please_complete_all_mandatory_fields_before_you_proceed')}

<Link href="https://ophapiv2-demo.authing.cn/u" passHref>
<a className="text-primary ms-2">{t('personal_profile')}</a>
</Link>
<a
className="text-primary ms-2"
target="_blank"
href="https://ophapiv2-demo.authing.cn/u"
rel="noreferrer"
>
{t('personal_profile')}
</a>
</small>
<ol className="my-3 px-3">{fields.map(this.renderField)}</ol>
</Container>
Expand Down
30 changes: 12 additions & 18 deletions components/PlatformAdmin/PlatformAdminFrame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,21 @@ import {
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Loading } from 'idea-react';
import { makeObservable, observable, reaction } from 'mobx';
import { makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import { Fragment, PureComponent } from 'react';
import { Container, Nav } from 'react-bootstrap';

import { adminMenus } from '../../configuration/menu';
import { i18n } from '../../models/Base/Translation';
import platformAdminStore from '../../models/User/PlatformAdmin';
import sessionStore from '../../models/User/Session';
import { findDeep } from '../../utils/data';
import { MainBreadcrumb } from '../layout/MainBreadcrumb';
import PageHead from '../layout/PageHead';
import { SessionBox } from '../User/SessionBox';
import { PageHead } from '../layout/PageHead';
import {
ServerSessionBox,
ServerSessionBoxProps,
} from '../User/ServerSessionBox';

const { t } = i18n;

Expand All @@ -44,7 +46,7 @@ library.add(
faDesktop,
);

export interface PlatformAdminFrameProps {
export interface PlatformAdminFrameProps extends ServerSessionBoxProps {
title: string;
path?: string;
}
Expand All @@ -59,21 +61,12 @@ export class PlatformAdminFrame extends PureComponent<PlatformAdminFrameProps> {
@observable
loading = false;

sessionDisposer = reaction(
() => sessionStore.user,
user => user && this.componentDidMount(),
);

async componentDidMount() {
this.loading = true;
await platformAdminStore.checkAuthorization();
this.loading = false;
}

componentWillUnmount() {
this.sessionDisposer();
}

get currentRoute() {
const { path = '' } = this.props;

Expand Down Expand Up @@ -123,16 +116,17 @@ export class PlatformAdminFrame extends PureComponent<PlatformAdminFrameProps> {

render() {
const { currentRoute, loading } = this,
{ children, title } = this.props,
{ children, title, ...props } = this.props,
{ isPlatformAdmin } = platformAdminStore;

return (
<SessionBox
auto
<ServerSessionBox
{...props}
className="d-flex justify-content-center align-items-center"
style={{ height: 'calc(100vh - 3.5rem)' }}
>
<PageHead title={`${title} - ${t('platform_management')}`} />

{loading ? (
<Loading />
) : isPlatformAdmin ? (
Expand All @@ -147,7 +141,7 @@ export class PlatformAdminFrame extends PureComponent<PlatformAdminFrameProps> {
) : (
<div className="display-3">{t('no_permission')}</div>
)}
</SessionBox>
</ServerSessionBox>
);
}
}
43 changes: 21 additions & 22 deletions components/Team/TeamManageFrame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Loading } from 'idea-react';
import { computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import Link from 'next/link';
import { JWTProps, RouteProps } from 'next-ssr-middleware';
import { Fragment, PureComponent } from 'react';
import { Col, Nav } from 'react-bootstrap';

Expand All @@ -22,14 +22,16 @@ import sessionStore from '../../models/User/Session';
import { findDeep } from '../../utils/data';
import { ActivityManageFrameProps } from '../Activity/ActivityManageFrame';
import { MainBreadcrumb } from '../layout/MainBreadcrumb';
import PageHead from '../layout/PageHead';
import { SessionBox } from '../User/SessionBox';
import { PageHead } from '../layout/PageHead';
import { ServerSessionBox } from '../User/ServerSessionBox';

const { t } = i18n;

library.add(faTrophy, faUser, faUserSecret, faCloud);

export type TeamManageBaseRouterProps = Record<'path' | 'name' | 'tid', string>;
export type TeamManageBaseParams = Record<'name' | 'tid', string>;

export type TeamManageBaseProps = RouteProps<TeamManageBaseParams> & JWTProps;

export interface TeamManageFrameProps extends ActivityManageFrameProps {
tid: string;
Expand Down Expand Up @@ -87,19 +89,16 @@ export class TeamManageFrame extends PureComponent<TeamManageFrameProps> {
(teamMemberRole === 'admin' ||
(teamMemberRole &&
roles?.includes(teamMemberRole as Staff['type']))) && (
<Link
<Nav.Link
key={title}
href={`/activity/${name}/team/${tid}/manage/${href}`}
passHref
>
<Nav.Link>
<FontAwesomeIcon
icon={icon}
className="text-primary ms-3 me-3"
/>
<span className="d-md-none d-lg-inline">{title}</span>
</Nav.Link>
</Link>
<FontAwesomeIcon
icon={icon}
className="text-primary ms-3 me-3"
/>
<span className="d-md-none d-lg-inline">{title}</span>
</Nav.Link>
),
)}
</Fragment>
Expand Down Expand Up @@ -131,13 +130,11 @@ export class TeamManageFrame extends PureComponent<TeamManageFrameProps> {

render() {
const { authorized, currentRoute, isLoading } = this,
{ children, name, title, tid } = this.props;
{ children, name, title, tid, ...props } = this.props;

return isLoading ? (
<Loading />
) : (
<SessionBox
auto
return (
<ServerSessionBox
{...props}
className={
authorized
? 'row row-cols-xs-1 row-cols-md-2'
Expand All @@ -146,7 +143,9 @@ export class TeamManageFrame extends PureComponent<TeamManageFrameProps> {
>
<PageHead title={`${title} - ${tid} - ${name} ${t('team_manage')}`} />

{authorized ? (
{isLoading ? (
<Loading />
) : authorized ? (
<>
<Col md="auto">{this.renderNav()}</Col>

Expand All @@ -158,7 +157,7 @@ export class TeamManageFrame extends PureComponent<TeamManageFrameProps> {
) : (
<div className="display-3">{t('no_permission')}</div>
)}
</SessionBox>
</ServerSessionBox>
);
}
}
10 changes: 3 additions & 7 deletions components/Team/WorkEdit.tsx → components/Team/WorkEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,11 @@ import { i18n } from '../../models/Base/Translation';

const { t } = i18n;

export interface WorkEditProps {
name: string;
tid: string;
wid?: string;
}
export type WorkEditorProps = Record<'name' | 'tid', string> & { wid?: string };

@observer
export class WorkEdit extends PureComponent<WorkEditProps> {
constructor(props: WorkEditProps) {
export class WorkEditor extends PureComponent<WorkEditorProps> {
constructor(props: WorkEditorProps) {
super(props);
makeObservable(this);
}
Expand Down
10 changes: 7 additions & 3 deletions components/User/AuthingGuard.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import '@authing/native-js-ui-components/lib/index.min.css';

import { Guard, GuardEvents } from '@authing/native-js-ui-components';
import { FC } from 'react';

import sessionStore from '../../models/User/Session';

const title = process.env.NEXT_PUBLIC_SITE_NAME,
AppId = process.env.NEXT_PUBLIC_AUTHING_APP_ID!;

export type AuthingGuardProps = Pick<GuardEvents, 'onLogin' | 'onLoginError'>;

const AuthingGuard = ({
onLogin = console.log,
const AuthingGuard: FC<AuthingGuardProps> = ({
// @ts-ignore
onLogin = profile => sessionStore.signIn(profile, true),
onLoginError = console.error,
}: AuthingGuardProps) => (
}) => (
<div
className="d-flex justify-content-center"
ref={async target => {
Expand Down
13 changes: 13 additions & 0 deletions components/User/ServerSessionBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import dynamic from 'next/dynamic';
import { JWTProps } from 'next-ssr-middleware';
import { FC, HTMLAttributes } from 'react';

const AuthingGuard = dynamic(() => import('./AuthingGuard'), { ssr: false });

export type ServerSessionBoxProps = HTMLAttributes<HTMLDivElement> & JWTProps;

export const ServerSessionBox: FC<ServerSessionBoxProps> = ({
jwtPayload,
children,
...props
}) => <div {...props}>{jwtPayload ? children : <AuthingGuard />}</div>;
8 changes: 7 additions & 1 deletion components/User/UserBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ const UserBar = observer(() => {
<Dropdown.Item href={`/user/${user.id}`}>
{t('home_page')}
</Dropdown.Item>
<Dropdown.Item onClick={() => sessionStore.signOut()}>
<Dropdown.Item
target="_blank"
href="https://ophapiv2-demo.authing.cn/u"
>
{t('edit_profile')}
</Dropdown.Item>
<Dropdown.Item onClick={() => sessionStore.signOut(true)}>
{t('sign_out')}
</Dropdown.Item>
</Dropdown.Menu>
Expand Down
Loading

1 comment on commit e5bbc9f

@github-actions
Copy link

@github-actions github-actions bot commented on e5bbc9f Nov 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for open-hackathon ready!

✅ Preview
https://open-hackathon-ga06s07tg-techquery.vercel.app

Built with commit e5bbc9f.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.