feat: oauth for chrome extension (#4870)

Previously we had to create a separate API key to give access to chrome
extension so we can make calls to the DB. This PR includes logic to
initiate a oauth flow with PKCE method which redirects to the
`Authorise` screen to give access to server tokens.

Implemented in this PR- 
1. make `redirectUrl` a non-nullable parameter 
2. Add `NODE_ENV` to environment variable service
3. new env variable `CHROME_EXTENSION_REDIRECT_URL` on server side
4. strict checks for redirectUrl
5. try catch blocks on utils db query methods
6. refactor Apollo Client to handle `unauthorized` condition
7. input field to enter server url (for self-hosting)
8. state to show user if its already connected
9. show error if oauth flow is cancelled by user

Follow up PR -
Renew token logic

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
Aditya Pimpalkar
2024-04-24 10:45:16 +01:00
committed by GitHub
parent 0a7f82333b
commit c63ee519ea
33 changed files with 18564 additions and 15049 deletions

View File

@@ -255,6 +255,7 @@ export type Mutation = {
deleteOneObject: Object;
deleteUser: User;
emailPasswordResetLink: EmailPasswordResetLink;
exchangeAuthorizationCode: ExchangeAuthCode;
generateApiKeyToken: ApiKeyToken;
generateJWT: AuthTokens;
generateTransientToken: TransientToken;
@@ -282,7 +283,7 @@ export type MutationActivateWorkspaceArgs = {
export type MutationAuthorizeAppArgs = {
clientId: Scalars['String'];
codeChallenge?: InputMaybe<Scalars['String']>;
redirectUrl?: InputMaybe<Scalars['String']>;
redirectUrl: Scalars['String'];
};
@@ -308,6 +309,13 @@ export type MutationEmailPasswordResetLinkArgs = {
};
export type MutationExchangeAuthorizationCodeArgs = {
authorizationCode: Scalars['String'];
clientSecret?: InputMaybe<Scalars['String']>;
codeVerifier?: InputMaybe<Scalars['String']>;
};
export type MutationGenerateApiKeyTokenArgs = {
apiKeyId: Scalars['String'];
expiresAt: Scalars['String'];
@@ -429,7 +437,6 @@ export type Query = {
clientConfig: ClientConfig;
currentUser: User;
currentWorkspace: Workspace;
exchangeAuthorizationCode: ExchangeAuthCode;
findWorkspaceFromInviteHash: Workspace;
getProductPrices: ProductPricesEntity;
getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal;
@@ -457,13 +464,6 @@ export type QueryCheckWorkspaceInviteHashIsValidArgs = {
};
export type QueryExchangeAuthorizationCodeArgs = {
authorizationCode: Scalars['String'];
clientSecret?: InputMaybe<Scalars['String']>;
codeVerifier?: InputMaybe<Scalars['String']>;
};
export type QueryFindWorkspaceFromInviteHashArgs = {
inviteHash: Scalars['String'];
};
@@ -988,6 +988,7 @@ export type AuthTokensFragmentFragment = { __typename?: 'AuthTokenPair', accessT
export type AuthorizeAppMutationVariables = Exact<{
clientId: Scalars['String'];
codeChallenge: Scalars['String'];
redirectUrl: Scalars['String'];
}>;
@@ -1517,8 +1518,12 @@ export type TrackMutationHookResult = ReturnType<typeof useTrackMutation>;
export type TrackMutationResult = Apollo.MutationResult<TrackMutation>;
export type TrackMutationOptions = Apollo.BaseMutationOptions<TrackMutation, TrackMutationVariables>;
export const AuthorizeAppDocument = gql`
mutation authorizeApp($clientId: String!, $codeChallenge: String!) {
authorizeApp(clientId: $clientId, codeChallenge: $codeChallenge) {
mutation authorizeApp($clientId: String!, $codeChallenge: String!, $redirectUrl: String!) {
authorizeApp(
clientId: $clientId
codeChallenge: $codeChallenge
redirectUrl: $redirectUrl
) {
redirectUrl
}
}
@@ -1540,6 +1545,7 @@ export type AuthorizeAppMutationFn = Apollo.MutationFunction<AuthorizeAppMutatio
* variables: {
* clientId: // value for 'clientId'
* codeChallenge: // value for 'codeChallenge'
* redirectUrl: // value for 'redirectUrl'
* },
* });
*/