Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(app-check): adds all web providers as an option #812

Merged
merged 12 commits into from
Feb 6, 2025
Merged
5 changes: 5 additions & 0 deletions .changeset/chilly-squids-hammer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@capacitor-firebase/app-check': minor
---

Adds support to use different web App Check providers
eljass marked this conversation as resolved.
Show resolved Hide resolved
31 changes: 26 additions & 5 deletions packages/app-check/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,32 @@ Only available for Web.

#### InitializeOptions

| Prop | Type | Description | Default | Since |
| ------------------------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----- |
| **`debug`** | <code>boolean</code> | If `true`, the debug provider is used. ⚠️ **Attention**: The debug provider allows access to your Firebase resources from unverified devices. Don't use the debug provider in production builds of your app, and don't share your debug builds with untrusted parties. Read more: https://firebase.google.com/docs/app-check/web/debug-provider | <code>false</code> | 1.3.0 |
| **`isTokenAutoRefreshEnabled`** | <code>boolean</code> | If `true`, the SDK automatically refreshes App Check tokens as needed. | <code>false</code> | 1.3.0 |
| **`siteKey`** | <code>string</code> | The reCAPTCHA v3 site key (public key). Only available for Web. | | 1.3.0 |
| Prop | Type | Description | Default | Since |
| ------------------------------- | --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | ----- |
| **`debug`** | <code>boolean</code> | If `true`, the debug provider is used. ⚠️ **Attention**: The debug provider allows access to your Firebase resources from unverified devices. Don't use the debug provider in production builds of your app, and don't share your debug builds with untrusted parties. Read more: https://firebase.google.com/docs/app-check/web/debug-provider | <code>false</code> | 1.3.0 |
| **`isTokenAutoRefreshEnabled`** | <code>boolean</code> | If `true`, the SDK automatically refreshes App Check tokens as needed. | <code>false</code> | 1.3.0 |
| **`provider`** | <code>'ReCaptchaV3Provider' \| 'ReCaptchaEnterpriseProvider' \| 'CustomProvider'</code> | Set used app check provider for Web. Only available for Web. Read more: https://firebase.google.com/docs/app-check/web/custom-provider | <code>ReCaptchaV3Provider</code> | TBA |
| **`customProviderOptions`** | <code><a href="#customprovideroptions">CustomProviderOptions</a></code> | Set custom app check provider options. Only available for Web. Read more: https://firebase.google.com/docs/app-check/web/custom-provider#implement-object | <code>undefined</code> | TBA |
| **`siteKey`** | <code>string</code> | The reCAPTCHA v3 or reCAPTCHA Enterprise site key (public key). Only available for Web. | | 1.3.0 |


#### CustomProviderOptions

Options when creating a {@link CustomProvider}.

| Prop | Type | Description |
| -------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| **`getToken`** | <code>() =&gt; Promise&lt;<a href="#appchecktoken">AppCheckToken</a>&gt;</code> | Function to get an App Check token through a custom provider service. |


#### AppCheckToken

The token returned from an App Check provider.

| Prop | Type | Description |
| ---------------------- | ------------------- | ------------------------------------------------------ |
| **`token`** | <code>string</code> | |
| **`expireTimeMillis`** | <code>number</code> | The local timestamp after which the token will expire. |


#### InstanceFactoryOptions
Expand Down
25 changes: 24 additions & 1 deletion packages/app-check/src/definitions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { PluginListenerHandle } from '@capacitor/core';
import type { CustomProviderOptions } from 'firebase/app-check';

export interface FirebaseAppCheckPlugin {
/**
Expand Down Expand Up @@ -101,7 +102,29 @@ export interface InitializeOptions {
*/
isTokenAutoRefreshEnabled?: boolean;
/**
* The reCAPTCHA v3 site key (public key).
* Set used app check provider for Web.
*
* Only available for Web.
*
* Read more: https://firebase.google.com/docs/app-check/web/custom-provider
*
* @since TBA
eljass marked this conversation as resolved.
Show resolved Hide resolved
* @default ReCaptchaV3Provider
*/
provider?: 'ReCaptchaV3Provider' | 'ReCaptchaEnterpriseProvider' | 'CustomProvider';
eljass marked this conversation as resolved.
Show resolved Hide resolved
/**
* Set custom app check provider options.
*
* Only available for Web.
*
* Read more: https://firebase.google.com/docs/app-check/web/custom-provider#implement-object
*
* @since TBA
eljass marked this conversation as resolved.
Show resolved Hide resolved
* @default undefined
*/
customProviderOptions?: CustomProviderOptions;
/**
* The reCAPTCHA v3 or reCAPTCHA Enterprise site key (public key).
*
* Only available for Web.
*
Expand Down
55 changes: 49 additions & 6 deletions packages/app-check/src/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import { WebPlugin } from '@capacitor/core';
import { getApp } from 'firebase/app';
import type {
AppCheck,
AppCheckOptions,
AppCheckTokenResult,
CustomProvider as CustomProviderType,
Unsubscribe,
} from 'firebase/app-check';
import {
getToken,
initializeAppCheck,
onTokenChanged,
ReCaptchaV3Provider,
setTokenAutoRefreshEnabled,
} from 'firebase/app-check';

Expand All @@ -28,6 +29,10 @@ declare global {
}
}

const isCustomProvider = (_providerClass: unknown, _providerOption: InitializeOptions['provider']): _providerClass is typeof CustomProviderType => {
return !!_providerClass && _providerOption === 'CustomProvider';
}

export class FirebaseAppCheckWeb
extends WebPlugin
implements FirebaseAppCheckPlugin
Expand All @@ -36,6 +41,9 @@ export class FirebaseAppCheckWeb
public static readonly errorNotInitialized =
'AppCheck has not been initialized.';
public static readonly errorSiteKeyMissing = 'siteKey must be provided.';
public static readonly errorProviderMissing = 'AppCheck Provider missing.';
public static readonly errorCustomProviderOptionsMissing =
eljass marked this conversation as resolved.
Show resolved Hide resolved
'customProviderOptions must be provided when using CustomProvider option.';

private _appCheckInstance: AppCheck | undefined;
get appCheckInstance(): AppCheck | undefined {
Expand All @@ -51,6 +59,25 @@ export class FirebaseAppCheckWeb
}
private onTokenChangedListenerUnsubscribe: Unsubscribe | undefined;

private async getProvider(providerName: InitializeOptions['provider'] = 'ReCaptchaV3Provider') {
switch (providerName) {
case 'ReCaptchaV3Provider': {
const { ReCaptchaV3Provider } = await import('firebase/app-check');
return ReCaptchaV3Provider;
}
case 'ReCaptchaEnterpriseProvider': {
const { ReCaptchaEnterpriseProvider } = await import('firebase/app-check');
return ReCaptchaEnterpriseProvider;
}
case 'CustomProvider': {
const { CustomProvider } = await import('firebase/app-check');
return CustomProvider;
}
default:
throw new Error(FirebaseAppCheckWeb.errorProviderMissing);
eljass marked this conversation as resolved.
Show resolved Hide resolved
}
}

public async getToken(options?: GetTokenOptions): Promise<GetTokenResult> {
if (!this.appCheckInstance) {
throw new Error(FirebaseAppCheckWeb.errorNotInitialized);
Expand All @@ -62,15 +89,31 @@ export class FirebaseAppCheckWeb
}

public async initialize(options?: InitializeOptions): Promise<void> {
if (!options?.siteKey) {
throw new Error(FirebaseAppCheckWeb.errorSiteKeyMissing);
}
if (options.debug) {
if (options?.debug) {
self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
}
const ProviderClass = await this.getProvider(options?.provider);
if (!ProviderClass) {
throw new Error(FirebaseAppCheckWeb.errorProviderMissing);
}
let provider: AppCheckOptions['provider'];
if (isCustomProvider(ProviderClass, options?.provider)) {
if (
!options?.customProviderOptions ||
typeof options?.customProviderOptions.getToken !== 'function'
) {
throw new Error(FirebaseAppCheckWeb.errorCustomProviderOptionsMissing);
}
provider = new ProviderClass(options.customProviderOptions);
} else {
if (!options?.siteKey) {
throw new Error(FirebaseAppCheckWeb.errorSiteKeyMissing);
}
provider = new ProviderClass(options.siteKey);
}
const app = getApp();
this.appCheckInstance = initializeAppCheck(app, {
provider: new ReCaptchaV3Provider(options.siteKey),
provider,
isTokenAutoRefreshEnabled: options.isTokenAutoRefreshEnabled,
});
}
Expand Down