@capgo/capacitor-social-login 8.2.23 → 8.2.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -78,7 +78,8 @@ const config: CapacitorConfig = {
78
78
  facebook: true, // Use false to reduce app size
79
79
  apple: true, // Apple uses system APIs, no external deps
80
80
  twitter: false // false = disabled (not bundled)
81
- }
81
+ },
82
+ logLevel: 1 // Warnings and errors only
82
83
  }
83
84
  }
84
85
  };
@@ -166,6 +167,86 @@ const res = await SocialLogin.login({
166
167
 
167
168
  Docs: [How to setup facebook login](https://capgo.app/docs/plugins/social-login/facebook/)
168
169
 
170
+ 📘 **[Complete Facebook Business Login Guide](./docs/facebook_business_login.md)** - Learn how to access Instagram, Pages, and business features
171
+
172
+ ### Facebook Business Login
173
+
174
+ This plugin fully supports Facebook Business Login for accessing business-related features and permissions. Business accounts can request additional permissions beyond standard consumer login, including Instagram and Pages management.
175
+
176
+ **Supported Business Permissions:**
177
+ - `instagram_basic` - Access to Instagram Basic Display API
178
+ - `instagram_manage_insights` - Access to Instagram Insights
179
+ - `pages_show_list` - List of Pages the person manages
180
+ - `pages_read_engagement` - Read engagement data from Pages
181
+ - `pages_manage_posts` - Manage posts on Pages
182
+ - `business_management` - Manage business assets
183
+ - And many more - see [Facebook Permissions Reference](https://developers.facebook.com/docs/permissions/reference)
184
+
185
+ **Configuration Requirements:**
186
+ 1. Your Facebook app must be configured as a Business app in the Facebook Developer Console
187
+ 2. Business permissions may require Facebook's App Review before production use
188
+ 3. Your app must comply with Facebook's Business Use Case policies
189
+
190
+ **Example - Instagram Basic Access:**
191
+ ```typescript
192
+ await SocialLogin.initialize({
193
+ facebook: {
194
+ appId: 'your-business-app-id',
195
+ clientToken: 'your-client-token',
196
+ },
197
+ });
198
+
199
+ const res = await SocialLogin.login({
200
+ provider: 'facebook',
201
+ options: {
202
+ permissions: [
203
+ 'email',
204
+ 'public_profile',
205
+ 'instagram_basic', // Instagram account info
206
+ 'pages_show_list', // List of managed Pages
207
+ 'pages_read_engagement' // Page engagement data
208
+ ],
209
+ },
210
+ });
211
+
212
+ // Access Instagram data through Facebook Graph API
213
+ const profile = await SocialLogin.providerSpecificCall({
214
+ call: 'facebook#getProfile',
215
+ options: {
216
+ fields: ['id', 'name', 'email', 'instagram_business_account'],
217
+ },
218
+ });
219
+ ```
220
+
221
+ **Example - Pages Management:**
222
+ ```typescript
223
+ const res = await SocialLogin.login({
224
+ provider: 'facebook',
225
+ options: {
226
+ permissions: [
227
+ 'email',
228
+ 'pages_show_list',
229
+ 'pages_manage_posts',
230
+ 'pages_read_engagement',
231
+ ],
232
+ },
233
+ });
234
+
235
+ // Fetch user's managed pages with Instagram accounts
236
+ const profile = await SocialLogin.providerSpecificCall({
237
+ call: 'facebook#getProfile',
238
+ options: {
239
+ fields: ['id', 'name', 'accounts{id,name,instagram_business_account}'],
240
+ },
241
+ });
242
+ ```
243
+
244
+ **Important Notes:**
245
+ - Testing: You can test business permissions with test users and development apps without App Review
246
+ - Production: Most business permissions require Facebook App Review before going live
247
+ - Rate Limits: Business APIs have different rate limits - review Facebook's documentation
248
+ - Setup: Follow [Facebook Business Integration Guide](https://developers.facebook.com/docs/development/create-an-app/app-dashboard/business-integrations)
249
+
169
250
  ### Android configuration
170
251
 
171
252
  More information can be found here: https://developers.facebook.com/docs/android/getting-started
@@ -58,6 +58,11 @@ public class GoogleProvider implements SocialProvider {
58
58
  private static final String GOOGLE_DATA_PREFERENCE = "GOOGLE_LOGIN_GOOGLE_DATA_9158025e-947d-4211-ba51-40451630cc47";
59
59
  private static final Integer FUTURE_LIST_LENGTH = 128;
60
60
  private static final String TOKEN_REQUEST_URL = "https://www.googleapis.com/oauth2/v3/tokeninfo";
61
+ private static final String[] DEFAULT_SCOPES = new String[] {
62
+ "https://www.googleapis.com/auth/userinfo.email",
63
+ "https://www.googleapis.com/auth/userinfo.profile",
64
+ "openid"
65
+ };
61
66
 
62
67
  public static final Integer REQUEST_AUTHORIZE_GOOGLE_MIN = 583892990;
63
68
  public static final Integer REQUEST_AUTHORIZE_GOOGLE_MAX = REQUEST_AUTHORIZE_GOOGLE_MIN + GoogleProvider.FUTURE_LIST_LENGTH;
@@ -104,6 +109,19 @@ public class GoogleProvider implements SocialProvider {
104
109
  JSONObject object = new JSONObject(data);
105
110
  GoogleProvider.this.idToken = object.optString("idToken", null);
106
111
  GoogleProvider.this.accessToken = object.optString("accessToken", null);
112
+ JSONArray storedScopes = object.optJSONArray("scopes");
113
+ if (storedScopes != null && storedScopes.length() > 0) {
114
+ List<String> scopesList = new ArrayList<>(storedScopes.length());
115
+ for (int i = 0; i < storedScopes.length(); i++) {
116
+ String scope = storedScopes.optString(i, null);
117
+ if (scope != null && !scope.isEmpty()) {
118
+ scopesList.add(scope);
119
+ }
120
+ }
121
+ if (!scopesList.isEmpty()) {
122
+ GoogleProvider.this.scopes = scopesList.toArray(new String[0]);
123
+ }
124
+ }
107
125
 
108
126
  Log.i(SocialLoginPlugin.LOG_TAG, String.format("Google restoreState: %s", object));
109
127
  } catch (JSONException e) {
@@ -384,9 +402,22 @@ public class GoogleProvider implements SocialProvider {
384
402
  }
385
403
 
386
404
  private void persistState(String idToken, String accessToken) throws JSONException {
405
+ persistState(idToken, accessToken, GoogleProvider.this.scopes);
406
+ }
407
+
408
+ private void persistState(String idToken, String accessToken, String[] scopes) throws JSONException {
387
409
  JSONObject object = new JSONObject();
388
410
  object.put("idToken", idToken);
389
411
  object.put("accessToken", accessToken);
412
+ if (scopes != null && scopes.length > 0) {
413
+ JSONArray scopesArray = new JSONArray();
414
+ for (String scope : scopes) {
415
+ if (scope != null && !scope.isEmpty()) {
416
+ scopesArray.put(scope);
417
+ }
418
+ }
419
+ object.put("scopes", scopesArray);
420
+ }
390
421
 
391
422
  GoogleProvider.this.idToken = idToken;
392
423
  GoogleProvider.this.accessToken = accessToken;
@@ -415,6 +446,10 @@ public class GoogleProvider implements SocialProvider {
415
446
  // return accessToken;
416
447
 
417
448
  ListenableFuture<AuthorizationResult> future = CallbackToFutureAdapter.getFuture((completer) -> {
449
+ // Scopes can be null when restoring state after an app restart; default to the base OIDC/userinfo scopes.
450
+ if (GoogleProvider.this.scopes == null || GoogleProvider.this.scopes.length == 0) {
451
+ GoogleProvider.this.scopes = DEFAULT_SCOPES;
452
+ }
418
453
  List<Scope> scopes = new ArrayList<>(this.scopes.length);
419
454
  for (String scope : this.scopes) {
420
455
  scopes.add(new Scope(scope));
@@ -835,7 +870,119 @@ public class GoogleProvider implements SocialProvider {
835
870
 
836
871
  @Override
837
872
  public void refresh(PluginCall call) {
838
- // Implement refresh logic here
839
- call.reject("Not implemented");
873
+ if (this.mode == GoogleProviderLoginType.OFFLINE) {
874
+ call.reject("refresh is not implemented when using offline mode");
875
+ return;
876
+ }
877
+ if (this.clientId == null || this.clientId.isEmpty()) {
878
+ call.reject("Google Sign-In failed: Client ID is not set");
879
+ return;
880
+ }
881
+ if (this.credentialManager == null) {
882
+ this.credentialManager = CredentialManager.create(activity);
883
+ }
884
+
885
+ // If both tokens are still valid, do nothing (mirrors iOS refreshTokensIfNeeded behavior).
886
+ try {
887
+ if (GoogleProvider.this.idToken != null && GoogleProvider.this.accessToken != null) {
888
+ boolean isValidIdToken = idTokenValid(GoogleProvider.this.idToken);
889
+ boolean isValidAccessToken = accessTokenIsValid(GoogleProvider.this.accessToken).get(7, TimeUnit.SECONDS);
890
+ if (isValidIdToken && isValidAccessToken) {
891
+ call.resolve();
892
+ return;
893
+ }
894
+ }
895
+ } catch (Exception e) {
896
+ // Non-fatal: fall through to attempt refresh.
897
+ Log.w(LOG_TAG, "Error checking token validity during refresh", e);
898
+ }
899
+
900
+ // Ensure we have scopes for the authorization flow (may be null after restoreState).
901
+ if (GoogleProvider.this.scopes == null || GoogleProvider.this.scopes.length == 0) {
902
+ GoogleProvider.this.scopes = DEFAULT_SCOPES;
903
+ }
904
+
905
+ // 1) Retrieve a fresh ID token via Credential Manager (may be silent or may require user interaction).
906
+ // 2) Retrieve a fresh access token via the Authorization API.
907
+ GetCredentialRequest request = new GetCredentialRequest.Builder()
908
+ .addCredentialOption(
909
+ new GetGoogleIdOption.Builder()
910
+ .setServerClientId(this.clientId)
911
+ .setFilterByAuthorizedAccounts(true)
912
+ .setAutoSelectEnabled(true)
913
+ .build()
914
+ )
915
+ .build();
916
+
917
+ ExecutorService executor = Executors.newSingleThreadExecutor();
918
+ credentialManager.getCredentialAsync(
919
+ context,
920
+ request,
921
+ null,
922
+ executor,
923
+ new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() {
924
+ @Override
925
+ public void onResult(GetCredentialResponse result) {
926
+ try {
927
+ Credential credential = result.getCredential();
928
+ if (!(credential instanceof CustomCredential)) {
929
+ call.reject("Failed to refresh tokens: unexpected credential type");
930
+ return;
931
+ }
932
+
933
+ if (!GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL.equals(credential.getType())) {
934
+ call.reject("Failed to refresh tokens: unexpected credential subtype");
935
+ return;
936
+ }
937
+
938
+ GoogleIdTokenCredential googleIdTokenCredential = GoogleIdTokenCredential.createFrom(
939
+ ((CustomCredential) credential).getData()
940
+ );
941
+ String newIdToken = googleIdTokenCredential.getIdToken();
942
+
943
+ ListenableFuture<AuthorizationResult> future = getAuthorizationResult(false);
944
+
945
+ ExecutorService authExecutor = Executors.newSingleThreadExecutor();
946
+ authExecutor.execute(
947
+ new Runnable() {
948
+ @Override
949
+ public void run() {
950
+ try {
951
+ AuthorizationResult authResult = future.get(60, TimeUnit.SECONDS);
952
+ String newAccessToken = authResult.getAccessToken();
953
+ if (newAccessToken == null || newAccessToken.isEmpty()) {
954
+ call.reject("Failed to refresh tokens: access token is null");
955
+ return;
956
+ }
957
+
958
+ persistState(newIdToken, newAccessToken, GoogleProvider.this.scopes);
959
+ call.resolve();
960
+ } catch (Exception e) {
961
+ call.reject("Failed to refresh tokens: " + e.getMessage());
962
+ } finally {
963
+ authExecutor.shutdown();
964
+ }
965
+ }
966
+ }
967
+ );
968
+ } catch (Exception e) {
969
+ call.reject("Failed to refresh tokens: " + e.getMessage());
970
+ } finally {
971
+ executor.shutdown();
972
+ }
973
+ }
974
+
975
+ @Override
976
+ public void onError(@NonNull GetCredentialException e) {
977
+ if (e instanceof NoCredentialException) {
978
+ call.reject("User not logged in");
979
+ executor.shutdown();
980
+ return;
981
+ }
982
+ call.reject("Failed to refresh tokens: " + e.getMessage());
983
+ executor.shutdown();
984
+ }
985
+ }
986
+ );
840
987
  }
841
988
  }
@@ -20,7 +20,7 @@ import org.json.JSONObject;
20
20
  @CapacitorPlugin(name = "SocialLogin")
21
21
  public class SocialLoginPlugin extends Plugin {
22
22
 
23
- private final String pluginVersion = "8.2.23";
23
+ private final String pluginVersion = "8.2.25";
24
24
 
25
25
  public static String LOG_TAG = "CapgoSocialLogin";
26
26
 
package/dist/docs.json CHANGED
@@ -148,7 +148,11 @@
148
148
  "tags": [
149
149
  {
150
150
  "name": "description",
151
- "text": "refresh the access token"
151
+ "text": "refresh the access token\n\n**Google Offline Mode Limitation:**\nThis method is NOT supported when Google is initialized with `mode: 'offline'`.\nIt will reject with error: \"refresh is not implemented when using offline mode\""
152
+ },
153
+ {
154
+ "name": "throws",
155
+ "text": "Error if Google provider is in offline mode"
152
156
  }
153
157
  ],
154
158
  "docs": "Refresh the access token",
@@ -861,8 +865,24 @@
861
865
  "name": "permissions",
862
866
  "tags": [
863
867
  {
864
- "text": "select permissions to login with",
868
+ "text": "Select permissions to login with. Supports both consumer and business permissions.\n\n**Consumer Permissions:**\n- `email` - User's email address\n- `public_profile` - User's public profile info\n- `user_friends` - List of friends who also use your app\n\n**Business Permissions** (require business app configuration and may need App Review):\n- `instagram_basic` - Instagram Basic Display API access\n- `instagram_manage_insights` - Instagram Insights data\n- `instagram_manage_comments` - Manage Instagram comments\n- `instagram_content_publish` - Publish to Instagram\n- `pages_show_list` - List of Pages managed by user\n- `pages_read_engagement` - Read Page engagement metrics\n- `pages_manage_posts` - Manage Page posts\n- `pages_messaging` - Page messaging features\n- `business_management` - Manage business assets\n- `catalog_management` - Manage product catalogs\n- `ads_management` - Manage advertising accounts",
865
869
  "name": "description"
870
+ },
871
+ {
872
+ "text": "['email', 'public_profile'] // Consumer permissions",
873
+ "name": "example"
874
+ },
875
+ {
876
+ "text": "['email', 'instagram_basic', 'pages_show_list'] // Business permissions",
877
+ "name": "example"
878
+ },
879
+ {
880
+ "text": "https ://developers.facebook.com/docs/permissions/reference",
881
+ "name": "see"
882
+ },
883
+ {
884
+ "text": "docs /facebook_business_login.md for complete business integration guide",
885
+ "name": "see"
866
886
  }
867
887
  ],
868
888
  "docs": "Permissions",
@@ -111,6 +111,9 @@ export interface InitializeOptions {
111
111
  facebook?: {
112
112
  /**
113
113
  * Facebook App ID, provided by Facebook for web, in mobile it's set in the native files
114
+ * @description For business integrations, use your Business App ID from Facebook Developer Console.
115
+ * Business apps can access additional permissions like Instagram API, Pages API, and business management features.
116
+ * @see docs/facebook_business_login.md for business app setup guide
114
117
  */
115
118
  appId: string;
116
119
  /**
@@ -257,7 +260,30 @@ export interface InitializeOptions {
257
260
  export interface FacebookLoginOptions {
258
261
  /**
259
262
  * Permissions
260
- * @description select permissions to login with
263
+ * @description Select permissions to login with. Supports both consumer and business permissions.
264
+ *
265
+ * **Consumer Permissions:**
266
+ * - `email` - User's email address
267
+ * - `public_profile` - User's public profile info
268
+ * - `user_friends` - List of friends who also use your app
269
+ *
270
+ * **Business Permissions** (require business app configuration and may need App Review):
271
+ * - `instagram_basic` - Instagram Basic Display API access
272
+ * - `instagram_manage_insights` - Instagram Insights data
273
+ * - `instagram_manage_comments` - Manage Instagram comments
274
+ * - `instagram_content_publish` - Publish to Instagram
275
+ * - `pages_show_list` - List of Pages managed by user
276
+ * - `pages_read_engagement` - Read Page engagement metrics
277
+ * - `pages_manage_posts` - Manage Page posts
278
+ * - `pages_messaging` - Page messaging features
279
+ * - `business_management` - Manage business assets
280
+ * - `catalog_management` - Manage product catalogs
281
+ * - `ads_management` - Manage advertising accounts
282
+ *
283
+ * @example ['email', 'public_profile'] // Consumer permissions
284
+ * @example ['email', 'instagram_basic', 'pages_show_list'] // Business permissions
285
+ * @see https://developers.facebook.com/docs/permissions/reference
286
+ * @see docs/facebook_business_login.md for complete business integration guide
261
287
  */
262
288
  permissions: string[];
263
289
  /**
@@ -778,6 +804,12 @@ export interface SocialLoginPlugin {
778
804
  /**
779
805
  * Refresh the access token
780
806
  * @description refresh the access token
807
+ *
808
+ * **Google Offline Mode Limitation:**
809
+ * This method is NOT supported when Google is initialized with `mode: 'offline'`.
810
+ * It will reject with error: "refresh is not implemented when using offline mode"
811
+ *
812
+ * @throws Error if Google provider is in offline mode
781
813
  */
782
814
  refresh(options: LoginOptions): Promise<void>;
783
815
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Configuration for a single OAuth2 provider instance\n */\nexport interface OAuth2ProviderConfig {\n /**\n * The OAuth 2.0 client identifier (App ID / Client ID)\n * @example 'your-client-id'\n */\n appId: string;\n /**\n * The base URL of the authorization endpoint\n * @example 'https://accounts.example.com/oauth2/authorize'\n */\n authorizationBaseUrl: string;\n /**\n * The URL to exchange the authorization code for tokens\n * Required for authorization code flow\n * @example 'https://accounts.example.com/oauth2/token'\n */\n accessTokenEndpoint?: string;\n /**\n * Redirect URL that receives the OAuth callback\n * @example 'myapp://oauth/callback'\n */\n redirectUrl: string;\n /**\n * Optional URL to fetch user profile/resource data after authentication\n * The access token will be sent as Bearer token in the Authorization header\n * @example 'https://api.example.com/userinfo'\n */\n resourceUrl?: string;\n /**\n * The OAuth response type\n * - 'code': Authorization Code flow (recommended, requires accessTokenEndpoint)\n * - 'token': Implicit flow (less secure, tokens returned directly)\n * @default 'code'\n */\n responseType?: 'code' | 'token';\n /**\n * Enable PKCE (Proof Key for Code Exchange)\n * Strongly recommended for public clients (mobile/web apps)\n * @default true\n */\n pkceEnabled?: boolean;\n /**\n * Default scopes to request during authorization\n * @example 'openid profile email'\n */\n scope?: string;\n /**\n * Additional parameters to include in the authorization request\n * @example { prompt: 'consent', login_hint: 'user@example.com' }\n */\n additionalParameters?: Record<string, string>;\n /**\n * Additional headers to include when fetching the resource URL\n * @example { 'X-Custom-Header': 'value' }\n */\n additionalResourceHeaders?: Record<string, string>;\n /**\n * Custom logout URL for ending the session\n * @example 'https://accounts.example.com/logout'\n */\n logoutUrl?: string;\n /**\n * Enable debug logging\n * @default false\n */\n logsEnabled?: boolean;\n}\n\nexport interface InitializeOptions {\n /**\n * OAuth2 provider configurations.\n * Supports multiple providers by using a Record with provider IDs as keys.\n * @example\n * {\n * github: { appId: '...', authorizationBaseUrl: 'https://github.com/login/oauth/authorize', ... },\n * azure: { appId: '...', authorizationBaseUrl: 'https://login.microsoftonline.com/.../oauth2/v2.0/authorize', ... }\n * }\n */\n oauth2?: Record<string, OAuth2ProviderConfig>;\n twitter?: {\n /**\n * The OAuth 2.0 client identifier issued by X (Twitter) Developer Portal\n * @example 'Y2xpZW50SWQ'\n */\n clientId: string;\n /**\n * Redirect URL that is registered inside the X Developer Portal.\n * The plugin uses this URL on every platform to receive the authorization code.\n * @example 'myapp://auth/x'\n */\n redirectUrl: string;\n /**\n * Default scopes appended to every login request when no custom scopes are provided.\n * @description Defaults to the minimum required scopes for Log in with X.\n * @default ['tweet.read','users.read']\n */\n defaultScopes?: string[];\n /**\n * Force the consent screen to show on every login attempt.\n * Mirrors X's `force_login=true` flag.\n * @default false\n */\n forceLogin?: boolean;\n /**\n * Optional audience value when your application has been approved for multi-tenant access.\n */\n audience?: string;\n };\n facebook?: {\n /**\n * Facebook App ID, provided by Facebook for web, in mobile it's set in the native files\n */\n appId: string;\n /**\n * Facebook Client Token, provided by Facebook for web, in mobile it's set in the native files\n */\n clientToken?: string;\n /**\n * Locale\n * @description The locale to use for the Facebook SDK (e.g., 'en_US', 'fr_FR', 'es_ES')\n * @default 'en_US'\n * @example 'fr_FR'\n */\n locale?: string;\n };\n\n google?: {\n /**\n * The app's client ID, found and created in the Google Developers Console.\n * Required for iOS platform.\n * @example xxxxxx-xxxxxxxxxxxxxxxxxx.apps.googleusercontent.com\n * @since 3.1.0\n */\n iOSClientId?: string;\n /**\n * The app's server client ID, required for offline mode on iOS.\n * Should be the same value as webClientId.\n * Found and created in the Google Developers Console.\n * @example xxxxxx-xxxxxxxxxxxxxxxxxx.apps.googleusercontent.com\n * @since 3.1.0\n */\n iOSServerClientId?: string;\n /**\n * The app's web client ID, found and created in the Google Developers Console.\n * Required for Android and Web platforms.\n * @example xxxxxx-xxxxxxxxxxxxxxxxxx.apps.googleusercontent.com\n * @since 3.1.0\n */\n webClientId?: string;\n /**\n * The login mode, can be online or offline.\n *\n * **Online mode (default):**\n * - Returns user profile data and access tokens\n * - Supports all methods: login, logout, isLoggedIn, getAuthorizationCode\n *\n * **Offline mode:**\n * - Returns only serverAuthCode for backend authentication\n * - No user profile data available\n * - **Limitations:** The following methods are NOT supported in offline mode:\n * - `logout()` - Will reject with \"not implemented when using offline mode\"\n * - `isLoggedIn()` - Will reject with \"not implemented when using offline mode\"\n * - `getAuthorizationCode()` - Will reject with \"not implemented when using offline mode\"\n * - Only `login()` method works in offline mode, returning serverAuthCode only\n * - Requires `iOSServerClientId` to be set on iOS\n *\n * @example 'offline'\n * @default 'online'\n * @since 3.1.0\n */\n mode?: 'online' | 'offline';\n /**\n * Filter visible accounts by hosted domain\n * @description filter visible accounts by hosted domain\n */\n hostedDomain?: string;\n /**\n * Google Redirect URL, should be your backend url that is configured in your google app\n */\n redirectUrl?: string;\n };\n apple?: {\n /**\n * Apple Client ID, provided by Apple for web and Android\n */\n clientId?: string;\n /**\n * Apple Redirect URL, should be your backend url that is configured in your apple app\n *\n * **Note**: Use empty string `''` for iOS to prevent redirect.\n * **Note**: Not required when using Broadcast Channel mode on Android.\n */\n redirectUrl?: string;\n /**\n * Use proper token exchange for Apple Sign-In\n * @description Controls how Apple Sign-In tokens are handled and what gets returned:\n *\n * **When `true` (Recommended for new implementations):**\n * - Exchanges authorization code for proper access tokens via Apple's token endpoint\n * - `idToken`: JWT containing user identity information (email, name, user ID)\n * - `accessToken.token`: Proper access token from Apple (short-lived, ~1 hour)\n * - `authorizationCode`: Raw authorization code for backend token exchange\n *\n * **When `false` (Default - Legacy mode):**\n * - Uses authorization code directly as access token for backward compatibility\n * - `idToken`: JWT containing user identity information (email, name, user ID)\n * - `accessToken.token`: The authorization code itself (not a real access token)\n * - `authorizationCode`: undefined\n *\n * @default false\n * @example\n * // Enable proper token exchange (recommended)\n * useProperTokenExchange: true\n * // Result: idToken=JWT, accessToken=real_token, authorizationCode=present\n *\n * // Legacy mode (backward compatibility)\n * useProperTokenExchange: false\n * // Result: idToken=JWT, accessToken=auth_code, authorizationCode=undefined\n */\n useProperTokenExchange?: boolean;\n /**\n * Use Broadcast Channel for Android Apple Sign-In (Recommended)\n * @description When enabled, Android uses Broadcast Channel API instead of URL redirects.\n * This eliminates the need for redirect URL configuration and server-side setup.\n *\n * **Benefits:**\n * - No redirect URL configuration required\n * - No backend server needed for Android\n * - Simpler setup and more reliable communication\n * - Direct client-server communication via Broadcast Channel\n *\n * **When `true`:**\n * - Uses Broadcast Channel for authentication flow\n * - `redirectUrl` is ignored\n * - Requires Broadcast Channel compatible backend or direct token handling\n *\n * **When `false` (Default - Legacy mode):**\n * - Uses traditional URL redirect flow\n * - Requires `redirectUrl` configuration\n * - Requires backend server for token exchange\n *\n * @default false\n * @since 7.10.0\n * @example\n * // Enable Broadcast Channel mode (recommended for new Android implementations)\n * useBroadcastChannel: true\n * // Result: Simplified setup, no redirect URL needed\n *\n * // Legacy mode (backward compatibility)\n * useBroadcastChannel: false\n * // Result: Traditional URL redirect flow with server-side setup\n */\n useBroadcastChannel?: boolean;\n };\n}\n\nexport interface FacebookLoginOptions {\n /**\n * Permissions\n * @description select permissions to login with\n */\n permissions: string[];\n /**\n * Is Limited Login\n * @description use limited login for Facebook iOS only. Important: This is iOS-only and doesn't affect Android.\n * Even if set to false, Facebook will automatically force it to true if App Tracking Transparency (ATT) permission is not granted.\n * Developers should always be prepared to handle both limited and full login scenarios.\n * @default false\n */\n limitedLogin?: boolean;\n /**\n * Nonce\n * @description A custom nonce to use for the login request\n */\n nonce?: string;\n}\n\nexport interface TwitterLoginOptions {\n /**\n * Additional scopes to request during login.\n * If omitted the plugin falls back to the default scopes configured during initialization.\n * @example ['tweet.read','users.read','offline.access']\n */\n scopes?: string[];\n /**\n * Provide a custom OAuth state value.\n * When not provided the plugin generates a cryptographically random value.\n */\n state?: string;\n /**\n * Provide a pre-computed PKCE code verifier (mostly used for testing).\n * When omitted the plugin generates a secure verifier automatically.\n */\n codeVerifier?: string;\n /**\n * Override the redirect URI for a single login call.\n * Useful when the same app supports multiple callback URLs per platform.\n */\n redirectUrl?: string;\n /**\n * Force the consent screen on every attempt, maps to `force_login=true`.\n */\n forceLogin?: boolean;\n}\n\nexport interface OAuth2LoginOptions {\n /**\n * The provider ID as configured in initialize()\n * This is required to identify which OAuth2 provider to use\n * @example 'github', 'azure', 'keycloak'\n */\n providerId: string;\n /**\n * Override the scopes for this login request\n * If not provided, uses the scopes from initialization\n */\n scope?: string;\n /**\n * Custom state parameter for CSRF protection\n * If not provided, a random value is generated\n */\n state?: string;\n /**\n * Override PKCE code verifier (for testing purposes)\n * If not provided, a secure random verifier is generated\n */\n codeVerifier?: string;\n /**\n * Override redirect URL for this login request\n */\n redirectUrl?: string;\n /**\n * Additional parameters to add to the authorization URL\n */\n additionalParameters?: Record<string, string>;\n}\n\nexport interface OAuth2LoginResponse {\n /**\n * The provider ID that was used for this login\n */\n providerId: string;\n /**\n * The access token received from the OAuth provider\n */\n accessToken: AccessToken | null;\n /**\n * The ID token (JWT) if provided by the OAuth server (e.g., OpenID Connect)\n */\n idToken: string | null;\n /**\n * The refresh token if provided (requires appropriate scope like offline_access)\n */\n refreshToken: string | null;\n /**\n * Resource data fetched from resourceUrl if configured\n * Contains the raw JSON response from the resource endpoint\n */\n resourceData: Record<string, unknown> | null;\n /**\n * The scopes that were granted\n */\n scope: string[];\n /**\n * Token type (usually 'bearer')\n */\n tokenType: string;\n /**\n * Token expiration time in seconds\n */\n expiresIn: number | null;\n}\n\nexport interface GoogleLoginOptions {\n /**\n * Specifies the scopes required for accessing Google APIs\n * The default is defined in the configuration.\n * @example [\"profile\", \"email\"]\n * @see [Google OAuth2 Scopes](https://developers.google.com/identity/protocols/oauth2/scopes)\n */\n scopes?: string[];\n /**\n * Nonce\n * @description nonce\n */\n nonce?: string;\n /**\n * Force refresh token (only for Android)\n * @description force refresh token\n * @default false\n * @note On Android, the OS caches access tokens, and if a token is invalid (e.g., user revoked app access), the plugin might return an invalid accessToken. Using getAuthorizationCode() is recommended to ensure the token is valid.\n */\n forceRefreshToken?: boolean;\n /**\n * Force account selection prompt (iOS)\n * @description forces the account selection prompt to appear on iOS\n * @default false\n */\n forcePrompt?: boolean;\n /**\n * Style\n * @description style\n * @default 'standard'\n */\n style?: 'bottom' | 'standard';\n /**\n * Filter by authorized accounts (Android only)\n * @description Only show accounts that have previously been used to sign in to the app.\n * This option is only available for the 'bottom' style.\n * Note: For Family Link supervised accounts, this should be set to false.\n * @default true\n */\n filterByAuthorizedAccounts?: boolean;\n /**\n * Auto select enabled (Android only)\n * @description Automatically select the account if only one Google account is available.\n * This option is only available for the 'bottom' style.\n * @default false\n */\n autoSelectEnabled?: boolean;\n /**\n * Prompt parameter for Google OAuth (Web only)\n * @description A space-delimited, case-sensitive list of prompts to present the user.\n * If you don't specify this parameter, the user will be prompted only the first time your project requests access.\n *\n * **Possible values:**\n * - `none`: Don't display any authentication or consent screens. Must not be specified with other values.\n * - `consent`: Prompt the user for consent.\n * - `select_account`: Prompt the user to select an account.\n *\n * **Examples:**\n * - `prompt: 'consent'` - Always show consent screen\n * - `prompt: 'select_account'` - Always show account selection\n * - `prompt: 'consent select_account'` - Show both consent and account selection\n *\n * **Note:** This parameter only affects web platform behavior. Mobile platforms use their own native prompts.\n *\n * @example 'consent'\n * @example 'select_account'\n * @example 'consent select_account'\n * @see [Google OAuth2 Prompt Parameter](https://developers.google.com/identity/protocols/oauth2/openid-connect#prompt)\n * @since 7.12.0\n */\n prompt?: 'none' | 'consent' | 'select_account' | 'consent select_account' | 'select_account consent';\n}\n\nexport interface GoogleLoginResponseOnline {\n accessToken: AccessToken | null;\n idToken: string | null;\n profile: {\n email: string | null;\n familyName: string | null;\n givenName: string | null;\n id: string | null;\n name: string | null;\n imageUrl: string | null;\n };\n responseType: 'online';\n}\n\nexport interface GoogleLoginResponseOffline {\n serverAuthCode: string;\n responseType: 'offline';\n}\n\nexport type GoogleLoginResponse = GoogleLoginResponseOnline | GoogleLoginResponseOffline;\n\nexport interface AppleProviderOptions {\n /**\n * Scopes\n * @description An array of scopes to request during login\n * @example [\"name\", \"email\"]\n * default: [\"name\", \"email\"]\n */\n scopes?: string[];\n /**\n * Nonce\n * @description nonce\n */\n nonce?: string;\n /**\n * State\n * @description state\n */\n state?: string;\n /**\n * Use Broadcast Channel for authentication flow\n * @description When enabled, uses Broadcast Channel API for communication instead of URL redirects.\n * Only applicable on platforms that support Broadcast Channel (Android).\n * @default false\n */\n useBroadcastChannel?: boolean;\n}\n\nexport interface AppleProviderResponse {\n /**\n * Access token from Apple\n * @description Content depends on `useProperTokenExchange` setting:\n * - When `useProperTokenExchange: true`: Real access token from Apple (~1 hour validity)\n * - When `useProperTokenExchange: false`: Contains authorization code as token (legacy mode)\n * Use `idToken` for user authentication, `accessToken` for API calls when properly exchanged.\n */\n accessToken: AccessToken | null;\n\n /**\n * Identity token (JWT) from Apple\n * @description Always contains the JWT with user identity information including:\n * - User ID (sub claim)\n * - Email (if user granted permission)\n * - Name components (if user granted permission)\n * - Email verification status\n * This is the primary token for user authentication and should be verified on your backend.\n */\n idToken: string | null;\n\n /**\n * User profile information\n * @description Basic user profile data extracted from the identity token and Apple response:\n * - `user`: Apple's user identifier (sub claim from idToken)\n * - `email`: User's email address (if permission granted)\n * - `givenName`: User's first name (if permission granted)\n * - `familyName`: User's last name (if permission granted)\n */\n profile: {\n user: string;\n email: string | null;\n givenName: string | null;\n familyName: string | null;\n };\n\n /**\n * Authorization code for proper token exchange (when useProperTokenExchange is enabled)\n * @description Only present when `useProperTokenExchange` is `true`. This code should be exchanged\n * for proper access tokens on your backend using Apple's token endpoint. Use this for secure\n * server-side token validation and to obtain refresh tokens.\n * @see https://developer.apple.com/documentation/sign_in_with_apple/tokenresponse\n */\n authorizationCode?: string;\n}\n\nexport type LoginOptions =\n | {\n provider: 'facebook';\n options: FacebookLoginOptions;\n }\n | {\n provider: 'google';\n options: GoogleLoginOptions;\n }\n | {\n provider: 'apple';\n options: AppleProviderOptions;\n }\n | {\n provider: 'twitter';\n options: TwitterLoginOptions;\n }\n | {\n provider: 'oauth2';\n options: OAuth2LoginOptions;\n };\n\nexport type LoginResult =\n | {\n provider: 'facebook';\n result: FacebookLoginResponse;\n }\n | {\n provider: 'google';\n result: GoogleLoginResponse;\n }\n | {\n provider: 'apple';\n result: AppleProviderResponse;\n }\n | {\n provider: 'twitter';\n result: TwitterLoginResponse;\n }\n | {\n provider: 'oauth2';\n result: OAuth2LoginResponse;\n };\n\nexport interface AccessToken {\n applicationId?: string;\n declinedPermissions?: string[];\n expires?: string;\n isExpired?: boolean;\n lastRefresh?: string;\n permissions?: string[];\n token: string;\n tokenType?: string;\n refreshToken?: string;\n userId?: string;\n}\n\nexport interface FacebookLoginResponse {\n accessToken: AccessToken | null;\n idToken: string | null;\n profile: {\n userID: string;\n email: string | null;\n friendIDs: string[];\n birthday: string | null;\n ageRange: { min?: number; max?: number } | null;\n gender: string | null;\n location: { id: string; name: string } | null;\n hometown: { id: string; name: string } | null;\n profileURL: string | null;\n name: string | null;\n imageURL: string | null;\n };\n}\n\nexport interface TwitterProfile {\n id: string;\n username: string;\n name: string | null;\n profileImageUrl: string | null;\n verified: boolean;\n email?: string | null;\n}\n\nexport interface TwitterLoginResponse {\n accessToken: AccessToken | null;\n refreshToken?: string | null;\n scope: string[];\n tokenType: 'bearer';\n expiresIn?: number | null;\n profile: TwitterProfile;\n}\n\nexport interface AuthorizationCode {\n /**\n * Jwt\n * @description A JSON web token\n */\n jwt?: string;\n /**\n * Access Token\n * @description An access token\n */\n accessToken?: string;\n}\n\nexport interface AuthorizationCodeOptions {\n /**\n * Provider\n * @description Provider for the authorization code\n */\n provider: 'apple' | 'google' | 'facebook' | 'twitter' | 'oauth2';\n /**\n * Provider ID for OAuth2 providers (required when provider is 'oauth2')\n * @description The ID used when configuring the OAuth2 provider in initialize()\n */\n providerId?: string;\n}\n\nexport interface isLoggedInOptions {\n /**\n * Provider\n * @description Provider for the isLoggedIn\n */\n provider: 'apple' | 'google' | 'facebook' | 'twitter' | 'oauth2';\n /**\n * Provider ID for OAuth2 providers (required when provider is 'oauth2')\n * @description The ID used when configuring the OAuth2 provider in initialize()\n */\n providerId?: string;\n}\n\n// Define the provider-specific call types\nexport type ProviderSpecificCall = 'facebook#getProfile' | 'facebook#requestTracking';\n\n// Define the options and response types for each specific call\nexport interface FacebookGetProfileOptions {\n /**\n * Fields to retrieve from Facebook profile\n * @example [\"id\", \"name\", \"email\", \"picture\"]\n */\n fields?: string[];\n}\n\nexport interface FacebookGetProfileResponse {\n /**\n * Facebook profile data\n */\n profile: {\n id: string | null;\n name: string | null;\n email: string | null;\n first_name: string | null;\n last_name: string | null;\n picture?: {\n data: {\n height: number | null;\n is_silhouette: boolean | null;\n url: string | null;\n width: number | null;\n };\n } | null;\n [key: string]: any; // For additional fields that might be requested\n };\n}\n\nexport interface OpenSecureWindowOptions {\n /**\n * The endpoint to open\n */\n authEndpoint: string;\n /**\n * The redirect URI to use for the openSecureWindow call.\n * This will be checked to make sure it matches the redirect URI after the window finishes the redirection.\n */\n redirectUri: string;\n /**\n * The name of the broadcast channel to listen to, relevant only for web\n */\n broadcastChannelName?: string;\n}\n\nexport interface OpenSecureWindowResponse {\n /**\n * The result of the openSecureWindow call\n */\n redirectedUri: string;\n}\n\nexport type FacebookRequestTrackingOptions = Record<string, never>;\n\nexport interface FacebookRequestTrackingResponse {\n /**\n * App tracking authorization status\n */\n status: 'authorized' | 'denied' | 'notDetermined' | 'restricted';\n}\n\n// Map call strings to their options and response types\nexport type ProviderSpecificCallOptionsMap = {\n 'facebook#getProfile': FacebookGetProfileOptions;\n 'facebook#requestTracking': FacebookRequestTrackingOptions;\n};\n\nexport type ProviderSpecificCallResponseMap = {\n 'facebook#getProfile': FacebookGetProfileResponse;\n 'facebook#requestTracking': FacebookRequestTrackingResponse;\n};\n\n// Add a helper type to map providers to their response types\nexport type ProviderResponseMap = {\n facebook: FacebookLoginResponse;\n google: GoogleLoginResponse;\n apple: AppleProviderResponse;\n twitter: TwitterLoginResponse;\n oauth2: OAuth2LoginResponse;\n};\n\nexport interface SocialLoginPlugin {\n /**\n * Initialize the plugin\n * @description initialize the plugin with the required options\n */\n initialize(options: InitializeOptions): Promise<void>;\n /**\n * Login with the selected provider\n * @description login with the selected provider\n */\n login<T extends LoginOptions['provider']>(\n options: Extract<LoginOptions, { provider: T }>,\n ): Promise<{ provider: T; result: ProviderResponseMap[T] }>;\n /**\n * Logout\n * @description Logout the user from the specified provider\n *\n * **Google Offline Mode Limitation:**\n * This method is NOT supported when Google is initialized with `mode: 'offline'`.\n * It will reject with error: \"logout is not implemented when using offline mode\"\n *\n * @throws Error if Google provider is in offline mode\n */\n logout(options: {\n provider: 'apple' | 'google' | 'facebook' | 'twitter' | 'oauth2';\n providerId?: string;\n }): Promise<void>;\n /**\n * IsLoggedIn\n * @description Check if the user is currently logged in with the specified provider\n *\n * **Google Offline Mode Limitation:**\n * This method is NOT supported when Google is initialized with `mode: 'offline'`.\n * It will reject with error: \"isLoggedIn is not implemented when using offline mode\"\n *\n * @throws Error if Google provider is in offline mode\n */\n isLoggedIn(options: isLoggedInOptions): Promise<{ isLoggedIn: boolean }>;\n\n /**\n * Get the current authorization code\n * @description Get the authorization code for server-side authentication\n *\n * **Google Offline Mode Limitation:**\n * This method is NOT supported when Google is initialized with `mode: 'offline'`.\n * It will reject with error: \"getAuthorizationCode is not implemented when using offline mode\"\n *\n * In offline mode, the authorization code (serverAuthCode) is already returned by the `login()` method.\n *\n * @throws Error if Google provider is in offline mode\n */\n getAuthorizationCode(options: AuthorizationCodeOptions): Promise<AuthorizationCode>;\n /**\n * Refresh the access token\n * @description refresh the access token\n */\n refresh(options: LoginOptions): Promise<void>;\n\n /**\n * Execute provider-specific calls\n * @description Execute a provider-specific functionality\n */\n providerSpecificCall<T extends ProviderSpecificCall>(options: {\n call: T;\n options: ProviderSpecificCallOptionsMap[T];\n }): Promise<ProviderSpecificCallResponseMap[T]>;\n\n /**\n * Get the native Capacitor plugin version\n *\n * @returns {Promise<{ id: string }>} an Promise with version for this device\n * @throws An error if the something went wrong\n */\n getPluginVersion(): Promise<{ version: string }>;\n\n /**\n * Opens a secured window for OAuth2 authentication.\n * For web, you should have the code in the redirected page to use a broadcast channel to send the redirected url to the app\n * Something like:\n * ```html\n * <html>\n * <head></head>\n * <body>\n * <script>\n * const searchParams = new URLSearchParams(location.search)\n * if (searchParams.has(\"code\")) {\n * new BroadcastChannel(\"my-channel-name\").postMessage(location.href);\n * window.close();\n * }\n * </script>\n * </body>\n * </html>\n * ```\n * For mobile, you should have a redirect uri that opens the app, something like: `myapp://oauth_callback/`\n * And make sure to register it in the app's info.plist:\n * ```xml\n * <key>CFBundleURLTypes</key>\n * <array>\n * <dict>\n * <key>CFBundleURLSchemes</key>\n * <array>\n * <string>myapp</string>\n * </array>\n * </dict>\n * </array>\n * ```\n * And in the AndroidManifest.xml file:\n * ```xml\n * <activity>\n * <intent-filter>\n * <action android:name=\"android.intent.action.VIEW\" />\n * <category android:name=\"android.intent.category.DEFAULT\" />\n * <category android:name=\"android.intent.category.BROWSABLE\" />\n * <data android:host=\"oauth_callback\" android:scheme=\"myapp\" />\n * </intent-filter>\n * </activity>\n * ```\n * @param options - the options for the openSecureWindow call\n */\n openSecureWindow(options: OpenSecureWindowOptions): Promise<OpenSecureWindowResponse>;\n}\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Configuration for a single OAuth2 provider instance\n */\nexport interface OAuth2ProviderConfig {\n /**\n * The OAuth 2.0 client identifier (App ID / Client ID)\n * @example 'your-client-id'\n */\n appId: string;\n /**\n * The base URL of the authorization endpoint\n * @example 'https://accounts.example.com/oauth2/authorize'\n */\n authorizationBaseUrl: string;\n /**\n * The URL to exchange the authorization code for tokens\n * Required for authorization code flow\n * @example 'https://accounts.example.com/oauth2/token'\n */\n accessTokenEndpoint?: string;\n /**\n * Redirect URL that receives the OAuth callback\n * @example 'myapp://oauth/callback'\n */\n redirectUrl: string;\n /**\n * Optional URL to fetch user profile/resource data after authentication\n * The access token will be sent as Bearer token in the Authorization header\n * @example 'https://api.example.com/userinfo'\n */\n resourceUrl?: string;\n /**\n * The OAuth response type\n * - 'code': Authorization Code flow (recommended, requires accessTokenEndpoint)\n * - 'token': Implicit flow (less secure, tokens returned directly)\n * @default 'code'\n */\n responseType?: 'code' | 'token';\n /**\n * Enable PKCE (Proof Key for Code Exchange)\n * Strongly recommended for public clients (mobile/web apps)\n * @default true\n */\n pkceEnabled?: boolean;\n /**\n * Default scopes to request during authorization\n * @example 'openid profile email'\n */\n scope?: string;\n /**\n * Additional parameters to include in the authorization request\n * @example { prompt: 'consent', login_hint: 'user@example.com' }\n */\n additionalParameters?: Record<string, string>;\n /**\n * Additional headers to include when fetching the resource URL\n * @example { 'X-Custom-Header': 'value' }\n */\n additionalResourceHeaders?: Record<string, string>;\n /**\n * Custom logout URL for ending the session\n * @example 'https://accounts.example.com/logout'\n */\n logoutUrl?: string;\n /**\n * Enable debug logging\n * @default false\n */\n logsEnabled?: boolean;\n}\n\nexport interface InitializeOptions {\n /**\n * OAuth2 provider configurations.\n * Supports multiple providers by using a Record with provider IDs as keys.\n * @example\n * {\n * github: { appId: '...', authorizationBaseUrl: 'https://github.com/login/oauth/authorize', ... },\n * azure: { appId: '...', authorizationBaseUrl: 'https://login.microsoftonline.com/.../oauth2/v2.0/authorize', ... }\n * }\n */\n oauth2?: Record<string, OAuth2ProviderConfig>;\n twitter?: {\n /**\n * The OAuth 2.0 client identifier issued by X (Twitter) Developer Portal\n * @example 'Y2xpZW50SWQ'\n */\n clientId: string;\n /**\n * Redirect URL that is registered inside the X Developer Portal.\n * The plugin uses this URL on every platform to receive the authorization code.\n * @example 'myapp://auth/x'\n */\n redirectUrl: string;\n /**\n * Default scopes appended to every login request when no custom scopes are provided.\n * @description Defaults to the minimum required scopes for Log in with X.\n * @default ['tweet.read','users.read']\n */\n defaultScopes?: string[];\n /**\n * Force the consent screen to show on every login attempt.\n * Mirrors X's `force_login=true` flag.\n * @default false\n */\n forceLogin?: boolean;\n /**\n * Optional audience value when your application has been approved for multi-tenant access.\n */\n audience?: string;\n };\n facebook?: {\n /**\n * Facebook App ID, provided by Facebook for web, in mobile it's set in the native files\n * @description For business integrations, use your Business App ID from Facebook Developer Console.\n * Business apps can access additional permissions like Instagram API, Pages API, and business management features.\n * @see docs/facebook_business_login.md for business app setup guide\n */\n appId: string;\n /**\n * Facebook Client Token, provided by Facebook for web, in mobile it's set in the native files\n */\n clientToken?: string;\n /**\n * Locale\n * @description The locale to use for the Facebook SDK (e.g., 'en_US', 'fr_FR', 'es_ES')\n * @default 'en_US'\n * @example 'fr_FR'\n */\n locale?: string;\n };\n\n google?: {\n /**\n * The app's client ID, found and created in the Google Developers Console.\n * Required for iOS platform.\n * @example xxxxxx-xxxxxxxxxxxxxxxxxx.apps.googleusercontent.com\n * @since 3.1.0\n */\n iOSClientId?: string;\n /**\n * The app's server client ID, required for offline mode on iOS.\n * Should be the same value as webClientId.\n * Found and created in the Google Developers Console.\n * @example xxxxxx-xxxxxxxxxxxxxxxxxx.apps.googleusercontent.com\n * @since 3.1.0\n */\n iOSServerClientId?: string;\n /**\n * The app's web client ID, found and created in the Google Developers Console.\n * Required for Android and Web platforms.\n * @example xxxxxx-xxxxxxxxxxxxxxxxxx.apps.googleusercontent.com\n * @since 3.1.0\n */\n webClientId?: string;\n /**\n * The login mode, can be online or offline.\n *\n * **Online mode (default):**\n * - Returns user profile data and access tokens\n * - Supports all methods: login, logout, isLoggedIn, getAuthorizationCode\n *\n * **Offline mode:**\n * - Returns only serverAuthCode for backend authentication\n * - No user profile data available\n * - **Limitations:** The following methods are NOT supported in offline mode:\n * - `logout()` - Will reject with \"not implemented when using offline mode\"\n * - `isLoggedIn()` - Will reject with \"not implemented when using offline mode\"\n * - `getAuthorizationCode()` - Will reject with \"not implemented when using offline mode\"\n * - Only `login()` method works in offline mode, returning serverAuthCode only\n * - Requires `iOSServerClientId` to be set on iOS\n *\n * @example 'offline'\n * @default 'online'\n * @since 3.1.0\n */\n mode?: 'online' | 'offline';\n /**\n * Filter visible accounts by hosted domain\n * @description filter visible accounts by hosted domain\n */\n hostedDomain?: string;\n /**\n * Google Redirect URL, should be your backend url that is configured in your google app\n */\n redirectUrl?: string;\n };\n apple?: {\n /**\n * Apple Client ID, provided by Apple for web and Android\n */\n clientId?: string;\n /**\n * Apple Redirect URL, should be your backend url that is configured in your apple app\n *\n * **Note**: Use empty string `''` for iOS to prevent redirect.\n * **Note**: Not required when using Broadcast Channel mode on Android.\n */\n redirectUrl?: string;\n /**\n * Use proper token exchange for Apple Sign-In\n * @description Controls how Apple Sign-In tokens are handled and what gets returned:\n *\n * **When `true` (Recommended for new implementations):**\n * - Exchanges authorization code for proper access tokens via Apple's token endpoint\n * - `idToken`: JWT containing user identity information (email, name, user ID)\n * - `accessToken.token`: Proper access token from Apple (short-lived, ~1 hour)\n * - `authorizationCode`: Raw authorization code for backend token exchange\n *\n * **When `false` (Default - Legacy mode):**\n * - Uses authorization code directly as access token for backward compatibility\n * - `idToken`: JWT containing user identity information (email, name, user ID)\n * - `accessToken.token`: The authorization code itself (not a real access token)\n * - `authorizationCode`: undefined\n *\n * @default false\n * @example\n * // Enable proper token exchange (recommended)\n * useProperTokenExchange: true\n * // Result: idToken=JWT, accessToken=real_token, authorizationCode=present\n *\n * // Legacy mode (backward compatibility)\n * useProperTokenExchange: false\n * // Result: idToken=JWT, accessToken=auth_code, authorizationCode=undefined\n */\n useProperTokenExchange?: boolean;\n /**\n * Use Broadcast Channel for Android Apple Sign-In (Recommended)\n * @description When enabled, Android uses Broadcast Channel API instead of URL redirects.\n * This eliminates the need for redirect URL configuration and server-side setup.\n *\n * **Benefits:**\n * - No redirect URL configuration required\n * - No backend server needed for Android\n * - Simpler setup and more reliable communication\n * - Direct client-server communication via Broadcast Channel\n *\n * **When `true`:**\n * - Uses Broadcast Channel for authentication flow\n * - `redirectUrl` is ignored\n * - Requires Broadcast Channel compatible backend or direct token handling\n *\n * **When `false` (Default - Legacy mode):**\n * - Uses traditional URL redirect flow\n * - Requires `redirectUrl` configuration\n * - Requires backend server for token exchange\n *\n * @default false\n * @since 7.10.0\n * @example\n * // Enable Broadcast Channel mode (recommended for new Android implementations)\n * useBroadcastChannel: true\n * // Result: Simplified setup, no redirect URL needed\n *\n * // Legacy mode (backward compatibility)\n * useBroadcastChannel: false\n * // Result: Traditional URL redirect flow with server-side setup\n */\n useBroadcastChannel?: boolean;\n };\n}\n\nexport interface FacebookLoginOptions {\n /**\n * Permissions\n * @description Select permissions to login with. Supports both consumer and business permissions.\n *\n * **Consumer Permissions:**\n * - `email` - User's email address\n * - `public_profile` - User's public profile info\n * - `user_friends` - List of friends who also use your app\n *\n * **Business Permissions** (require business app configuration and may need App Review):\n * - `instagram_basic` - Instagram Basic Display API access\n * - `instagram_manage_insights` - Instagram Insights data\n * - `instagram_manage_comments` - Manage Instagram comments\n * - `instagram_content_publish` - Publish to Instagram\n * - `pages_show_list` - List of Pages managed by user\n * - `pages_read_engagement` - Read Page engagement metrics\n * - `pages_manage_posts` - Manage Page posts\n * - `pages_messaging` - Page messaging features\n * - `business_management` - Manage business assets\n * - `catalog_management` - Manage product catalogs\n * - `ads_management` - Manage advertising accounts\n *\n * @example ['email', 'public_profile'] // Consumer permissions\n * @example ['email', 'instagram_basic', 'pages_show_list'] // Business permissions\n * @see https://developers.facebook.com/docs/permissions/reference\n * @see docs/facebook_business_login.md for complete business integration guide\n */\n permissions: string[];\n /**\n * Is Limited Login\n * @description use limited login for Facebook iOS only. Important: This is iOS-only and doesn't affect Android.\n * Even if set to false, Facebook will automatically force it to true if App Tracking Transparency (ATT) permission is not granted.\n * Developers should always be prepared to handle both limited and full login scenarios.\n * @default false\n */\n limitedLogin?: boolean;\n /**\n * Nonce\n * @description A custom nonce to use for the login request\n */\n nonce?: string;\n}\n\nexport interface TwitterLoginOptions {\n /**\n * Additional scopes to request during login.\n * If omitted the plugin falls back to the default scopes configured during initialization.\n * @example ['tweet.read','users.read','offline.access']\n */\n scopes?: string[];\n /**\n * Provide a custom OAuth state value.\n * When not provided the plugin generates a cryptographically random value.\n */\n state?: string;\n /**\n * Provide a pre-computed PKCE code verifier (mostly used for testing).\n * When omitted the plugin generates a secure verifier automatically.\n */\n codeVerifier?: string;\n /**\n * Override the redirect URI for a single login call.\n * Useful when the same app supports multiple callback URLs per platform.\n */\n redirectUrl?: string;\n /**\n * Force the consent screen on every attempt, maps to `force_login=true`.\n */\n forceLogin?: boolean;\n}\n\nexport interface OAuth2LoginOptions {\n /**\n * The provider ID as configured in initialize()\n * This is required to identify which OAuth2 provider to use\n * @example 'github', 'azure', 'keycloak'\n */\n providerId: string;\n /**\n * Override the scopes for this login request\n * If not provided, uses the scopes from initialization\n */\n scope?: string;\n /**\n * Custom state parameter for CSRF protection\n * If not provided, a random value is generated\n */\n state?: string;\n /**\n * Override PKCE code verifier (for testing purposes)\n * If not provided, a secure random verifier is generated\n */\n codeVerifier?: string;\n /**\n * Override redirect URL for this login request\n */\n redirectUrl?: string;\n /**\n * Additional parameters to add to the authorization URL\n */\n additionalParameters?: Record<string, string>;\n}\n\nexport interface OAuth2LoginResponse {\n /**\n * The provider ID that was used for this login\n */\n providerId: string;\n /**\n * The access token received from the OAuth provider\n */\n accessToken: AccessToken | null;\n /**\n * The ID token (JWT) if provided by the OAuth server (e.g., OpenID Connect)\n */\n idToken: string | null;\n /**\n * The refresh token if provided (requires appropriate scope like offline_access)\n */\n refreshToken: string | null;\n /**\n * Resource data fetched from resourceUrl if configured\n * Contains the raw JSON response from the resource endpoint\n */\n resourceData: Record<string, unknown> | null;\n /**\n * The scopes that were granted\n */\n scope: string[];\n /**\n * Token type (usually 'bearer')\n */\n tokenType: string;\n /**\n * Token expiration time in seconds\n */\n expiresIn: number | null;\n}\n\nexport interface GoogleLoginOptions {\n /**\n * Specifies the scopes required for accessing Google APIs\n * The default is defined in the configuration.\n * @example [\"profile\", \"email\"]\n * @see [Google OAuth2 Scopes](https://developers.google.com/identity/protocols/oauth2/scopes)\n */\n scopes?: string[];\n /**\n * Nonce\n * @description nonce\n */\n nonce?: string;\n /**\n * Force refresh token (only for Android)\n * @description force refresh token\n * @default false\n * @note On Android, the OS caches access tokens, and if a token is invalid (e.g., user revoked app access), the plugin might return an invalid accessToken. Using getAuthorizationCode() is recommended to ensure the token is valid.\n */\n forceRefreshToken?: boolean;\n /**\n * Force account selection prompt (iOS)\n * @description forces the account selection prompt to appear on iOS\n * @default false\n */\n forcePrompt?: boolean;\n /**\n * Style\n * @description style\n * @default 'standard'\n */\n style?: 'bottom' | 'standard';\n /**\n * Filter by authorized accounts (Android only)\n * @description Only show accounts that have previously been used to sign in to the app.\n * This option is only available for the 'bottom' style.\n * Note: For Family Link supervised accounts, this should be set to false.\n * @default true\n */\n filterByAuthorizedAccounts?: boolean;\n /**\n * Auto select enabled (Android only)\n * @description Automatically select the account if only one Google account is available.\n * This option is only available for the 'bottom' style.\n * @default false\n */\n autoSelectEnabled?: boolean;\n /**\n * Prompt parameter for Google OAuth (Web only)\n * @description A space-delimited, case-sensitive list of prompts to present the user.\n * If you don't specify this parameter, the user will be prompted only the first time your project requests access.\n *\n * **Possible values:**\n * - `none`: Don't display any authentication or consent screens. Must not be specified with other values.\n * - `consent`: Prompt the user for consent.\n * - `select_account`: Prompt the user to select an account.\n *\n * **Examples:**\n * - `prompt: 'consent'` - Always show consent screen\n * - `prompt: 'select_account'` - Always show account selection\n * - `prompt: 'consent select_account'` - Show both consent and account selection\n *\n * **Note:** This parameter only affects web platform behavior. Mobile platforms use their own native prompts.\n *\n * @example 'consent'\n * @example 'select_account'\n * @example 'consent select_account'\n * @see [Google OAuth2 Prompt Parameter](https://developers.google.com/identity/protocols/oauth2/openid-connect#prompt)\n * @since 7.12.0\n */\n prompt?: 'none' | 'consent' | 'select_account' | 'consent select_account' | 'select_account consent';\n}\n\nexport interface GoogleLoginResponseOnline {\n accessToken: AccessToken | null;\n idToken: string | null;\n profile: {\n email: string | null;\n familyName: string | null;\n givenName: string | null;\n id: string | null;\n name: string | null;\n imageUrl: string | null;\n };\n responseType: 'online';\n}\n\nexport interface GoogleLoginResponseOffline {\n serverAuthCode: string;\n responseType: 'offline';\n}\n\nexport type GoogleLoginResponse = GoogleLoginResponseOnline | GoogleLoginResponseOffline;\n\nexport interface AppleProviderOptions {\n /**\n * Scopes\n * @description An array of scopes to request during login\n * @example [\"name\", \"email\"]\n * default: [\"name\", \"email\"]\n */\n scopes?: string[];\n /**\n * Nonce\n * @description nonce\n */\n nonce?: string;\n /**\n * State\n * @description state\n */\n state?: string;\n /**\n * Use Broadcast Channel for authentication flow\n * @description When enabled, uses Broadcast Channel API for communication instead of URL redirects.\n * Only applicable on platforms that support Broadcast Channel (Android).\n * @default false\n */\n useBroadcastChannel?: boolean;\n}\n\nexport interface AppleProviderResponse {\n /**\n * Access token from Apple\n * @description Content depends on `useProperTokenExchange` setting:\n * - When `useProperTokenExchange: true`: Real access token from Apple (~1 hour validity)\n * - When `useProperTokenExchange: false`: Contains authorization code as token (legacy mode)\n * Use `idToken` for user authentication, `accessToken` for API calls when properly exchanged.\n */\n accessToken: AccessToken | null;\n\n /**\n * Identity token (JWT) from Apple\n * @description Always contains the JWT with user identity information including:\n * - User ID (sub claim)\n * - Email (if user granted permission)\n * - Name components (if user granted permission)\n * - Email verification status\n * This is the primary token for user authentication and should be verified on your backend.\n */\n idToken: string | null;\n\n /**\n * User profile information\n * @description Basic user profile data extracted from the identity token and Apple response:\n * - `user`: Apple's user identifier (sub claim from idToken)\n * - `email`: User's email address (if permission granted)\n * - `givenName`: User's first name (if permission granted)\n * - `familyName`: User's last name (if permission granted)\n */\n profile: {\n user: string;\n email: string | null;\n givenName: string | null;\n familyName: string | null;\n };\n\n /**\n * Authorization code for proper token exchange (when useProperTokenExchange is enabled)\n * @description Only present when `useProperTokenExchange` is `true`. This code should be exchanged\n * for proper access tokens on your backend using Apple's token endpoint. Use this for secure\n * server-side token validation and to obtain refresh tokens.\n * @see https://developer.apple.com/documentation/sign_in_with_apple/tokenresponse\n */\n authorizationCode?: string;\n}\n\nexport type LoginOptions =\n | {\n provider: 'facebook';\n options: FacebookLoginOptions;\n }\n | {\n provider: 'google';\n options: GoogleLoginOptions;\n }\n | {\n provider: 'apple';\n options: AppleProviderOptions;\n }\n | {\n provider: 'twitter';\n options: TwitterLoginOptions;\n }\n | {\n provider: 'oauth2';\n options: OAuth2LoginOptions;\n };\n\nexport type LoginResult =\n | {\n provider: 'facebook';\n result: FacebookLoginResponse;\n }\n | {\n provider: 'google';\n result: GoogleLoginResponse;\n }\n | {\n provider: 'apple';\n result: AppleProviderResponse;\n }\n | {\n provider: 'twitter';\n result: TwitterLoginResponse;\n }\n | {\n provider: 'oauth2';\n result: OAuth2LoginResponse;\n };\n\nexport interface AccessToken {\n applicationId?: string;\n declinedPermissions?: string[];\n expires?: string;\n isExpired?: boolean;\n lastRefresh?: string;\n permissions?: string[];\n token: string;\n tokenType?: string;\n refreshToken?: string;\n userId?: string;\n}\n\nexport interface FacebookLoginResponse {\n accessToken: AccessToken | null;\n idToken: string | null;\n profile: {\n userID: string;\n email: string | null;\n friendIDs: string[];\n birthday: string | null;\n ageRange: { min?: number; max?: number } | null;\n gender: string | null;\n location: { id: string; name: string } | null;\n hometown: { id: string; name: string } | null;\n profileURL: string | null;\n name: string | null;\n imageURL: string | null;\n };\n}\n\nexport interface TwitterProfile {\n id: string;\n username: string;\n name: string | null;\n profileImageUrl: string | null;\n verified: boolean;\n email?: string | null;\n}\n\nexport interface TwitterLoginResponse {\n accessToken: AccessToken | null;\n refreshToken?: string | null;\n scope: string[];\n tokenType: 'bearer';\n expiresIn?: number | null;\n profile: TwitterProfile;\n}\n\nexport interface AuthorizationCode {\n /**\n * Jwt\n * @description A JSON web token\n */\n jwt?: string;\n /**\n * Access Token\n * @description An access token\n */\n accessToken?: string;\n}\n\nexport interface AuthorizationCodeOptions {\n /**\n * Provider\n * @description Provider for the authorization code\n */\n provider: 'apple' | 'google' | 'facebook' | 'twitter' | 'oauth2';\n /**\n * Provider ID for OAuth2 providers (required when provider is 'oauth2')\n * @description The ID used when configuring the OAuth2 provider in initialize()\n */\n providerId?: string;\n}\n\nexport interface isLoggedInOptions {\n /**\n * Provider\n * @description Provider for the isLoggedIn\n */\n provider: 'apple' | 'google' | 'facebook' | 'twitter' | 'oauth2';\n /**\n * Provider ID for OAuth2 providers (required when provider is 'oauth2')\n * @description The ID used when configuring the OAuth2 provider in initialize()\n */\n providerId?: string;\n}\n\n// Define the provider-specific call types\nexport type ProviderSpecificCall = 'facebook#getProfile' | 'facebook#requestTracking';\n\n// Define the options and response types for each specific call\nexport interface FacebookGetProfileOptions {\n /**\n * Fields to retrieve from Facebook profile\n * @example [\"id\", \"name\", \"email\", \"picture\"]\n */\n fields?: string[];\n}\n\nexport interface FacebookGetProfileResponse {\n /**\n * Facebook profile data\n */\n profile: {\n id: string | null;\n name: string | null;\n email: string | null;\n first_name: string | null;\n last_name: string | null;\n picture?: {\n data: {\n height: number | null;\n is_silhouette: boolean | null;\n url: string | null;\n width: number | null;\n };\n } | null;\n [key: string]: any; // For additional fields that might be requested\n };\n}\n\nexport interface OpenSecureWindowOptions {\n /**\n * The endpoint to open\n */\n authEndpoint: string;\n /**\n * The redirect URI to use for the openSecureWindow call.\n * This will be checked to make sure it matches the redirect URI after the window finishes the redirection.\n */\n redirectUri: string;\n /**\n * The name of the broadcast channel to listen to, relevant only for web\n */\n broadcastChannelName?: string;\n}\n\nexport interface OpenSecureWindowResponse {\n /**\n * The result of the openSecureWindow call\n */\n redirectedUri: string;\n}\n\nexport type FacebookRequestTrackingOptions = Record<string, never>;\n\nexport interface FacebookRequestTrackingResponse {\n /**\n * App tracking authorization status\n */\n status: 'authorized' | 'denied' | 'notDetermined' | 'restricted';\n}\n\n// Map call strings to their options and response types\nexport type ProviderSpecificCallOptionsMap = {\n 'facebook#getProfile': FacebookGetProfileOptions;\n 'facebook#requestTracking': FacebookRequestTrackingOptions;\n};\n\nexport type ProviderSpecificCallResponseMap = {\n 'facebook#getProfile': FacebookGetProfileResponse;\n 'facebook#requestTracking': FacebookRequestTrackingResponse;\n};\n\n// Add a helper type to map providers to their response types\nexport type ProviderResponseMap = {\n facebook: FacebookLoginResponse;\n google: GoogleLoginResponse;\n apple: AppleProviderResponse;\n twitter: TwitterLoginResponse;\n oauth2: OAuth2LoginResponse;\n};\n\nexport interface SocialLoginPlugin {\n /**\n * Initialize the plugin\n * @description initialize the plugin with the required options\n */\n initialize(options: InitializeOptions): Promise<void>;\n /**\n * Login with the selected provider\n * @description login with the selected provider\n */\n login<T extends LoginOptions['provider']>(\n options: Extract<LoginOptions, { provider: T }>,\n ): Promise<{ provider: T; result: ProviderResponseMap[T] }>;\n /**\n * Logout\n * @description Logout the user from the specified provider\n *\n * **Google Offline Mode Limitation:**\n * This method is NOT supported when Google is initialized with `mode: 'offline'`.\n * It will reject with error: \"logout is not implemented when using offline mode\"\n *\n * @throws Error if Google provider is in offline mode\n */\n logout(options: {\n provider: 'apple' | 'google' | 'facebook' | 'twitter' | 'oauth2';\n providerId?: string;\n }): Promise<void>;\n /**\n * IsLoggedIn\n * @description Check if the user is currently logged in with the specified provider\n *\n * **Google Offline Mode Limitation:**\n * This method is NOT supported when Google is initialized with `mode: 'offline'`.\n * It will reject with error: \"isLoggedIn is not implemented when using offline mode\"\n *\n * @throws Error if Google provider is in offline mode\n */\n isLoggedIn(options: isLoggedInOptions): Promise<{ isLoggedIn: boolean }>;\n\n /**\n * Get the current authorization code\n * @description Get the authorization code for server-side authentication\n *\n * **Google Offline Mode Limitation:**\n * This method is NOT supported when Google is initialized with `mode: 'offline'`.\n * It will reject with error: \"getAuthorizationCode is not implemented when using offline mode\"\n *\n * In offline mode, the authorization code (serverAuthCode) is already returned by the `login()` method.\n *\n * @throws Error if Google provider is in offline mode\n */\n getAuthorizationCode(options: AuthorizationCodeOptions): Promise<AuthorizationCode>;\n /**\n * Refresh the access token\n * @description refresh the access token\n *\n * **Google Offline Mode Limitation:**\n * This method is NOT supported when Google is initialized with `mode: 'offline'`.\n * It will reject with error: \"refresh is not implemented when using offline mode\"\n *\n * @throws Error if Google provider is in offline mode\n */\n refresh(options: LoginOptions): Promise<void>;\n\n /**\n * Execute provider-specific calls\n * @description Execute a provider-specific functionality\n */\n providerSpecificCall<T extends ProviderSpecificCall>(options: {\n call: T;\n options: ProviderSpecificCallOptionsMap[T];\n }): Promise<ProviderSpecificCallResponseMap[T]>;\n\n /**\n * Get the native Capacitor plugin version\n *\n * @returns {Promise<{ id: string }>} an Promise with version for this device\n * @throws An error if the something went wrong\n */\n getPluginVersion(): Promise<{ version: string }>;\n\n /**\n * Opens a secured window for OAuth2 authentication.\n * For web, you should have the code in the redirected page to use a broadcast channel to send the redirected url to the app\n * Something like:\n * ```html\n * <html>\n * <head></head>\n * <body>\n * <script>\n * const searchParams = new URLSearchParams(location.search)\n * if (searchParams.has(\"code\")) {\n * new BroadcastChannel(\"my-channel-name\").postMessage(location.href);\n * window.close();\n * }\n * </script>\n * </body>\n * </html>\n * ```\n * For mobile, you should have a redirect uri that opens the app, something like: `myapp://oauth_callback/`\n * And make sure to register it in the app's info.plist:\n * ```xml\n * <key>CFBundleURLTypes</key>\n * <array>\n * <dict>\n * <key>CFBundleURLSchemes</key>\n * <array>\n * <string>myapp</string>\n * </array>\n * </dict>\n * </array>\n * ```\n * And in the AndroidManifest.xml file:\n * ```xml\n * <activity>\n * <intent-filter>\n * <action android:name=\"android.intent.action.VIEW\" />\n * <category android:name=\"android.intent.category.DEFAULT\" />\n * <category android:name=\"android.intent.category.BROWSABLE\" />\n * <data android:host=\"oauth_callback\" android:scheme=\"myapp\" />\n * </intent-filter>\n * </activity>\n * ```\n * @param options - the options for the openSecureWindow call\n */\n openSecureWindow(options: OpenSecureWindowOptions): Promise<OpenSecureWindowResponse>;\n}\n"]}
@@ -16,7 +16,7 @@ import GoogleSignIn
16
16
  */
17
17
  @objc(SocialLoginPlugin)
18
18
  public class SocialLoginPlugin: CAPPlugin, CAPBridgedPlugin {
19
- private let pluginVersion: String = "8.2.23"
19
+ private let pluginVersion: String = "8.2.25"
20
20
  public let identifier = "SocialLoginPlugin"
21
21
  public let jsName = "SocialLogin"
22
22
  public let pluginMethods: [CAPPluginMethod] = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-social-login",
3
- "version": "8.2.23",
3
+ "version": "8.2.25",
4
4
  "description": "All social logins in one plugin",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",
@@ -70,6 +70,13 @@ const defaultProviders = {
70
70
  apple: 'implementation',
71
71
  twitter: 'implementation',
72
72
  };
73
+ /**
74
+ * 0 - Error
75
+ * 1 - Warning
76
+ * 2 - Info
77
+ * 3 - Success
78
+ */
79
+ let logLevel = 3;
73
80
  // ============================================================================
74
81
  // Common: Logging Utilities
75
82
  // ============================================================================
@@ -95,22 +102,33 @@ function log(message, emoji = '', color = '') {
95
102
  const resetCode = color ? colors.reset : '';
96
103
  console.log(`${colorCode}${emojiPart}${message}${resetCode}`);
97
104
  }
98
- function logSuccess(message) {
99
- log(message, '✔', colors.green);
100
- }
101
105
  function logError(message) {
102
- log(message, '✖', colors.red);
106
+ if (logLevel >= 0) {
107
+ log(message, '✖', colors.red);
108
+ }
109
+ }
110
+ function logWarning(message) {
111
+ if (logLevel >= 1) {
112
+ log(message, '⚠', colors.yellow);
113
+ }
103
114
  }
104
115
  function logInfo(message) {
105
- log(message, 'ℹ', colors.blue);
116
+ if (logLevel >= 2) {
117
+ log(message, 'ℹ', colors.blue);
118
+ }
106
119
  }
107
- function logWarning(message) {
108
- log(message, '⚠', colors.yellow);
120
+ function logSuccess(message) {
121
+ if (logLevel >= 3) {
122
+ log(message, '✔', colors.green);
123
+ }
109
124
  }
110
125
  /**
111
126
  * Log provider configuration status
112
127
  */
113
128
  function logProviderConfig(providerConfig) {
129
+ if (logLevel < 3) {
130
+ return;
131
+ }
114
132
  log('\nProvider configuration:', '', colors.bright);
115
133
  const providers = ['google', 'facebook', 'apple', 'twitter'];
116
134
  for (const provider of providers) {
@@ -138,10 +156,11 @@ function logProviderConfig(providerConfig) {
138
156
  function getProviderConfig() {
139
157
  try {
140
158
  if (!CONFIG_JSON) {
141
- logInfo('No CAPACITOR_CONFIG found, using defaults');
159
+ logWarning('No CAPACITOR_CONFIG found, using defaults');
142
160
  return defaultProviders;
143
161
  }
144
162
  const config = JSON.parse(CONFIG_JSON);
163
+ logLevel = config.plugins?.SocialLogin?.logLevel ?? 3;
145
164
  const providerConfig = config.plugins?.SocialLogin?.providers || defaultProviders;
146
165
  // Normalize config: convert true to 'implementation', false to 'compileOnly'
147
166
  const normalized = {};
@@ -315,17 +334,17 @@ function main() {
315
334
  // Route to platform-specific configuration
316
335
  switch (PLATFORM) {
317
336
  case 'android':
318
- log('Configuring dynamic provider dependencies for SocialLogin', '🔧', colors.cyan);
319
337
  // eslint-disable-next-line no-case-declarations
320
338
  const androidConfig = getProviderConfig();
339
+ logInfo('Configuring dynamic provider dependencies for SocialLogin');
321
340
  logProviderConfig(androidConfig);
322
341
  configureAndroid(androidConfig);
323
342
  logSuccess('Configuration complete\n');
324
343
  break;
325
344
  case 'ios':
326
- log('Configuring dynamic provider dependencies for SocialLogin', '🔧', colors.cyan);
327
345
  // eslint-disable-next-line no-case-declarations
328
346
  const iosConfig = getProviderConfig();
347
+ logInfo('Configuring dynamic provider dependencies for SocialLogin');
329
348
  logProviderConfig(iosConfig);
330
349
  configureIOS(iosConfig);
331
350
  logSuccess('Configuration complete\n');
@@ -335,9 +354,9 @@ function main() {
335
354
  break;
336
355
  default:
337
356
  // If platform is not specified, configure all platforms (backward compatibility)
338
- log('Configuring dynamic provider dependencies for SocialLogin', '🔧', colors.blue);
339
357
  // eslint-disable-next-line no-case-declarations
340
358
  const defaultConfig = getProviderConfig();
359
+ logInfo('Configuring dynamic provider dependencies for SocialLogin');
341
360
  logProviderConfig(defaultConfig);
342
361
  logWarning(`Unknown platform: ${PLATFORM || 'undefined'}, configuring all platforms`);
343
362
  configureAndroid(defaultConfig);