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 — |
|
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 |
|
Registration |
Token passed with the account-creation request. |
Forgot password |
|
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.