Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Secure connexion between TinyBird and webhookResponseGraph #7913

Merged
merged 8 commits into from
Oct 21, 2024

Conversation

anamarn
Copy link
Contributor

@anamarn anamarn commented Oct 21, 2024

TLDR:
Secure connexion between tinybird and twenty using jwt when accessing datasource from tinybird.

Solves:
https://github.com/twentyhq/private-issues/issues/73

In order to test:

  1. Set ANALYTICS_ENABLED to true
  2. Set TINYBIRD_JWT_TOKEN to the ADMIN token from the workspace twenty_analytics_playground
  3. Set TINYBIRD_JWT_TOKEN to the datasource or your admin token from the workspace twenty_analytics_playground
  4. Create a Webhook in twenty and set wich events it needs to track
  5. Run twenty-worker in order to make the webhooks work.
  6. Do your tasks in order to populate the data
  7. Enter to settings> webhook>your webhook and the statistics section should be displayed.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

PR Summary

This pull request introduces JWT support for secure connections between TinyBird and the webhook response graph. Key changes include:

  • Added analyticsTinybirdJwt field to User type in GraphQL schema
  • Created new useAnalyticsTinybirdJwt hook for retrieving TinyBird JWT
  • Modified fetchGraphDataOrThrow to use JWT for TinyBird API authorization
  • Updated AnalyticsService to generate and use JWT for TinyBird authentication
  • Added new environment variables for TinyBird JWT configuration
  • Integrated AnalyticsModule into UserModule and UserResolver

These changes enhance security by replacing hardcoded tokens with dynamic JWTs for TinyBird data access.

11 file(s) reviewed, 18 comment(s)
Edit PR Review Bot Settings | Greptile

@@ -7,6 +7,7 @@ export type CurrentUser = Pick<
| 'id'
| 'email'
| 'supportUserHash'
| 'analyticsTinybirdJwt'
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Consider adding a comment explaining the purpose of 'analyticsTinybirdJwt' for future maintainers

@@ -0,0 +1,8 @@
import { useRecoilValue } from 'recoil';
Copy link
Contributor

Choose a reason for hiding this comment

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

style: consider using more specific imports from recoil to reduce bundle size


export const useAnalyticsTinybirdJwt = (): string | null | undefined => {
const currentUser = useRecoilValue(currentUserState);
return currentUser?.analyticsTinybirdJwt;
Copy link
Contributor

Choose a reason for hiding this comment

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

style: consider adding a null check before accessing analyticsTinybirdJwt

import { fetchGraphDataOrThrow } from '@/settings/developers/webhook/utils/fetchGraphDataOrThrow';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';

export const useGraphData = (webhookId: string) => {
const { enqueueSnackBar } = useSnackBar();
const supportTinybirdJwt = useAnalyticsTinybirdJwt();
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Consider adding a comment explaining the purpose of supportTinybirdJwt

const fetchGraphData = async (
windowLengthGraphOption: '7D' | '1D' | '12H' | '4H',
) => {
try {
return await fetchGraphDataOrThrow({
webhookId,
windowLength: windowLengthGraphOption,
tinybirdJwt: supportTinybirdJwt ?? undefined,
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Use optional chaining instead of nullish coalescing

],
};

return this.jwtService.sign(payload);
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: JWT signing options (like expiration) are not specified. Consider adding these for better security.


@IsString()
@ValidateIf((env) => env.ANALYTICS_ENABLED)
TINYBIRD_TOKEN_EXPIRES_IN: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Consider adding a validator to ensure TINYBIRD_TOKEN_EXPIRES_IN is a valid duration string

@@ -55,6 +56,7 @@ export class UserResolver {
private readonly onboardingService: OnboardingService,
private readonly userVarService: UserVarsService,
private readonly fileService: FileService,
private readonly analyticsservice: AnalyticsService,
Copy link
Contributor

Choose a reason for hiding this comment

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

syntax: Typo in service name: 'analyticsservice' should be 'analyticsService'

Comment on lines 162 to 166
async analyticsTinybirdJwt(
@AuthWorkspace() workspace: Workspace | undefined,
): Promise<string | null> {
return await this.analyticsservice.generateJWT(workspace?.id);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: Consider adding error handling for cases where JWT generation fails

Comment on lines 159 to 166
@ResolveField(() => String, {
nullable: true,
})
async analyticsTinybirdJwt(
@AuthWorkspace() workspace: Workspace | undefined,
): Promise<string | null> {
return await this.analyticsservice.generateJWT(workspace?.id);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Add documentation for the new analyticsTinybirdJwt field


import { currentUserState } from '@/auth/states/currentUserState';

export const useAnalyticsTinybirdJwt = (): string | null | undefined => {
Copy link
Member

Choose a reason for hiding this comment

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

can it be null

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes it can be null when ANALYTICS is not enabled in the server

const fetchGraphData = async (
windowLengthGraphOption: '7D' | '1D' | '12H' | '4H',
) => {
try {
return await fetchGraphDataOrThrow({
webhookId,
windowLength: windowLengthGraphOption,
tinybirdJwt: supportTinybirdJwt ?? undefined,
Copy link
Member

Choose a reason for hiding this comment

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

?? undefined is not useful, we should throw before if the jwt is undefined

@@ -4,22 +4,24 @@ import { WEBHOOK_GRAPH_API_OPTIONS_MAP } from '@/settings/developers/webhook/con
type fetchGraphDataOrThrowProps = {
webhookId: string;
windowLength: '7D' | '1D' | '12H' | '4H';
tinybirdJwt?: string;
Copy link
Member

Choose a reason for hiding this comment

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

should be required

@@ -1,17 +1,34 @@
/* eslint-disable no-restricted-imports */
Copy link
Member

Choose a reason for hiding this comment

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

why?

@@ -58,7 +60,7 @@ export class AnalyticsService {
const config: AxiosRequestConfig = {
headers: {
Authorization:
'Bearer ' + this.environmentService.get('TINYBIRD_TOKEN'),
'Bearer ' + this.environmentService.get('TINYBIRD_DATASOURCE_TOKEN'),
Copy link
Member

Choose a reason for hiding this comment

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

TINYBIRD_INGEST_TOKEN
(vs TINYBIRD_GENERATE_JWT_TOKEN)

@@ -86,4 +88,22 @@ export class AnalyticsService {

return { success: true };
}

async generateJWT(workspaceId: string | null | undefined) {
Copy link
Member

Choose a reason for hiding this comment

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

generateWorkspaceJwt

@@ -55,6 +56,7 @@ export class UserResolver {
private readonly onboardingService: OnboardingService,
private readonly userVarService: UserVarsService,
private readonly fileService: FileService,
private readonly analyticsservice: AnalyticsService,
Copy link
Member

Choose a reason for hiding this comment

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

analyticsService


@IsString()
@ValidateIf((env) => env.ANALYTICS_ENABLED)
TINYBIRD_TOKEN: string;
TINYBIRD_DATASOURCE_TOKEN: string;
Copy link
Member

Choose a reason for hiding this comment

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

add these to doc

@charlesBochet charlesBochet merged commit 373926b into main Oct 21, 2024
12 checks passed
@charlesBochet charlesBochet deleted the feat/tinybird-jwt branch October 21, 2024 15:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants