โ† All skills
Tencent SkillHub ยท Productivity

Twenty CRM OAuth Mastery

Provides expert OAuth 2.0 implementation, troubleshooting, and token management for Twenty CRM with Google/Microsoft OAuth and email/calendar sync integration.

skill openclawclawhub Free
0 Downloads
0 Stars
0 Installs
0 Score
High Signal

Provides expert OAuth 2.0 implementation, troubleshooting, and token management for Twenty CRM with Google/Microsoft OAuth and email/calendar sync integration.

โฌ‡ 0 downloads โ˜… 0 stars Unverified but indexed

Install for OpenClaw

Quick setup
  1. Download the package from Yavira.
  2. Extract the archive and review SKILL.md first.
  3. Import or place the package into your OpenClaw setup.

Requirements

Target platform
OpenClaw
Install method
Manual import
Extraction
Extract archive
Prerequisites
OpenClaw
Primary doc
SKILL.md

Package facts

Download mode
Yavira redirect
Package format
ZIP package
Source platform
Tencent SkillHub
What's included
README.md, SKILL.md

Validation

  • Use the Yavira download entry.
  • Review SKILL.md after the package is downloaded.
  • Confirm the extracted package contains the expected setup assets.

Install with your agent

Agent handoff

Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.

  1. Download the package from Yavira.
  2. Extract it into a folder your agent can access.
  3. Paste one of the prompts below and point your agent at the extracted folder.
New install

I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete.

Upgrade existing

I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run.

Trust & source

Release facts

Source
Tencent SkillHub
Verification
Indexed source record
Version
1.0.0

Documentation

ClawHub primary doc Primary doc: SKILL.md 19 sections Open source page

Twenty CRM OAuth Mastery Skill

Author: Generated from extensive OAuth debugging sessions in OpenCode Last Updated: 2026-02-08 Version: 1.0

Skill Metadata

name: twenty-oauth-mastery description: Expert-level OAuth authentication knowledge for Twenty CRM including implementation, troubleshooting, and best practices expertise_level: Expert/Mastery category: Authentication applicable_to: - Twenty CRM authentication - Google/Microsoft OAuth - Token refresh management - Domain restrictions - Email/Calendar sync integration prerequisites: - Knowledge of TypeScript/JavaScript - Understanding of OAuth 2.0 protocol - Familiarity with NestJS framework keywords: - oauth - authentication - twenty-crm - google-oauth - microsoft-oauth - token-refresh - sync-integration - domain-restriction

When to Use This Skill

You should use this skill when working on: โœ… Implementing new OAuth providers โœ… Fixing OAuth login issues โœ… Setting up automatic Gmail/Calendar sync after OAuth โœ… Debugging token refresh failures โœ… Configuring domain restrictions โœ… Troubleshooting redirect loops

Quick Reference for Common Issues

IssueFile to CheckQuick FixRedirect loopauth.service.tsRebuild: npx nx build twenty-server.co domain blockedgoogle-auth.controller.tsAdd to allowlist: ['company.com', 'company.co']Sync not startinggoogle.auth.strategy.tsReturn tokens in validate()Cookie not readableController cookie settingsSet httpOnly: falseInfinite loopSignInUpGlobalScopeFormEffect.tsxTrack processed token signatures

1. Twenty CRM OAuth Architecture

Key Files: twenty/packages/twenty-server/src/engine/core-modules/auth/ Structure: auth/ โ”œโ”€โ”€ strategies/ # Passport strategies (Google, Microsoft) โ”œโ”€โ”€ controllers/ # OAuth endpoints and callbacks โ”œโ”€โ”€ services/ # Auth logic, sync setup, token management โ”œโ”€โ”€ guards/ # Auth guards and validation โ””โ”€โ”€ utils/ # Scope configuration, utilities

2. Critical Code Patterns

Passport Strategy Pattern (MUST FOLLOW) @Injectable() export class GoogleStrategy extends PassportStrategy(Strategy, 'google') { constructor(twentyConfigService: TwentyConfigService) { super({ clientID: twentyConfigService.get('AUTH_GOOGLE_CLIENT_ID'), clientSecret: twentyConfigService.get('AUTH_GOOGLE_CLIENT_SECRET'), callbackURL: twentyConfigService.get('AUTH_GOOGLE_CALLBACK_URL'), scope: getGoogleApisOauthScopes(), passReqToCallback: true, // ๐Ÿ”ด CRITICAL: Required for request state }); } async validate( request: GoogleRequest, _accessToken: string, _refreshToken: string, profile: GoogleProfile, ) { // ๐Ÿ”ด CRITICAL: Include tokens in return object // Without this, automatic sync setup fails return { ...profile, accessToken: _accessToken, refreshToken: _refreshToken, hostedDomain: request.query.hosted_domain || profile.emails?.[0]?.value?.split('@')[1], }; } } Why This Matters: passReqToCallback: true: Enables access to request state Token preservation: Required for OAuthSyncService to work

3. Common Issues & Solutions

Issue 1: Redirect Loop After OAuth Symptoms: OAuth completes but user stuck on welcome page Root Causes: Backend not compiled: Source has fix, container running old JavaScript Fix: npx nx build twenty-server docker restart fratres-twenty Missing isSingleDomainMode: Redirect logic not in compiled code Check: docker exec fratres-twenty cat /app/dist/engine/core-modules/auth/services/auth.service.js | grep isSingleDomainMode Cookie domain mismatch: Cookie not accessible Fix: // auth.service.ts - Remove explicit domain attribute res.cookie('tokenPair', JSON.stringify(authTokens), { path: '/', secure: true, sameSite: 'lax', httpOnly: false, // ๐Ÿ”ด Must be false for JavaScript access }); Issue 2: Domain Enforcement Blocking .co Users Symptoms: @company.co rejected, only @company.com allowed Three Places to Fix: Google Strategy (google.auth.strategy.ts): // โŒ WRONG - Hardcoded hd: 'company.com' // โœ… CORRECT - Remove hd parameter // (no hd parameter) Controller (google-auth.controller.ts): // โŒ WRONG - Hardcoded check if (hostedDomain !== 'company.com') { throw ... } // โœ… CORRECT - Allowlist const allowedOAuthDomains = ['company.com', 'company.co']; if (!hostedDomain || !allowedOAuthDomains.includes(hostedDomain)) { throw new UnauthorizedException( `Only ${allowedOAuthDomains.map(d => `@${d}`).join(', ')} allowed` ); } Database (workspaceMetadata table): INSERT INTO "workspaceMetadata" ("id", "workspaceId", "key", "value", "createdAt", "updatedAt") VALUES (gen_random_uuid(), 'workspace-id', 'approvedAccessDomains', '["company.com", "company.co"]', NOW(), NOW()); Issue 3: Automatic Sync Not Triggered Symptoms: User logs in but connected account/sync channels not created Root Cause: Tokens lost in validate() method Fix: // google.auth.strategy.ts validate() async validate(request, accessToken, refreshToken, profile) { // โŒ WRONG - Tokens lost return { ...profile }; // โœ… CORRECT - Tokens preserved return { ...profile, accessToken, refreshToken, }; } Additional Checks: Verify auth.service.ts calls oauthSyncService.setupSyncForOAuthUser() after login Verify tokens are passed to sync service Check Google scopes include gmail.readonly and calendar.events Verify CALENDAR_PROVIDER_GOOGLE_ENABLED=true Issue 4: Frontend Token Processing Loop Symptoms: SignInUpGlobalScopeFormEffect runs repeatedly, infinite API calls Root Cause: Same token processed multiple times Fix: // SignInUpGlobalScopeFormEffect.tsx useEffect(() => { const tokenPairFromUrl = getAuthPairFromUrl(); if (tokenPairFromUrl) { const tokenSignature = JSON.stringify(tokenPairFromUrl); // ๐Ÿ”ด CRITICAL: Skip if already processed if (processedTokenSignatures.current.has(tokenSignature)) { return; } // Track this signature processedTokenSignatures.current.add(tokenSignature); // Now process the token setAuthTokens(tokenPairFromUrl); } }, []);

4. OAuth Sync Integration

When to Use: Users should have Gmail/Calendar auto-connected after OAuth login Implementation: Create OAuthSyncService: async setupSyncForOAuthUser(input: { workspaceId: string; userId: string; workspaceMemberId: string; email: string; accessToken: string; refreshToken: string; scopes: string[]; }) { // 1. Create/update connected account with tokens // 2. Create message channel // 3. Create calendar channel (if enabled) // 4. Queue initial sync jobs } Integrate into AuthService: // auth.service.ts:signInUpWithSocialSSO() const { redirectUrl, authTokens } = await this.generateTokens(...); // ๐Ÿ”ด CRITICAL: Call sync setup BEFORE redirect if (provider === 'google') { try { await this.oauthSyncService.setupSyncForOAuthUser({ workspaceId, userId, email: user.email, accessToken: authTokens.authToken.accessToken, refreshToken: authTokens.authToken.refreshToken, scopes: user.scopes || [], }); } catch (error) { // Log error but don't fail login this.logger.error('Failed to setup OAuth sync', error); } } return { redirectUrl, authTokens }; Critical: Use try/catch to prevent sync setup from failing login Check for existing channels (prevent duplication) Only run for specific providers/domains if needed

5. Token Refresh Management

Token Refresh Pattern: async refreshTokens(refreshToken: string): Promise<ConnectedAccountTokens> { const oAuth2Client = new google.auth.OAuth2(clientId, clientSecret); oAuth2Client.setCredentials({ refresh_token: refreshToken }); try { const { token } = await oAuth2Client.getAccessToken(); // ๐Ÿ”ด CRITICAL: Preserve original refresh token // Google may not return a new one return { accessToken: token, refreshToken: refreshToken, }; } catch (error) { throw parseGoogleOAuthError(error); } } Error Handling: export const parseGoogleOAuthError = (error: unknown) => { const gaxiosError = error as GaxiosError; const code = gaxiosError.response?.status; const reason = gaxiosError.response?.data?.error; switch (code) { case 400: if (reason === 'invalid_grant') { // ๐Ÿ”ด FATAL: Refresh token expired/revoked return new ConnectedAccountRefreshAccessTokenException( 'invalid_grant', ConnectedAccountRefreshAccessTokenExceptionCode.INVALID_REFRESH_TOKEN, ); } break; case 401: return new ConnectedAccountRefreshAccessTokenException( 'unauthorized', ConnectedAccountRefreshAccessTokenExceptionCode.UNAUTHORIZED, ); case 429: // ๐Ÿ”ด RETRYABLE: Rate limit error return new ConnectedAccountRefreshAccessTokenException( 'rate_limit', ConnectedAccountRefreshAccessTokenExceptionCode.RATE_LIMIT_ERROR, ); } return new ConnectedAccountRefreshAccessTokenException('unknown', ...); };

6. Testing Strategies

Unit Testing (Token Refresh) describe('GoogleAPIRefreshAccessTokenService', () => { it('should refresh token successfully', async () => { const mockRefreshToken = 'valid-refresh-token'; const mockNewAccessToken = 'new-access-token'; jest.spyOn(google.auth, 'OAuth2').mockImplementation(() => ({ setCredentials: jest.fn(), getAccessToken: jest.fn().mockResolvedValue({ token: mockNewAccessToken }), })); const result = await service.refreshTokens(mockRefreshToken); expect(result.accessToken).toBe(mockNewAccessToken); expect(result.refreshToken).toBe(mockRefreshToken); // Original preserved }); }); Cookie Injection Test (Playwright) // Test: frontend reads and processes cookie await context.addCookies([{ name: 'tokenPair', value: JSON.stringify({ authToken: { accessToken: 'fake-token' } }), domain: 'isearch.1791technology.com', path: '/', secure: true, sameSite: 'Lax', }]); await page.goto('https://isearch.1791technology.com'); // Check console logs const logs = await page.evaluate(() => window.tokenPairLogs || []); assert(logs.includes('tokenPairPayload from cookies: found')); assert(logs.includes('Setting auth tokens...'));

7. Configuration

Required Environment Variables: # Google OAuth AUTH_GOOGLE_ENABLED=true AUTH_GOOGLE_CLIENT_ID=849758856044-54v9md2rt6ucthch26p8g4etotcb8gth.apps.googleusercontent.com AUTH_GOOGLE_CLIENT_SECRET=GOCSPX-... AUTH_GOOGLE_CALLBACK_URL=https://yourdomain.com/auth/google/redirect # Calendars/Email CALENDAR_PROVIDER_GOOGLE_ENABLED=true MESSAGING_PROVIDER_GMAIL_ENABLED=true # Billing (disable for self-hosted) IS_BILLING_ENABLED=false Google Cloud Console: Redirect URIs: https://yourdomain.com/auth/google/redirect Authorized Origins: https://yourdomain.com

8. Deployment Checklist

Before Deploying: TypeScript source updated Unit tests passing Type check: npx nx typecheck twenty-server Build: npx nx build twenty-server Verify compiled JavaScript has changes (check dist/ folder) Copy dist/ to container Restart container Check health: curl -f /healthz After Deploying: Test OAuth flow manually Check browser console Verify redirect to dashboard (not welcome) Check connected account in database Verify sync channels created (if applicable)

9. Troubleshooting Workflow

Step 1: Verify Container Running New Code docker ps | grep fratres-twenty docker exec fratres-twenty cat /app/dist/engine/core-modules/auth/services/auth.service.js | grep isSingleDomainMode Step 2: Check Google Cloud Console Redirect URIs match production URL Client ID and secret correct OAuth consent screen configured Step 3: Check Environment docker exec fratres-twenty env | grep AUTH_GOOGLE docker exec fratres-twenty env | grep CALENDAR_PROVIDER Step 4: Test OAuth Entry Point curl -v https://yourdomain.com/auth/google | grep Location # Should redirect to accounts.google.com with correct client_id Step 5: Check Database (Sync Issues) -- Check connected accounts SELECT id, handle, provider, "accessToken" IS NOT NULL FROM "connectedAccount" WHERE handle = 'user@example.com'; -- Check sync channels SELECT id, "syncStatus" FROM "messageChannel" WHERE "connectedAccountId" = 'account-id'; Step 6: Check Logs docker logs fratres-twenty --tail 100 | grep -i oauth

10. Common Pitfalls โŒ

Forgetting to rebuild - Source changes don't auto-compile Hardcoding domains - Use allowlists instead Setting httpOnly: true - Frontend can't read tokenPair cookie Losing tokens in validate() - Must return accessToken/refreshToken Not preserving refresh tokens - Google may not return new ones Missing passReqToCallback: true - Can't access request state Not testing with real OAuth - Mock tests miss edge cases Skipping health checks - Container running old code unnoticed

When OAuth Works But Sync Doesn't

Debug Path: Check oauth-sync.service.ts exists and is called Verify tokens passed through validate() Check scopes include gmail.readonly and calendar.events Verify CALENDAR_PROVIDER_GOOGLE_ENABLED=true Check connected account in database Verify sync channels with syncStatus=ONGOING Common Fix: Return tokens in validate() method

When .co Domain Users Can't Login

Debug Path: Check google.auth.strategy.ts for hardcoded hd parameter Check google-auth.controller.ts domain validation Check auth.service.ts domain allowlist Check workspaceMetadata.approvedAccessDomains in database Common Fixes: Remove hardcoded hd parameter Update controller/service allowlists Insert domain into database

When Frontend Gets Stuck on Welcome Page

Debug Path: Check isSingleDomainMode logic in auth.service.ts Check compiled auth.service.js has logic Check computeRedirectURI returns AppPath.Index Check cookie httpOnly attribute Common Fixes: Rebuild backend: npx nx build twenty-server Ensure redirect to dashboard: AppPath.Index Set httpOnly: false on cookie

Quick Commands

# Build backend npx nx build twenty-server # Build frontend npx nx build twenty-front # Typecheck npx nx typecheck twenty-server # Restart container docker restart fratres-twenty # Check logs docker logs fratres-twenty --tail 100 # Health check curl -f https://yourdomain.com/healthz # Test OAuth redirect curl -v https://yourdomain.com/auth/google

Summary

This skill provides expert-level OAuth knowledge for Twenty CRM covering: Architecture: Twenty's OAuth using Passport strategies Common Issues: 5+ major issues with detailed fixes Automatic Sync: Gmail/Calendar sync after OAuth Token Management: Refresh patterns and error handling Testing: Unit and integration test patterns Configuration: Required environment variables Deployment: Step-by-step checklist Troubleshooting: Systematic workflow Use this skill when: Implementing new OAuth provider Fixing OAuth login issues Setting up automatic sync integration Debugging token refresh failures Configuring domain restrictions Troubleshooting redirect loops

Category context

Workflow acceleration for inboxes, docs, calendars, planning, and execution loops.

Source: Tencent SkillHub

Largest current source with strong distribution and engagement signals.

Package contents

Included in package
2 Docs
  • SKILL.md Primary doc
  • README.md Docs