mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-23 03:51:36 +03:00
Store refresh token on login
This commit is contained in:
parent
fe10c0ba7d
commit
cd18e952b9
@ -13,7 +13,7 @@ Twenty development stack is composed of 3 different layers
|
||||
|
||||
## Setup env variables and npmrc variables
|
||||
|
||||
1. `cp ./front/.env.example ./front/.env` and fill with values
|
||||
1. `cp ./infra/dev/.env.example ./infra/dev/.env` and fill with values
|
||||
2. `cp ./front/.npmrc.example ./front/.npmrc` and fill with values
|
||||
|
||||
## Development environment setup with docker-compose (Recommended)
|
||||
|
@ -1 +0,0 @@
|
||||
REACT_APP_API_URL=http://localhost:8080
|
@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import People from './pages/people/People';
|
||||
import Companies from './pages/companies/Companies';
|
||||
import AuthCallback from './pages/auth/Callback';
|
||||
import Login from './pages/auth/Login';
|
||||
import AppLayout from './layout/AppLayout';
|
||||
import { Routes, Route, Navigate } from 'react-router-dom';
|
||||
|
||||
@ -26,6 +28,8 @@ function App() {
|
||||
<Route path="/" element={<Navigate to="/people" replace />} />
|
||||
<Route path="/people" element={<People />} />
|
||||
<Route path="/companies" element={<Companies />} />
|
||||
<Route path="/auth/callback" element={<AuthCallback />} />
|
||||
<Route path="/auth/login" element={<Login />} />
|
||||
</Routes>
|
||||
</AppLayout>
|
||||
}
|
||||
|
17
front/src/pages/auth/Callback.tsx
Normal file
17
front/src/pages/auth/Callback.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useSearchParams, useNavigate } from 'react-router-dom';
|
||||
|
||||
function Callback() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const refreshToken = searchParams.get('refreshToken');
|
||||
localStorage.setItem('refreshToken', refreshToken || '');
|
||||
|
||||
const navigate = useNavigate();
|
||||
useEffect(() => {
|
||||
navigate('/');
|
||||
}, [navigate]);
|
||||
|
||||
return <></>;
|
||||
}
|
||||
|
||||
export default Callback;
|
17
front/src/pages/auth/Login.tsx
Normal file
17
front/src/pages/auth/Login.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
function Login() {
|
||||
const refreshToken = localStorage.getItem('refreshToken');
|
||||
const navigate = useNavigate();
|
||||
useEffect(() => {
|
||||
if (!refreshToken) {
|
||||
window.location.href = process.env.REACT_APP_LOGIN_PROVIDER_URL || '';
|
||||
}
|
||||
navigate('/');
|
||||
}, [refreshToken, navigate]);
|
||||
|
||||
return <></>;
|
||||
}
|
||||
|
||||
export default Login;
|
19
front/src/pages/auth/__stories__/Callback.stories.tsx
Normal file
19
front/src/pages/auth/__stories__/Callback.stories.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import Callback from '../Callback';
|
||||
import { ThemeProvider } from '@emotion/react';
|
||||
import { lightTheme } from '../../../layout/styles/themes';
|
||||
|
||||
const component = {
|
||||
title: 'Callback',
|
||||
component: Callback,
|
||||
};
|
||||
|
||||
export default component;
|
||||
|
||||
export const CallbackDefault = () => (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<MemoryRouter>
|
||||
<Callback />
|
||||
</MemoryRouter>
|
||||
</ThemeProvider>
|
||||
);
|
19
front/src/pages/auth/__stories__/Login.stories.tsx
Normal file
19
front/src/pages/auth/__stories__/Login.stories.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import Login from '../Login';
|
||||
import { ThemeProvider } from '@emotion/react';
|
||||
import { lightTheme } from '../../../layout/styles/themes';
|
||||
|
||||
const component = {
|
||||
title: 'Login',
|
||||
component: Login,
|
||||
};
|
||||
|
||||
export default component;
|
||||
|
||||
export const LoginDefault = () => (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<MemoryRouter>
|
||||
<Login />
|
||||
</MemoryRouter>
|
||||
</ThemeProvider>
|
||||
);
|
7
front/src/pages/auth/__tests__/Callback.test.tsx
Normal file
7
front/src/pages/auth/__tests__/Callback.test.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import { CallbackDefault } from '../__stories__/Callback.stories';
|
||||
|
||||
it('Checks the Callback page render', () => {
|
||||
render(<CallbackDefault />);
|
||||
});
|
15
front/src/pages/auth/__tests__/Login.test.tsx
Normal file
15
front/src/pages/auth/__tests__/Login.test.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { LoginDefault } from '../__stories__/Login.stories';
|
||||
|
||||
const assignMock = jest.fn();
|
||||
|
||||
delete window.location;
|
||||
window.location = { assign: assignMock };
|
||||
|
||||
afterEach(() => {
|
||||
assignMock.mockClear();
|
||||
});
|
||||
|
||||
it('Checks the Login page render', () => {
|
||||
render(<LoginDefault />);
|
||||
});
|
@ -0,0 +1,23 @@
|
||||
table:
|
||||
name: provider_requests
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
id:
|
||||
custom_name: id
|
||||
options:
|
||||
custom_name: options
|
||||
custom_column_names:
|
||||
id: id
|
||||
options: options
|
||||
custom_name: authProviderRequests
|
||||
custom_root_fields:
|
||||
delete: deleteAuthProviderRequests
|
||||
delete_by_pk: deleteAuthProviderRequest
|
||||
insert: insertAuthProviderRequests
|
||||
insert_one: insertAuthProviderRequest
|
||||
select: authProviderRequests
|
||||
select_aggregate: authProviderRequestsAggregate
|
||||
select_by_pk: authProviderRequest
|
||||
update: updateAuthProviderRequests
|
||||
update_by_pk: updateAuthProviderRequest
|
28
hasura/metadata/databases/default/tables/auth_providers.yaml
Normal file
28
hasura/metadata/databases/default/tables/auth_providers.yaml
Normal file
@ -0,0 +1,28 @@
|
||||
table:
|
||||
name: providers
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
id:
|
||||
custom_name: id
|
||||
custom_column_names:
|
||||
id: id
|
||||
custom_name: authProviders
|
||||
custom_root_fields:
|
||||
delete: deleteAuthProviders
|
||||
delete_by_pk: deleteAuthProvider
|
||||
insert: insertAuthProviders
|
||||
insert_one: insertAuthProvider
|
||||
select: authProviders
|
||||
select_aggregate: authProvidersAggregate
|
||||
select_by_pk: authProvider
|
||||
update: updateAuthProviders
|
||||
update_by_pk: updateAuthProvider
|
||||
array_relationships:
|
||||
- name: userProviders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: provider_id
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
@ -0,0 +1,36 @@
|
||||
table:
|
||||
name: refresh_tokens
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
created_at:
|
||||
custom_name: createdAt
|
||||
expires_at:
|
||||
custom_name: expiresAt
|
||||
refresh_token:
|
||||
custom_name: refreshToken
|
||||
refresh_token_hash:
|
||||
custom_name: refreshTokenHash
|
||||
user_id:
|
||||
custom_name: userId
|
||||
custom_column_names:
|
||||
created_at: createdAt
|
||||
expires_at: expiresAt
|
||||
refresh_token: refreshToken
|
||||
refresh_token_hash: refreshTokenHash
|
||||
user_id: userId
|
||||
custom_name: authRefreshTokens
|
||||
custom_root_fields:
|
||||
delete: deleteAuthRefreshTokens
|
||||
delete_by_pk: deleteAuthRefreshToken
|
||||
insert: insertAuthRefreshTokens
|
||||
insert_one: insertAuthRefreshToken
|
||||
select: authRefreshTokens
|
||||
select_aggregate: authRefreshTokensAggregate
|
||||
select_by_pk: authRefreshToken
|
||||
update: updateAuthRefreshTokens
|
||||
update_by_pk: updateAuthRefreshToken
|
||||
object_relationships:
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
35
hasura/metadata/databases/default/tables/auth_roles.yaml
Normal file
35
hasura/metadata/databases/default/tables/auth_roles.yaml
Normal file
@ -0,0 +1,35 @@
|
||||
table:
|
||||
name: roles
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
role:
|
||||
custom_name: role
|
||||
custom_column_names:
|
||||
role: role
|
||||
custom_name: authRoles
|
||||
custom_root_fields:
|
||||
delete: deleteAuthRoles
|
||||
delete_by_pk: deleteAuthRole
|
||||
insert: insertAuthRoles
|
||||
insert_one: insertAuthRole
|
||||
select: authRoles
|
||||
select_aggregate: authRolesAggregate
|
||||
select_by_pk: authRole
|
||||
update: updateAuthRoles
|
||||
update_by_pk: updateAuthRole
|
||||
array_relationships:
|
||||
- name: userRoles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: role
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
- name: usersByDefaultRole
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: default_role
|
||||
table:
|
||||
name: users
|
||||
schema: auth
|
@ -0,0 +1,48 @@
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
access_token:
|
||||
custom_name: accessToken
|
||||
created_at:
|
||||
custom_name: createdAt
|
||||
id:
|
||||
custom_name: id
|
||||
provider_id:
|
||||
custom_name: providerId
|
||||
provider_user_id:
|
||||
custom_name: providerUserId
|
||||
refresh_token:
|
||||
custom_name: refreshToken
|
||||
updated_at:
|
||||
custom_name: updatedAt
|
||||
user_id:
|
||||
custom_name: userId
|
||||
custom_column_names:
|
||||
access_token: accessToken
|
||||
created_at: createdAt
|
||||
id: id
|
||||
provider_id: providerId
|
||||
provider_user_id: providerUserId
|
||||
refresh_token: refreshToken
|
||||
updated_at: updatedAt
|
||||
user_id: userId
|
||||
custom_name: authUserProviders
|
||||
custom_root_fields:
|
||||
delete: deleteAuthUserProviders
|
||||
delete_by_pk: deleteAuthUserProvider
|
||||
insert: insertAuthUserProviders
|
||||
insert_one: insertAuthUserProvider
|
||||
select: authUserProviders
|
||||
select_aggregate: authUserProvidersAggregate
|
||||
select_by_pk: authUserProvider
|
||||
update: updateAuthUserProviders
|
||||
update_by_pk: updateAuthUserProvider
|
||||
object_relationships:
|
||||
- name: provider
|
||||
using:
|
||||
foreign_key_constraint_on: provider_id
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
@ -0,0 +1,36 @@
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
created_at:
|
||||
custom_name: createdAt
|
||||
id:
|
||||
custom_name: id
|
||||
role:
|
||||
custom_name: role
|
||||
user_id:
|
||||
custom_name: userId
|
||||
custom_column_names:
|
||||
created_at: createdAt
|
||||
id: id
|
||||
role: role
|
||||
user_id: userId
|
||||
custom_name: authUserRoles
|
||||
custom_root_fields:
|
||||
delete: deleteAuthUserRoles
|
||||
delete_by_pk: deleteAuthUserRole
|
||||
insert: insertAuthUserRoles
|
||||
insert_one: insertAuthUserRole
|
||||
select: authUserRoles
|
||||
select_aggregate: authUserRolesAggregate
|
||||
select_by_pk: authUserRole
|
||||
update: updateAuthUserRoles
|
||||
update_by_pk: updateAuthUserRole
|
||||
object_relationships:
|
||||
- name: roleByRole
|
||||
using:
|
||||
foreign_key_constraint_on: role
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
@ -0,0 +1,33 @@
|
||||
table:
|
||||
name: user_security_keys
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
credential_id:
|
||||
custom_name: credentialId
|
||||
credential_public_key:
|
||||
custom_name: credentialPublicKey
|
||||
id:
|
||||
custom_name: id
|
||||
user_id:
|
||||
custom_name: userId
|
||||
custom_column_names:
|
||||
credential_id: credentialId
|
||||
credential_public_key: credentialPublicKey
|
||||
id: id
|
||||
user_id: userId
|
||||
custom_name: authUserSecurityKeys
|
||||
custom_root_fields:
|
||||
delete: deleteAuthUserSecurityKeys
|
||||
delete_by_pk: deleteAuthUserSecurityKey
|
||||
insert: insertAuthUserSecurityKeys
|
||||
insert_one: insertAuthUserSecurityKey
|
||||
select: authUserSecurityKeys
|
||||
select_aggregate: authUserSecurityKeysAggregate
|
||||
select_by_pk: authUserSecurityKey
|
||||
update: updateAuthUserSecurityKeys
|
||||
update_by_pk: updateAuthUserSecurityKey
|
||||
object_relationships:
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
122
hasura/metadata/databases/default/tables/auth_users.yaml
Normal file
122
hasura/metadata/databases/default/tables/auth_users.yaml
Normal file
@ -0,0 +1,122 @@
|
||||
table:
|
||||
name: users
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
active_mfa_type:
|
||||
custom_name: activeMfaType
|
||||
avatar_url:
|
||||
custom_name: avatarUrl
|
||||
created_at:
|
||||
custom_name: createdAt
|
||||
default_role:
|
||||
custom_name: defaultRole
|
||||
disabled:
|
||||
custom_name: disabled
|
||||
display_name:
|
||||
custom_name: displayName
|
||||
email:
|
||||
custom_name: email
|
||||
email_verified:
|
||||
custom_name: emailVerified
|
||||
id:
|
||||
custom_name: id
|
||||
is_anonymous:
|
||||
custom_name: isAnonymous
|
||||
last_seen:
|
||||
custom_name: lastSeen
|
||||
locale:
|
||||
custom_name: locale
|
||||
new_email:
|
||||
custom_name: newEmail
|
||||
otp_hash:
|
||||
custom_name: otpHash
|
||||
otp_hash_expires_at:
|
||||
custom_name: otpHashExpiresAt
|
||||
otp_method_last_used:
|
||||
custom_name: otpMethodLastUsed
|
||||
password_hash:
|
||||
custom_name: passwordHash
|
||||
phone_number:
|
||||
custom_name: phoneNumber
|
||||
phone_number_verified:
|
||||
custom_name: phoneNumberVerified
|
||||
ticket:
|
||||
custom_name: ticket
|
||||
ticket_expires_at:
|
||||
custom_name: ticketExpiresAt
|
||||
totp_secret:
|
||||
custom_name: totpSecret
|
||||
updated_at:
|
||||
custom_name: updatedAt
|
||||
webauthn_current_challenge:
|
||||
custom_name: currentChallenge
|
||||
custom_column_names:
|
||||
active_mfa_type: activeMfaType
|
||||
avatar_url: avatarUrl
|
||||
created_at: createdAt
|
||||
default_role: defaultRole
|
||||
disabled: disabled
|
||||
display_name: displayName
|
||||
email: email
|
||||
email_verified: emailVerified
|
||||
id: id
|
||||
is_anonymous: isAnonymous
|
||||
last_seen: lastSeen
|
||||
locale: locale
|
||||
new_email: newEmail
|
||||
otp_hash: otpHash
|
||||
otp_hash_expires_at: otpHashExpiresAt
|
||||
otp_method_last_used: otpMethodLastUsed
|
||||
password_hash: passwordHash
|
||||
phone_number: phoneNumber
|
||||
phone_number_verified: phoneNumberVerified
|
||||
ticket: ticket
|
||||
ticket_expires_at: ticketExpiresAt
|
||||
totp_secret: totpSecret
|
||||
updated_at: updatedAt
|
||||
webauthn_current_challenge: currentChallenge
|
||||
custom_name: users
|
||||
custom_root_fields:
|
||||
delete: deleteUsers
|
||||
delete_by_pk: deleteUser
|
||||
insert: insertUsers
|
||||
insert_one: insertUser
|
||||
select: users
|
||||
select_aggregate: usersAggregate
|
||||
select_by_pk: user
|
||||
update: updateUsers
|
||||
update_by_pk: updateUser
|
||||
object_relationships:
|
||||
- name: defaultRoleByRole
|
||||
using:
|
||||
foreign_key_constraint_on: default_role
|
||||
array_relationships:
|
||||
- name: refreshTokens
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: refresh_tokens
|
||||
schema: auth
|
||||
- name: roles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
- name: securityKeys
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_security_keys
|
||||
schema: auth
|
||||
- name: userProviders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
@ -1,3 +1,11 @@
|
||||
- "!include auth_provider_requests.yaml"
|
||||
- "!include auth_providers.yaml"
|
||||
- "!include auth_refresh_tokens.yaml"
|
||||
- "!include auth_roles.yaml"
|
||||
- "!include auth_user_providers.yaml"
|
||||
- "!include auth_user_roles.yaml"
|
||||
- "!include auth_user_security_keys.yaml"
|
||||
- "!include auth_users.yaml"
|
||||
- "!include public_company.yaml"
|
||||
- "!include public_person.yaml"
|
||||
- "!include public_workspaces.yaml"
|
||||
|
6
infra/dev/.env.example
Normal file
6
infra/dev/.env.example
Normal file
@ -0,0 +1,6 @@
|
||||
HASURA_AUTH_SERVER_URL: http://localhost:4000
|
||||
HASURA_AUTH_CLIENT_URL: http://localhost:3001/auth/callback
|
||||
HASURA_AUTH_PROVIDER_GOOGLE_CLIENT_ID: REPLACE_ME
|
||||
HASURA_AUTH_PROVIDER_GOOGLE_CLIENT_SECRET: REPLACE_ME
|
||||
FRONT_REACT_APP_API_URL=http://localhost:8080
|
||||
FRONT_REACT_APP_LOGIN_PROVIDER_URL=http://localhost:4000/signin/provider/google
|
@ -7,6 +7,9 @@ services:
|
||||
ports:
|
||||
- "3001:3001"
|
||||
- "6006:6006"
|
||||
environment:
|
||||
- REACT_APP_API_URL=${FRONT_REACT_APP_API_URL}
|
||||
- REACT_APP_LOGIN_PROVIDER_URL=${FRONT_REACT_APP_LOGIN_PROVIDER_URL}
|
||||
volumes:
|
||||
- ../../front:/app/front
|
||||
- twenty_node_modules_front:/app/front/node_modules
|
||||
@ -44,6 +47,12 @@ services:
|
||||
npm_package_version: '0'
|
||||
AUTH_SMTP_HOST: mailhog
|
||||
AUTH_SMTP_PORT: 1025
|
||||
AUTH_SERVER_URL: ${HASURA_AUTH_SERVER_URL}
|
||||
AUTH_CLIENT_URL: ${HASURA_AUTH_CLIENT_URL}
|
||||
AUTH_PROVIDER_GOOGLE_ENABLED: true
|
||||
AUTH_PROVIDER_GOOGLE_CLIENT_ID: ${HASURA_AUTH_PROVIDER_GOOGLE_CLIENT_ID}
|
||||
AUTH_PROVIDER_GOOGLE_CLIENT_SECRET: ${HASURA_AUTH_PROVIDER_GOOGLE_CLIENT_SECRET}
|
||||
AUTH_PROVIDER_GOOGLE_SCOPE: email,profile
|
||||
depends_on:
|
||||
- "twenty-hasura"
|
||||
- "postgres"
|
||||
|
@ -2,6 +2,7 @@ FROM node:18.16.0-alpine as front
|
||||
|
||||
ARG FONTAWESOME_NPM_AUTH_TOKEN
|
||||
ARG REACT_APP_API_URL
|
||||
ARG REACT_APP_LOGIN_PROVIDER_URL
|
||||
|
||||
WORKDIR /app/front
|
||||
COPY ./front .
|
||||
|
Loading…
Reference in New Issue
Block a user