Skip to content

Conversation

@gugu
Copy link
Contributor

@gugu 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>
Copilot AI review requested due to automatic review settings January 16, 2026 19:38
Copy link

Copilot AI left a 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;
Copy link

Copilot AI Jan 16, 2026

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).

Copilot uses AI. Check for mistakes.
Comment on lines +99 to +114
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();
});
Copy link

Copilot AI Jan 16, 2026

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.

Copilot uses AI. Check for mistakes.
export class TurnstileComponent implements OnInit, OnDestroy {
@ViewChild('turnstileContainer', { static: true }) turnstileContainer: ElementRef<HTMLDivElement>;

@Input() siteKey: string = (environment as any).turnstileSiteKey;
Copy link

Copilot AI Jan 16, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +53 to +57
if (attempts >= this.MAX_POLL_ATTEMPTS) {
this._clearPollInterval();
console.error('Turnstile script failed to load');
this.tokenError.emit();
}
Copy link

Copilot AI Jan 16, 2026

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.

Copilot uses AI. Check for mistakes.
@@ -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';
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

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

Unused imports fakeAsync, tick.

Suggested change
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';

Copilot uses AI. Check for mistakes.
gugu and others added 2 commits January 16, 2026 20:40
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants