# `siarashield_workspace`

Angular integration for CyberSiara **SiaraShield** captcha.

## Angular compatibility

- Minimum supported Angular: `16`
- Supported range: `16.x` to `21.x`
- Peer dependencies:
  - `@angular/core >=16.0.0 <22.0.0`
  - `@angular/common >=16.0.0 <22.0.0`

## Install

```bash
npm i siarashield_workspace
```

## Quick setup (recommended)

This is the easiest and most reliable flow. It applies all CSP wiring automatically.

```bash
ng add siarashield_workspace
```

If your `angular.json` does not have a `defaultProject`, pass the project name:

```bash
ng add siarashield_workspace --project <yourProjectName>
```

What `ng add` does:
- Updates `src/index.html` (adds `meta[name="csp-nonce"]` + CSP policy)
- Updates `src/app/app.config.ts` (standalone) **or** `src/main.ts` (fallback) to provide Angular `CSP_NONCE`

## Manual setup (if you don’t want `ng add`)

### Step 1: Add CSP meta tags in `src/index.html`

Copy/paste:

```html
<meta name="csp-nonce" content="REPLACE_WITH_NONCE" />
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self' 'nonce-REPLACE_WITH_NONCE' https://embed.mycybersiara.com https://embedcdn.mycybersiara.com;
script-src-elem 'self' 'nonce-REPLACE_WITH_NONCE' https://embed.mycybersiara.com https://embedcdn.mycybersiara.com;
script-src-attr 'none';
connect-src 'self' https://embed.mycybersiara.com https://embedcdn.mycybersiara.com;
style-src 'self' 'nonce-REPLACE_WITH_NONCE' https://embed.mycybersiara.com https://embedcdn.mycybersiara.com https://mycybersiara.com https://fonts.googleapis.com https://cdnjs.cloudflare.com;
style-src-elem 'self' 'nonce-REPLACE_WITH_NONCE' https://embed.mycybersiara.com https://embedcdn.mycybersiara.com https://mycybersiara.com https://fonts.googleapis.com https://cdnjs.cloudflare.com;
style-src-attr 'none';
font-src 'self' https://fonts.gstatic.com https://mycybersiara.com https://cdnjs.cloudflare.com data:;
img-src 'self' data: https://embed.mycybersiara.com https://embedcdn.mycybersiara.com https://mycybersiara.com;
">
```

### Step 2: Provide Angular `CSP_NONCE` (recommended)

In `src/app/app.config.ts`:

```ts
import { ApplicationConfig, CSP_NONCE } from '@angular/core';

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: CSP_NONCE,
      useFactory: () =>
        document.querySelector('meta[name="csp-nonce"]')?.getAttribute('content') ?? null,
    },
    // ...your other providers...
  ],
};
```

### Step 3: Remove old `cspNonce` plumbing (recommended)

You normally **do not need** to pass `cspNonce` from your component.

Remove these if present:
- `[cspNonce]="cspNonce"` in the template
- `protected readonly cspNonce = ...` in the component
- `(window as any).__cspNonce` usage

## Quick start (component integration)

### 1) Put public key in environment

```ts
export const environment = {
  siaraShieldPublicKey: 'YOUR-PUBLIC-KEY',
};
```

### 2) Render component + submit button in template

```html
<siara-shield
  [publicKey]="environment.siaraShieldPublicKey"
  [cspNonce]="cspNonce"
  (token)="onCaptchaToken($event)"
></siara-shield>

<button type="submit" class="CaptchaSubmit" (click)="onSubmit()">Submit</button>
```

### 3) Check captcha before API submit

```ts
import { Component, ViewChild } from '@angular/core';
import { environment } from '../environments/environment';
import { SiaraShieldComponent } from 'siarashield_workspace';

@Component({
  selector: 'app-form',
  standalone: true,
  imports: [SiaraShieldComponent],
  templateUrl: './form.component.html',
})
export class FormComponent {
  protected readonly environment = environment;
  protected readonly cspNonce =
    document.querySelector<HTMLMetaElement>('meta[name="csp-nonce"]')?.content ?? undefined;

  private isSubmitting = false;
  @ViewChild(SiaraShieldComponent) private readonly captcha?: SiaraShieldComponent;

  onCaptchaToken(token: string) {
    console.log('SiaraShield token:', token);
  }

  async onSubmit() {
    if (this.isSubmitting) return;
    this.isSubmitting = true;
    try {
      const ok = await this.captcha?.checkCaptchaAsync();
      if (!ok) return;

      // Call your backend API only after captcha success.
      alert('Form submitted successfully');
    } finally {
      this.isSubmitting = false;
    }
  }
}
```

## Legacy/optional: passing `cspNonce` explicitly

If you already have a server-generated nonce available in code, you can still pass it:
- Component input: `[cspNonce]="myNonce"`
- Function API: `initSiaraShield({ ..., cspNonce: myNonce })`

Common Angular way to read it from the meta tag:

```ts
protected readonly cspNonce =
  document.querySelector<HTMLMetaElement>('meta[name="csp-nonce"]')?.content ?? undefined;
```

If omitted, the plugin auto-resolves the nonce from `meta[name="csp-nonce"]` or an existing `script[nonce]`.

## Function API (alternative)

Template:

```html
<div class="SiaraShield"></div>
<button type="submit" class="CaptchaSubmit" (click)="onSubmit()">Submit</button>
```

TypeScript:

```ts
import { environment } from '../environments/environment';
import { initSiaraShield, checkSiaraShieldCaptcha } from 'siarashield_workspace';

export class FormComponent {
  ngOnInit() {
    void this.initializeCaptcha();
  }

  private async initializeCaptcha() {
    await initSiaraShield({
      publicKey: environment.siaraShieldPublicKey,
      // cspNonce: 'optional-explicit-nonce',
    });
  }

  onSubmit() {
    const result = checkSiaraShieldCaptcha();
    if (!result.ok) return;

    console.log(result.token);
    // API call here
  }
}
```

## Key handling (required)

- **Frontend (Angular): public key only**
- **Backend (.env or secret store): private key only**

Backend example:

```dotenv
SIARASHIELD_PRIVATE_KEY=YOUR-PRIVATE-KEY
```

Never place the private key in Angular code or browser-accessible files.

Get keys from [mycybersiara.com](https://mycybersiara.com).

## CSP guide

If your app has no strict CSP, default integration works without extra setup.

For strict CSP, use a **server-generated nonce per request**. The same nonce must appear in your `Content-Security-Policy` header and in `meta[name="csp-nonce"]` (or an existing `script[nonce]`). Do not ship the literal placeholder `REPLACE_WITH_NONCE` to browsers.

### Recommended policy example

```http
default-src 'self';
script-src 'self' 'nonce-<dynamic>' https://embed.mycybersiara.com https://embedcdn.mycybersiara.com;
script-src-elem 'self' 'nonce-<dynamic>' https://embed.mycybersiara.com https://embedcdn.mycybersiara.com;
script-src-attr 'none';
connect-src 'self' https://embed.mycybersiara.com https://embedcdn.mycybersiara.com;
style-src 'self' 'nonce-<dynamic>' https://embed.mycybersiara.com https://embedcdn.mycybersiara.com https://mycybersiara.com https://fonts.googleapis.com https://cdnjs.cloudflare.com;
style-src-elem 'self' 'nonce-<dynamic>' https://embed.mycybersiara.com https://embedcdn.mycybersiara.com https://mycybersiara.com https://fonts.googleapis.com https://cdnjs.cloudflare.com;
style-src-attr 'none';
font-src 'self' https://fonts.gstatic.com https://mycybersiara.com https://cdnjs.cloudflare.com data:;
img-src 'self' data: https://embed.mycybersiara.com https://embedcdn.mycybersiara.com https://mycybersiara.com;
```

## Script loading behavior

By default (`loadJQuery: true`) the package loads:
- `https://embedcdn.mycybersiara.com/CaptchaFormate/jquery.min.js` (fallback when jQuery not already present)
- `https://embedcdn.mycybersiara.com/CaptchaFormate/CaptchaResources.js`
- `https://embedcdn.mycybersiara.com/CaptchaFormate/SiaraShield_Validation.js`

Most users should **not** add these script tags manually.

If your app already has jQuery, set `[loadJQuery]="false"` (or `loadJQuery: false` in function API).

## Quick troubleshooting

- Captcha not visible:
  - Confirm component is rendered and not hidden.
  - Confirm submit button has `class="CaptchaSubmit"`.
  - For function API, confirm `<div class="SiaraShield"></div>` exists.
- `CheckCaptcha` not available:
  - Ensure initialization completed.
  - Ensure CSP allows required hosts and nonce.
- Token not available immediately:
  - Use async check methods (`checkCaptchaAsync` / `checkSiaraShieldCaptchaAsync`).

## Build and pack (maintainers)

```bash
npm run build:lib
npm run pack:lib
```
