reCAPTCHA

Google reCAPTCHA v3 protects the unauthenticated forms. It is invisible — there is no challenge to solve; a score-bearing token is produced on submit and verified server-side.

Configuration

Provided by AuthModule, not AppModule, because only auth forms use it:

{ provide: RECAPTCHA_V3_SITE_KEY, useValue: environment.recaptchaKey }

RecaptchaV3Module from ng-recaptcha is imported by AuthModule.

The badge component

src/app/core/pyb/components/recaptcha/recaptcha.compontent.ts — declared in AuthModule as RecaptchaComponent, selector pyb-recaptcha.

It renders nothing (template: ''). Its only job is controlling the visibility of Google’s floating badge, which reCAPTCHA injects into the document body outside Angular’s control:

ngAfterViewInit(): void {
    this.show();
    this.h = setTimeout(() => this.show(), 2000);   (1)
}

ngOnDestroy(): void {
    if (this.h) { clearTimeout(this.h); }
    if (this.document.querySelector('.grecaptcha-badge')) {
        this.renderer.setStyle(this.document.querySelector('.grecaptcha-badge'), 'visibility', 'hidden');   (2)
    }
}

private show(): void {
    if (this.document.querySelector('.grecaptcha-badge')) {
        this.renderer.setStyle(this.document.querySelector('.grecaptcha-badge'), 'visibility', 'visible');
    }
}
1 The badge is injected asynchronously, so a single call on view-init often runs before it exists. The 2 s retry covers that race.
2 On destroy the badge is hidden again, so it does not follow the user into the authenticated application.

The filename is misspelled — recaptcha.compontent.ts. Search for the class name RecaptchaComponent rather than the file.

Both attempts are best-effort. If the badge has still not been injected after two seconds it stays hidden, and Google’s terms require the badge or an equivalent attribution to be visible on protected forms.

Protected forms

Login

AuthService.login(email, password, recaptchaToken) sends the token as a recaptcha header alongside Basic auth.

Registration

Token passed with the account-creation request.

Forgot password

AuthService.requestPassword(email, recaptcha) sends a recaptcha header.

login(email: string, password: string, recaptchaToken: string = ''): Observable<AccessData> {
    const timezone = encodeURIComponent(this.timezone);
    return this.http.get<AccessData>(`${this.env.authUrl}/token?timezon=${timezone}`, {
        headers: {
            Authorization: 'Basic ' + btoa(email + ':' + password),
            recaptcha: recaptchaToken,
        },
    });
}

The token defaults to an empty string, so a call made without executing reCAPTCHA still reaches the server — the backend decides whether to reject it.

Obtaining a token

Components inject ReCaptchaV3Service and execute an action on submit:

this.recaptchaV3Service.execute('login').subscribe(token => {
    this.store.dispatch(/* … login with token … */);
});

Tokens are single-use and short-lived, so they must be generated per submission, not cached.