Skip to content

Commit

Permalink
feat(component): add the ability to provide nonce to the script tag
Browse files Browse the repository at this point in the history
This is unlocking the reCAPTCHA-recommended CSP handling.

Closes #100
  • Loading branch information
DethAriel committed Oct 24, 2018
1 parent df505fd commit 8f55b19
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 1 deletion.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ A simple, configurable, easy-to-start component for handling reCAPTCHA.
* [Resizing](#example-resizing)
* [SystemJS configuration](#example-systemjs)
* [Loading from a different location](#example-different-url)
* [Specifying nonce for Content-Security-Policy](#example-csp-nonce)

## <a name="installation"></a>Installation

Expand Down Expand Up @@ -357,3 +358,20 @@ import { RECAPTCHA_BASE_URL } from 'ng-recaptcha';
],
}) export class MyModule { }
```

### <a name="example-csp-nonce"></a>Specifying nonce for Content-Security-Policy

Per [reCAPTCHA FAQ on CSP](https://developers.google.com/recaptcha/docs/faq#im-using-content-security-policy-csp-on-my-website-how-can-i-configure-it-to-work-with-recaptcha), the recommended approach for that is to supply nonce to the script tag. This is possible by providing the `RECAPTCHA_NONCE` token, for example:

```javascript
import { RECAPTCHA_NONCE } from 'ng-recaptcha';

@NgModule({
providers: [
{
provide: RECAPTCHA_NONCE,
useValue: '<YOUR_NONCE_VALUE>',
},
],
}) export class MyModule { }
```
7 changes: 6 additions & 1 deletion index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
export { RecaptchaComponent } from './recaptcha/recaptcha.component';
export { RecaptchaLoaderService, RECAPTCHA_LANGUAGE, RECAPTCHA_BASE_URL } from './recaptcha/recaptcha-loader.service';
export {
RecaptchaLoaderService,
RECAPTCHA_LANGUAGE,
RECAPTCHA_BASE_URL,
RECAPTCHA_NONCE,
} from './recaptcha/recaptcha-loader.service';
export { RecaptchaModule } from './recaptcha/recaptcha.module';
export { RECAPTCHA_SETTINGS, RecaptchaSettings } from './recaptcha/recaptcha-settings';
9 changes: 9 additions & 0 deletions recaptcha/recaptcha-loader.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { BehaviorSubject, Observable, of } from 'rxjs';

export const RECAPTCHA_LANGUAGE = new InjectionToken<string>('recaptcha-language');
export const RECAPTCHA_BASE_URL = new InjectionToken<string>('recaptcha-base-url');
export const RECAPTCHA_NONCE = new InjectionToken<string>('recaptcha-nonce-tag');

@Injectable()
export class RecaptchaLoaderService {
Expand All @@ -25,15 +26,19 @@ export class RecaptchaLoaderService {
private language: string;
/** @internal */
private baseUrl: string;
/** @internal */
private nonce: string;

constructor(
// tslint:disable-next-line:no-any
@Inject(PLATFORM_ID) private readonly platformId: any,
@Optional() @Inject(RECAPTCHA_LANGUAGE) language?: string,
@Optional() @Inject(RECAPTCHA_BASE_URL) baseUrl?: string,
@Optional() @Inject(RECAPTCHA_NONCE) nonce?: string,
) {
this.language = language;
this.baseUrl = baseUrl;
this.nonce = nonce;
this.init();
this.ready = isPlatformBrowser(this.platformId) ? RecaptchaLoaderService.ready.asObservable() : of();
}
Expand All @@ -53,6 +58,10 @@ export class RecaptchaLoaderService {
const langParam = this.language ? '&hl=' + this.language : '';
const baseUrl = this.baseUrl || 'https://www.google.com/recaptcha/api.js';
script.src = `${baseUrl}?render=explicit&onload=ng2recaptchaloaded${langParam}`;
if (this.nonce) {
// tslint:disable-next-line:no-any Remove "any" cast once we upgrade Angular to 7 and TypeScript along with it
(script as any).nonce = this.nonce;
}
script.async = true;
script.defer = true;
document.head.appendChild(script);
Expand Down

0 comments on commit 8f55b19

Please sign in to comment.