-
-
Notifications
You must be signed in to change notification settings - Fork 18
Add Cloudflare Turnstile captcha to registration form #1504
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
base: main
Are you sure you want to change the base?
Conversation
gugu
commented
Jan 16, 2026
- Add TurnstileComponent for rendering captcha widget
- Integrate captcha into registration form (SaaS builds only)
- Add turnstileToken to NewAuthUser interface
- Disable submit button until captcha is completed
- Reset captcha widget on failed registration attempts
- Add TurnstileComponent for rendering captcha widget - Integrate captcha into registration form (SaaS builds only) - Add turnstileToken to NewAuthUser interface - Disable submit button until captcha is completed - Reset captcha widget on failed registration attempts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request adds Cloudflare Turnstile captcha protection to the user registration form for SaaS builds only. The implementation includes a reusable TurnstileComponent, integration with the registration form, and proper handling of token lifecycle events.
Changes:
- Created TurnstileComponent for rendering and managing Cloudflare Turnstile captcha widget
- Integrated captcha into registration form with conditional rendering for SaaS builds
- Added turnstileSiteKey configuration to SaaS environment files (test and production keys)
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/src/index.saas.html | Added Cloudflare Turnstile script tag with async/defer loading |
| frontend/src/environments/environment.saas.ts | Added test turnstileSiteKey and formatting updates |
| frontend/src/environments/environment.saas-prod.ts | Added production turnstileSiteKey and formatting updates |
| frontend/src/app/types/turnstile.d.ts | Added TypeScript type definitions for Turnstile API |
| frontend/src/app/models/user.ts | Added optional turnstileToken field to NewAuthUser interface and formatting updates |
| frontend/src/app/components/ui-components/turnstile/turnstile.component.ts | Implemented TurnstileComponent with polling, lifecycle management, and event emissions |
| frontend/src/app/components/ui-components/turnstile/turnstile.component.spec.ts | Added comprehensive unit tests for TurnstileComponent |
| frontend/src/app/components/ui-components/turnstile/turnstile.component.html | Simple template with container div for widget rendering |
| frontend/src/app/components/ui-components/turnstile/turnstile.component.css | Centered layout styles for captcha widget |
| frontend/src/app/components/registration/registration.component.ts | Integrated turnstile widget, added token handlers, reset on error, and conditional token inclusion |
| frontend/src/app/components/registration/registration.component.spec.ts | Added tests for turnstile token handling in registration flow |
| frontend/src/app/components/registration/registration.component.html | Added turnstile widget to form and updated submit button disabled logic |
| frontend/src/app/components/registration/registration.component.css | Adjusted spacing for turnstile widget and formatting updates |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| password: string, | ||
| email: string; | ||
| password: string; | ||
| turnstileToken?: string; |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The backend DTO SaasUsualUserRegisterDS does not include a turnstileToken field. The frontend is sending this token in the request body, but the backend endpoint at POST saas/user/register is not configured to accept or validate it. This means the turnstile token will be silently ignored during registration, defeating the purpose of the captcha implementation.
The backend DTO should be updated to include the turnstileToken field with proper validation decorators, and the registration use case should verify the token using the TurnstileService (similar to how it's done in the invite user endpoint).
| it('should set turnstileToken when onTurnstileToken is called', () => { | ||
| component.onTurnstileToken('new-token'); | ||
| expect(component.turnstileToken).toBe('new-token'); | ||
| }); | ||
|
|
||
| it('should clear turnstileToken when onTurnstileError is called', () => { | ||
| component.turnstileToken = 'existing-token'; | ||
| component.onTurnstileError(); | ||
| expect(component.turnstileToken).toBeNull(); | ||
| }); | ||
|
|
||
| it('should clear turnstileToken when onTurnstileExpired is called', () => { | ||
| component.turnstileToken = 'existing-token'; | ||
| component.onTurnstileExpired(); | ||
| expect(component.turnstileToken).toBeNull(); | ||
| }); |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test coverage for the Turnstile reset functionality is incomplete. While there are tests for token state changes (onTurnstileToken, onTurnstileError, onTurnstileExpired), there's no test verifying that the turnstile widget is actually reset when registration fails. Consider adding a test that simulates a failed registration and verifies that both turnstileWidget.reset() is called and turnstileToken is set to null.
| export class TurnstileComponent implements OnInit, OnDestroy { | ||
| @ViewChild('turnstileContainer', { static: true }) turnstileContainer: ElementRef<HTMLDivElement>; | ||
|
|
||
| @Input() siteKey: string = (environment as any).turnstileSiteKey; |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Turnstile site key configuration is missing from non-SaaS environment files. While the component accesses (environment as any).turnstileSiteKey, this will be undefined in non-SaaS environments. Although the turnstile widget is currently only rendered when isSaas is true, the default value on line 14 of the TurnstileComponent will attempt to read this undefined value. Consider either adding a default fallback value or ensuring the component only attempts to read this property when it's guaranteed to exist.
| if (attempts >= this.MAX_POLL_ATTEMPTS) { | ||
| this._clearPollInterval(); | ||
| console.error('Turnstile script failed to load'); | ||
| this.tokenError.emit(); | ||
| } |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the Turnstile script fails to load (after 50 polling attempts), the component emits a tokenError event and logs to console, but there's no user-visible error message. This leaves users unable to register with no clear indication of what went wrong. Consider adding user-facing error messaging or a fallback mechanism to handle cases where Cloudflare's CDN might be blocked or unavailable.
| @@ -1,5 +1,5 @@ | |||
| import { provideHttpClient } from '@angular/common/http'; | |||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||
| import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; | |||
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused imports fakeAsync, tick.
| import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; | |
| import { ComponentFixture, TestBed } from '@angular/core/testing'; |
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Integrates Turnstile verification into the company member invitation flow for SaaS environments. The captcha widget only appears when running in SaaS mode and the token is passed to the backend which already supports verification. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>