diff --git a/.circleci/config.yml b/.circleci/config.yml
index 31a25f1f3..0f3410535 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -110,7 +110,10 @@ workflows:
deploy-api:
jobs:
- - deploy-api-canary
+ - deploy-api-canary:
+ filters:
+ branches:
+ only: main
- slack/on-hold:
name: slack-notification
context: slack-secrets
diff --git a/api/metadata/databases/twenty/tables/public_tenants.yaml b/api/metadata/databases/twenty/tables/public_tenants.yaml
index a9c8db72d..a239e789c 100644
--- a/api/metadata/databases/twenty/tables/public_tenants.yaml
+++ b/api/metadata/databases/twenty/tables/public_tenants.yaml
@@ -10,13 +10,21 @@ array_relationships:
name: users
schema: public
select_permissions:
+ - role: public
+ permission:
+ columns:
+ - auth0_client_id
+ - domain
+ filter: {}
+ limit: 1
- role: user
permission:
columns:
+ - auth0_client_id
+ - email_domain
- id
- name
- uuid
- - email_domain
filter:
users:
email:
diff --git a/api/migrations/twenty/1675425693191_alter_table_public_tenants_add_column_auth0_client_id/down.sql b/api/migrations/twenty/1675425693191_alter_table_public_tenants_add_column_auth0_client_id/down.sql
new file mode 100644
index 000000000..d9cdfcc79
--- /dev/null
+++ b/api/migrations/twenty/1675425693191_alter_table_public_tenants_add_column_auth0_client_id/down.sql
@@ -0,0 +1 @@
+alter table "public"."tenants" drop column "auth0_client_id";
diff --git a/api/migrations/twenty/1675425693191_alter_table_public_tenants_add_column_auth0_client_id/up.sql b/api/migrations/twenty/1675425693191_alter_table_public_tenants_add_column_auth0_client_id/up.sql
new file mode 100644
index 000000000..da867c001
--- /dev/null
+++ b/api/migrations/twenty/1675425693191_alter_table_public_tenants_add_column_auth0_client_id/up.sql
@@ -0,0 +1 @@
+alter table "public"."tenants" add column "auth0_client_id" text null;
diff --git a/api/migrations/twenty/1675428346925_alter_table_public_tenants_add_column_domain/down.sql b/api/migrations/twenty/1675428346925_alter_table_public_tenants_add_column_domain/down.sql
new file mode 100644
index 000000000..61f33b11a
--- /dev/null
+++ b/api/migrations/twenty/1675428346925_alter_table_public_tenants_add_column_domain/down.sql
@@ -0,0 +1 @@
+alter table "public"."tenants" drop column "domain";
\ No newline at end of file
diff --git a/api/migrations/twenty/1675428346925_alter_table_public_tenants_add_column_domain/up.sql b/api/migrations/twenty/1675428346925_alter_table_public_tenants_add_column_domain/up.sql
new file mode 100644
index 000000000..f421a8a7a
--- /dev/null
+++ b/api/migrations/twenty/1675428346925_alter_table_public_tenants_add_column_domain/up.sql
@@ -0,0 +1,2 @@
+alter table "public"."tenants" add column "domain" text
+ null default 'pilot.twenty.com';
diff --git a/front/src/App.tsx b/front/src/App.tsx
index c94aa3b76..3db1c99b5 100644
--- a/front/src/App.tsx
+++ b/front/src/App.tsx
@@ -1,4 +1,5 @@
-import React from 'react';
+import { Auth0Provider } from '@auth0/auth0-react';
+import React, { useEffect } from 'react';
import Inbox from './pages/inbox/Inbox';
import Contacts from './pages/Contacts';
import Insights from './pages/Insights';
@@ -7,40 +8,59 @@ import AppLayout from './layout/AppLayout';
import RequireAuth from './components/RequireAuth';
import { Routes, Route } from 'react-router-dom';
import { useGetProfile } from './hooks/profile/useGetProfile';
+import { useGetTenantByDomain } from './hooks/tenant/useGetTenantByDomain';
function App() {
+ const { tenant } = useGetTenantByDomain();
const { user } = useGetProfile();
return (
-
-
-
-
-
- }
- />
-
-
-
- }
- />
-
-
-
- }
- />
- } />
-
-
+
+ {tenant && (
+
+
+
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+ } />
+
+
+
+ )}
+
);
}
diff --git a/front/src/hooks/tenant/__tests__/useGetTenantByDomain.test.tsx b/front/src/hooks/tenant/__tests__/useGetTenantByDomain.test.tsx
new file mode 100644
index 000000000..61805bc40
--- /dev/null
+++ b/front/src/hooks/tenant/__tests__/useGetTenantByDomain.test.tsx
@@ -0,0 +1,25 @@
+import { renderHook } from '@testing-library/react';
+import { useQuery, QueryResult } from '@apollo/client';
+import { useGetTenantByDomain } from '../useGetTenantByDomain';
+
+jest.mock('@apollo/client', () => ({
+ useQuery: jest.fn(),
+}));
+
+describe('useGetTenantByDomain', () => {
+ beforeEach(() => {
+ const result: Partial> = {
+ data: { tenants: [{ domain: 'pilot.twenty.com' }] },
+ loading: false,
+ error: undefined,
+ };
+ (useQuery as jest.Mock).mockImplementation(() => result as QueryResult);
+ });
+
+ it('returns tenant by domain', () => {
+ const { result } = renderHook(() => useGetTenantByDomain());
+ const domain = result.current.tenant?.domain;
+ expect(domain).toEqual(result.current.tenant?.domain);
+ expect(useQuery).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/front/src/hooks/tenant/useGetTenantByDomain.tsx b/front/src/hooks/tenant/useGetTenantByDomain.tsx
new file mode 100644
index 000000000..c3132c92c
--- /dev/null
+++ b/front/src/hooks/tenant/useGetTenantByDomain.tsx
@@ -0,0 +1,31 @@
+import { ApolloError, useQuery } from '@apollo/client';
+import { gql } from 'graphql-tag';
+import { Tenant } from '../../interfaces/tenant.interface';
+
+const GET_TENANT_BY_DOMAIN = gql`
+ query GetTenantByDomain($domain: String!) {
+ tenants(where: { domain: { _eq: $domain } }, limit: 1) {
+ auth0_client_id
+ domain
+ }
+ }
+`;
+
+type TenantResult = {
+ loading: boolean;
+ error?: ApolloError;
+ tenant?: Tenant;
+};
+
+export const useGetTenantByDomain = (): TenantResult => {
+ const domain = window.location.hostname;
+ const { loading, error, data } = useQuery(GET_TENANT_BY_DOMAIN, {
+ variables: { domain },
+ context: {
+ headers: {
+ 'x-hasura-default-role': 'public',
+ },
+ },
+ });
+ return { loading, error, tenant: data?.tenants[0] };
+};
diff --git a/front/src/index.tsx b/front/src/index.tsx
index 070a78d40..61333fb9b 100644
--- a/front/src/index.tsx
+++ b/front/src/index.tsx
@@ -3,7 +3,6 @@ import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
-import { Auth0Provider } from '@auth0/auth0-react';
import {
ApolloClient,
InMemoryCache,
@@ -15,12 +14,17 @@ import { setContext } from '@apollo/client/link/context';
const httpLink = createHttpLink({ uri: process.env.REACT_APP_API_URL });
const authLink = setContext((_, { headers }) => {
+ const requestHeaders = { ...headers };
const token = localStorage.getItem('accessToken');
+ const headerContainsPublicRole =
+ requestHeaders.hasOwnProperty('x-hasura-default-role') &&
+ requestHeaders['x-hasura-default-role'] === 'public';
+ if (!headerContainsPublicRole && token) {
+ requestHeaders['authorization'] = `Bearer ${token}`;
+ }
+
return {
- headers: {
- ...headers,
- authorization: token ? `Bearer ${token}` : '',
- },
+ headers: requestHeaders,
};
});
@@ -33,22 +37,9 @@ const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement,
);
root.render(
-
-
-
-
-
-
- ,
+
+
+
+
+ ,
);
diff --git a/front/src/interfaces/tenant.interface.ts b/front/src/interfaces/tenant.interface.ts
index 857c1b211..5c5c9052c 100644
--- a/front/src/interfaces/tenant.interface.ts
+++ b/front/src/interfaces/tenant.interface.ts
@@ -1,4 +1,6 @@
export interface Tenant {
id: number;
name: string;
+ domain: string;
+ auth0_client_id: string;
}
diff --git a/front/src/layout/navbar/__stories__/Navbar.stories.tsx b/front/src/layout/navbar/__stories__/Navbar.stories.tsx
index 234860bec..e6421340f 100644
--- a/front/src/layout/navbar/__stories__/Navbar.stories.tsx
+++ b/front/src/layout/navbar/__stories__/Navbar.stories.tsx
@@ -15,7 +15,12 @@ export const NavbarOnInsights = () => (
email: 'charles@twenty.com',
first_name: 'Charles',
last_name: 'Bochet',
- tenant: { id: 1, name: 'Twenty' },
+ tenant: {
+ id: 1,
+ name: 'Twenty',
+ domain: 'pilot.twenty.com',
+ auth0_client_id: 'auth0_client_id',
+ },
}}
/>
diff --git a/infra/dev/docker-compose.yml b/infra/dev/docker-compose.yml
index 736d868e7..c5fc3aaf0 100644
--- a/infra/dev/docker-compose.yml
+++ b/infra/dev/docker-compose.yml
@@ -36,6 +36,7 @@ services:
HASURA_GRAPHQL_PG_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/twenty
HASURA_GRAPHQL_ENABLE_CONSOLE: "true"
HASURA_GRAPHQL_DEV_MODE: "true"
+ HASURA_GRAPHQL_UNAUTHORIZED_ROLE: "public"
HASURA_GRAPHQL_ADMIN_SECRET: ${HASURA_GRAPHQL_ADMIN_SECRET}
HASURA_GRAPHQL_JWT_SECRET: ${HASURA_GRAPHQL_JWT_SECRET}
HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log