refactor(server): auth (#5895)

Remove `next-auth` and implement our own Authorization/Authentication system from scratch.

## Server

- [x] tokens
  - [x] function
  - [x] encryption

- [x] AuthController
  - [x] /api/auth/sign-in
  - [x] /api/auth/sign-out
  - [x] /api/auth/session
  - [x] /api/auth/session (WE SUPPORT MULTI-ACCOUNT!)

- [x] OAuthPlugin
  - [x] OAuthController
  - [x] /oauth/login
  - [x] /oauth/callback
  - [x] Providers
    - [x] Google
    - [x] GitHub

## Client

- [x] useSession
- [x] cloudSignIn
- [x] cloudSignOut

## NOTE:

Tests will be adding in the future
This commit is contained in:
liuyi
2024-03-12 10:00:09 +00:00
parent af49e8cc41
commit fb3a0e7b8f
148 changed files with 3407 additions and 2851 deletions

View File

@@ -73,6 +73,7 @@ test.describe('login first', () => {
await page.getByTestId('workspace-modal-account-option').click();
await page.getByTestId('workspace-modal-sign-out-option').click();
await page.getByTestId('confirm-sign-out-button').click();
await page.reload();
await clickSideBarCurrentWorkspaceBanner(page);
const signInButton = page.getByTestId('cloud-signin-button');
await expect(signInButton).toBeVisible();

View File

@@ -33,9 +33,7 @@ export async function getLatestMailMessage() {
export async function getLoginCookie(
context: BrowserContext
): Promise<Cookie | undefined> {
return (await context.cookies()).find(
c => c.name === 'next-auth.session-token'
);
return (await context.cookies()).find(c => c.name === 'sid');
}
const cloudUserSchema = z.object({
@@ -106,7 +104,7 @@ export async function createRandomUser(): Promise<{
await client.user.create({
data: {
...user,
emailVerified: new Date(),
emailVerifiedAt: new Date(),
password: await hash(user.password),
features: {
create: {

View File

@@ -1,10 +1,6 @@
import '@affine/component/theme/global.css';
import '@affine/component/theme/theme.css';
import { createI18n } from '@affine/i18n';
import MockSessionContext, {
mockAuthStates,
// @ts-ignore
} from '@tomfreudenberg/next-auth-mock';
import { ThemeProvider, useTheme } from 'next-themes';
import { useDarkMode } from 'storybook-dark-mode';
import { AffineContext } from '@affine/component/context';
@@ -40,51 +36,6 @@ export const parameters = {
},
};
const SB_PARAMETER_KEY = 'nextAuthMock';
export const mockAuthPreviewToolbarItem = ({
name = 'mockAuthState',
description = 'Set authentication state',
defaultValue = null,
icon = 'user',
items = mockAuthStates,
} = {}) => {
return {
mockAuthState: {
name,
description,
defaultValue,
toolbar: {
icon,
items: Object.keys(items).map(e => ({
value: e,
title: items[e].title,
})),
},
},
};
};
export const withMockAuth: Decorator = (Story, context) => {
// Set a session value for mocking
const session = (() => {
// Allow overwrite of session value by parameter in story
const paramValue = context?.parameters[SB_PARAMETER_KEY];
if (typeof paramValue?.session === 'string') {
return mockAuthStates[paramValue.session]?.session;
} else {
return paramValue?.session
? paramValue.session
: mockAuthStates[context.globals.mockAuthState]?.session;
}
})();
return (
<MockSessionContext session={session}>
<Story {...context} />
</MockSessionContext>
);
};
const i18n = createI18n();
const withI18n: Decorator = (Story, context) => {
const locale = context.globals.locale;
@@ -198,7 +149,6 @@ const withPlatformSelectionDecorator: Decorator = (Story, context) => {
const decorators = [
withContextDecorator,
withI18n,
withMockAuth,
withPlatformSelectionDecorator,
];

View File

@@ -40,7 +40,6 @@
"@storybook/react": "^7.6.17",
"@storybook/react-vite": "^7.6.17",
"@storybook/test-runner": "^0.16.0",
"@tomfreudenberg/next-auth-mock": "^0.5.6",
"@vanilla-extract/esbuild-plugin": "^2.3.5",
"@vitejs/plugin-react": "^4.2.1",
"chromatic": "^11.0.0",