@capgo/capacitor-social-login 0.0.49 → 0.0.51

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
@@ -75,22 +75,6 @@ const res = await SocialLogin.login({
75
75
 
76
76
  ### Android configuration
77
77
 
78
- In file `android/app/src/main/AndroidManifest.xml`, add the following XML elements under `<manifest><application>` :
79
-
80
- ```xml
81
- <meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id"/>
82
- <meta-data android:name="com.facebook.sdk.ClientToken" android:value="@string/facebook_client_token"/>
83
- ```
84
-
85
- In file `android/app/src/main/res/values/strings.xml` add the following lines :
86
-
87
- ```xml
88
- <string name="facebook_app_id">[APP_ID]</string>
89
- <string name="facebook_client_token">[CLIENT_TOKEN]</string>
90
- ```
91
-
92
- Don't forget to replace `[APP_ID]` and `[CLIENT_TOKEN]` by your Facebook application Id.
93
-
94
78
  More information can be found here: https://developers.facebook.com/docs/android/getting-started
95
79
 
96
80
  Then call the `initialize` method with the `facebook` provider
@@ -99,6 +83,7 @@ Then call the `initialize` method with the `facebook` provider
99
83
  await SocialLogin.initialize({
100
84
  facebook: {
101
85
  appId: 'your-app-id',
86
+ clientToken: 'your-client-token',
102
87
  },
103
88
  });
104
89
  const res = await SocialLogin.login({
@@ -362,7 +347,7 @@ Refresh the access token
362
347
 
363
348
  | Prop | Type |
364
349
  | -------------- | ---------------------------------------------------------------------------------------- |
365
- | **`facebook`** | <code>{ appId: string; }</code> |
350
+ | **`facebook`** | <code>{ appId: string; clientToken: string; }</code> |
366
351
  | **`google`** | <code>{ iOSClientId?: string; iOSServerClientId?: string; webClientId?: string; }</code> |
367
352
  | **`apple`** | <code>{ clientId?: string; redirectUrl?: string; }</code> |
368
353
 
@@ -8,12 +8,16 @@ import com.facebook.AccessToken;
8
8
  import com.facebook.CallbackManager;
9
9
  import com.facebook.FacebookCallback;
10
10
  import com.facebook.FacebookException;
11
+ import com.facebook.FacebookSdk;
11
12
  import com.facebook.GraphRequest;
12
13
  import com.facebook.GraphResponse;
14
+ import com.facebook.login.LoginBehavior;
13
15
  import com.facebook.login.LoginManager;
14
16
  import com.facebook.login.LoginResult;
17
+ import com.getcapacitor.JSArray;
15
18
  import com.getcapacitor.JSObject;
16
19
  import com.getcapacitor.PluginCall;
20
+ import androidx.activity.result.ActivityResultRegistryOwner;
17
21
  import ee.forgr.capacitor.social.login.helpers.JsonHelper;
18
22
  import ee.forgr.capacitor.social.login.helpers.SocialProvider;
19
23
  import java.util.Collection;
@@ -31,67 +35,92 @@ public class FacebookProvider implements SocialProvider {
31
35
  this.activity = activity;
32
36
  }
33
37
 
34
- public void initialize(JSONObject _config) {
35
- this.callbackManager = CallbackManager.Factory.create();
36
-
37
- LoginManager.getInstance()
38
- .registerCallback(
39
- callbackManager,
40
- new FacebookCallback<LoginResult>() {
41
- @Override
42
- public void onSuccess(LoginResult loginResult) {
43
- Log.d(LOG_TAG, "LoginManager.onSuccess");
44
- }
45
-
46
- @Override
47
- public void onCancel() {
48
- Log.d(LOG_TAG, "LoginManager.onCancel");
49
- }
50
-
51
- @Override
52
- public void onError(FacebookException exception) {
53
- Log.e(LOG_TAG, "LoginManager.onError", exception);
54
- }
55
- }
56
- );
38
+ public void initialize(JSONObject config) {
39
+ try {
40
+ // Set Facebook App ID
41
+ String facebookAppId = config.getString("appId");
42
+ FacebookSdk.setApplicationId(facebookAppId);
43
+
44
+ // Set Facebook Client Token
45
+ String facebookClientToken = config.getString("clientToken");
46
+ FacebookSdk.setClientToken(facebookClientToken);
47
+
48
+ // Initialize Facebook SDK
49
+ FacebookSdk.sdkInitialize(activity.getApplicationContext());
50
+
51
+ this.callbackManager = CallbackManager.Factory.create();
52
+
53
+ LoginManager.getInstance().registerCallback(callbackManager,
54
+ new FacebookCallback<LoginResult>() {
55
+ @Override
56
+ public void onSuccess(LoginResult loginResult) {
57
+ Log.d(LOG_TAG, "LoginManager.onSuccess");
58
+ }
59
+
60
+ @Override
61
+ public void onCancel() {
62
+ Log.d(LOG_TAG, "LoginManager.onCancel");
63
+ }
64
+
65
+ @Override
66
+ public void onError(FacebookException exception) {
67
+ Log.e(LOG_TAG, "LoginManager.onError", exception);
68
+ }
69
+ });
70
+ } catch (JSONException e) {
71
+ Log.e(LOG_TAG, "Error initializing Facebook SDK", e);
72
+ throw new RuntimeException("Failed to initialize Facebook SDK: " + e.getMessage());
73
+ }
57
74
  }
58
75
 
59
76
  @Override
60
77
  public void login(PluginCall call, JSONObject config) {
61
78
  try {
62
- Collection<String> permissions = JsonHelper.jsonArrayToList(
63
- config.getJSONArray("permissions")
64
- );
65
- LoginManager.getInstance()
66
- .registerCallback(
67
- callbackManager,
68
- new FacebookCallback<LoginResult>() {
69
- @Override
70
- public void onSuccess(LoginResult loginResult) {
71
- Log.d(LOG_TAG, "LoginManager.onSuccess");
72
- AccessToken accessToken = loginResult.getAccessToken();
73
- JSObject result = new JSObject();
74
- result.put("accessToken", accessToken.getToken());
75
- result.put("userId", accessToken.getUserId());
76
- call.resolve(result);
77
- }
78
-
79
- @Override
80
- public void onCancel() {
81
- Log.d(LOG_TAG, "LoginManager.onCancel");
82
- call.reject("Login cancelled");
83
- }
84
-
85
- @Override
86
- public void onError(FacebookException exception) {
87
- Log.e(LOG_TAG, "LoginManager.onError", exception);
88
- call.reject(exception.getMessage());
89
- }
90
- }
79
+ Collection<String> permissions = JsonHelper.jsonArrayToList(
80
+ config.getJSONArray("permissions")
91
81
  );
92
- LoginManager.getInstance().logIn(activity, permissions);
82
+ boolean limitedLogin = config.optBoolean("limitedLogin", false);
83
+ String nonce = config.optString("nonce", "");
84
+
85
+ LoginManager.getInstance().registerCallback(callbackManager,
86
+ new FacebookCallback<LoginResult>() {
87
+ @Override
88
+ public void onSuccess(LoginResult loginResult) {
89
+ Log.d(LOG_TAG, "LoginManager.onSuccess");
90
+ AccessToken accessToken = loginResult.getAccessToken();
91
+ JSObject result = new JSObject();
92
+ result.put("accessToken", createAccessTokenObject(accessToken));
93
+ result.put("authenticationToken", loginResult.getAuthenticationToken() != null ? loginResult.getAuthenticationToken().getToken() : null);
94
+ // TODO: Fetch profile information and add it to the result
95
+ call.resolve(result);
96
+ }
97
+
98
+ @Override
99
+ public void onCancel() {
100
+ Log.d(LOG_TAG, "LoginManager.onCancel");
101
+ call.reject("Login cancelled");
102
+ }
103
+
104
+ @Override
105
+ public void onError(FacebookException exception) {
106
+ Log.e(LOG_TAG, "LoginManager.onError", exception);
107
+ call.reject(exception.getMessage());
108
+ }
109
+ });
110
+
111
+ LoginManager loginManager = LoginManager.getInstance();
112
+ if (limitedLogin) {
113
+ Log.w(LOG_TAG, "Limited login is not available for Android");
114
+ }
115
+
116
+ loginManager.setLoginBehavior(LoginBehavior.NATIVE_WITH_FALLBACK);
117
+ if (!nonce.isEmpty()) {
118
+ loginManager.logIn((ActivityResultRegistryOwner) activity, callbackManager, permissions, nonce);
119
+ } else {
120
+ loginManager.logIn((ActivityResultRegistryOwner) activity, callbackManager, permissions);
121
+ }
93
122
  } catch (JSONException e) {
94
- call.reject("Invalid permissions format");
123
+ call.reject("Invalid login options format");
95
124
  }
96
125
  }
97
126
 
@@ -170,6 +199,23 @@ public class FacebookProvider implements SocialProvider {
170
199
  int resultCode,
171
200
  Intent data
172
201
  ) {
173
- return callbackManager.onActivityResult(requestCode, resultCode, data);
202
+ Log.d(LOG_TAG, "FacebookProvider.handleOnActivityResult called");
203
+ if (callbackManager != null) {
204
+ return callbackManager.onActivityResult(requestCode, resultCode, data);
205
+ }
206
+ return false;
207
+ }
208
+
209
+ private JSObject createAccessTokenObject(AccessToken accessToken) {
210
+ JSObject tokenObject = new JSObject();
211
+ tokenObject.put("applicationId", accessToken.getApplicationId());
212
+ tokenObject.put("declinedPermissions", new JSArray(accessToken.getDeclinedPermissions()));
213
+ tokenObject.put("expires", accessToken.getExpires().getTime());
214
+ tokenObject.put("isExpired", accessToken.isExpired());
215
+ tokenObject.put("lastRefresh", accessToken.getLastRefresh().getTime());
216
+ tokenObject.put("permissions", new JSArray(accessToken.getPermissions()));
217
+ tokenObject.put("token", accessToken.getToken());
218
+ tokenObject.put("userId", accessToken.getUserId());
219
+ return tokenObject;
174
220
  }
175
221
  }
@@ -69,11 +69,24 @@ public class SocialLoginPlugin extends Plugin {
69
69
 
70
70
  JSObject facebook = call.getObject("facebook");
71
71
  if (facebook != null) {
72
- FacebookProvider facebookProvider = new FacebookProvider(
73
- this.getActivity()
74
- );
75
- facebookProvider.initialize(new JSONObject());
76
- this.socialProviderHashMap.put("facebook", facebookProvider);
72
+ String facebookAppId = facebook.getString("appId");
73
+ String facebookClientToken = facebook.getString("clientToken");
74
+ if (facebookAppId == null || facebookAppId.isEmpty()) {
75
+ call.reject("facebook.appId is null or empty");
76
+ return;
77
+ }
78
+ if (facebookClientToken == null || facebookClientToken.isEmpty()) {
79
+ call.reject("facebook.clientToken is null or empty");
80
+ return;
81
+ }
82
+ FacebookProvider facebookProvider = new FacebookProvider(this.getActivity());
83
+ try {
84
+ facebookProvider.initialize(facebook);
85
+ this.socialProviderHashMap.put("facebook", facebookProvider);
86
+ } catch (Exception e) {
87
+ call.reject("Failed to initialize Facebook provider: " + e.getMessage());
88
+ return;
89
+ }
77
90
  }
78
91
 
79
92
  call.resolve();
@@ -176,4 +189,24 @@ public class SocialLoginPlugin extends Plugin {
176
189
  Log.e(SocialLoginPlugin.LOG_TAG, "Cannot handle apple login intent");
177
190
  }
178
191
  }
192
+
193
+ @Override
194
+ protected void handleOnActivityResult(int requestCode, int resultCode, Intent data) {
195
+ super.handleOnActivityResult(requestCode, resultCode, data);
196
+
197
+ Log.d(LOG_TAG, "SocialLoginPlugin.handleOnActivityResult called");
198
+
199
+ // Handle Facebook login result
200
+ SocialProvider facebookProvider = socialProviderHashMap.get("facebook");
201
+ if (facebookProvider instanceof FacebookProvider) {
202
+ boolean handled = ((FacebookProvider) facebookProvider).handleOnActivityResult(requestCode, resultCode, data);
203
+ if (handled) {
204
+ Log.d(LOG_TAG, "Facebook activity result handled");
205
+ return;
206
+ }
207
+ }
208
+
209
+ // Handle other providers' activity results if needed
210
+ Log.d(LOG_TAG, "Activity result not handled by any provider");
211
+ }
179
212
  }
package/dist/docs.json CHANGED
@@ -159,7 +159,7 @@
159
159
  "tags": [],
160
160
  "docs": "",
161
161
  "complexTypes": [],
162
- "type": "{ appId: string; } | undefined"
162
+ "type": "{ appId: string; clientToken: string; } | undefined"
163
163
  },
164
164
  {
165
165
  "name": "google",
@@ -4,6 +4,10 @@ export interface InitializeOptions {
4
4
  * Facebook App ID, provided by Facebook for web, in mobile it's set in the native files
5
5
  */
6
6
  appId: string;
7
+ /**
8
+ * Facebook Client Token, provided by Facebook for web, in mobile it's set in the native files
9
+ */
10
+ clientToken: string;
7
11
  };
8
12
  google?: {
9
13
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["export interface InitializeOptions {\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\n google?: {\n /**\n * The app's client ID, found and created in the Google Developers Console.\n * For iOS.\n * @example xxxxxx-xxxxxxxxxxxxxxxxxx.apps.googleusercontent.com\n * @since 3.1.0\n */\n iOSClientId?: string;\n /**\n * The app's server client ID, found and created in the Google Developers Console.\n * For iOS.\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 * For Android (and web in the future).\n * @example xxxxxx-xxxxxxxxxxxxxxxxxx.apps.googleusercontent.com\n * @since 3.1.0\n */\n webClientId?: 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, only for android\n */\n redirectUrl?: string;\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\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 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 * Set if your application needs to refresh access tokens when the user is not present at the browser.\n * In response use `serverAuthCode` key\n *\n * @default false\n * @since 3.1.0\n * */\n grantOfflineAccess?: boolean;\n}\n\nexport interface GoogleLoginResponse {\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}\n\nexport interface AppleProviderOptions {\n /**\n * Scopes\n * @description select scopes to login with\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\nexport interface AppleProviderResponse {\n user: string | null;\n email: string | null;\n givenName: string | null;\n familyName: string | null;\n identityToken: string | null;\n authorizationCode: string | null;\n}\n\nexport interface LoginOptions {\n /**\n * Provider\n * @description select provider to login with\n */\n provider: \"facebook\" | \"google\" | \"apple\" | \"twitter\";\n /**\n * Options\n * @description payload to login with\n */\n options: FacebookLoginOptions | GoogleLoginOptions | AppleProviderOptions;\n}\n\nexport interface LoginResult {\n /**\n * Provider\n * @description select provider to login with\n */\n provider: \"facebook\" | \"google\" | \"apple\" | \"twitter\";\n /**\n * Payload\n * @description payload to login with\n */\n result: FacebookLoginResponse | GoogleLoginResponse | AppleProviderResponse;\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 userId?: string;\n}\n\nexport interface FacebookLoginResponse {\n accessToken: AccessToken | 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 authenticationToken: string | null;\n}\n\nexport interface AuthorizationCode {\n /**\n * Jwt\n * @description A JSON web token\n */\n jwt: string;\n}\n\nexport interface AuthorizationCodeOptions {\n /**\n * Provider\n * @description Provider for the authorization code\n */\n provider: \"apple\" | \"google\" | \"facebook\";\n}\n\nexport interface isLoggedInOptions {\n /**\n * Provider\n * @description Provider for the isLoggedIn\n */\n provider: \"apple\" | \"google\" | \"facebook\";\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(options: LoginOptions): Promise<LoginResult>;\n /**\n * Logout\n * @description logout the user\n */\n logout(options: { provider: \"apple\" | \"google\" | \"facebook\" }): Promise<void>;\n /**\n * IsLoggedIn\n * @description logout the user\n */\n isLoggedIn(options: isLoggedInOptions): Promise<{ isLoggedIn: boolean }>;\n\n /**\n * Get the current access token\n * @description get the current access token\n */\n getAuthorizationCode(\n options: AuthorizationCodeOptions,\n ): Promise<AuthorizationCode>;\n /**\n * Refresh the access token\n * @description refresh the access token\n */\n refresh(options: LoginOptions): Promise<void>;\n}\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["export interface InitializeOptions {\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\n google?: {\n /**\n * The app's client ID, found and created in the Google Developers Console.\n * For iOS.\n * @example xxxxxx-xxxxxxxxxxxxxxxxxx.apps.googleusercontent.com\n * @since 3.1.0\n */\n iOSClientId?: string;\n /**\n * The app's server client ID, found and created in the Google Developers Console.\n * For iOS.\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 * For Android (and web in the future).\n * @example xxxxxx-xxxxxxxxxxxxxxxxxx.apps.googleusercontent.com\n * @since 3.1.0\n */\n webClientId?: 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, only for android\n */\n redirectUrl?: string;\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\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 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 * Set if your application needs to refresh access tokens when the user is not present at the browser.\n * In response use `serverAuthCode` key\n *\n * @default false\n * @since 3.1.0\n * */\n grantOfflineAccess?: boolean;\n}\n\nexport interface GoogleLoginResponse {\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}\n\nexport interface AppleProviderOptions {\n /**\n * Scopes\n * @description select scopes to login with\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\nexport interface AppleProviderResponse {\n user: string | null;\n email: string | null;\n givenName: string | null;\n familyName: string | null;\n identityToken: string | null;\n authorizationCode: string | null;\n}\n\nexport interface LoginOptions {\n /**\n * Provider\n * @description select provider to login with\n */\n provider: \"facebook\" | \"google\" | \"apple\" | \"twitter\";\n /**\n * Options\n * @description payload to login with\n */\n options: FacebookLoginOptions | GoogleLoginOptions | AppleProviderOptions;\n}\n\nexport interface LoginResult {\n /**\n * Provider\n * @description select provider to login with\n */\n provider: \"facebook\" | \"google\" | \"apple\" | \"twitter\";\n /**\n * Payload\n * @description payload to login with\n */\n result: FacebookLoginResponse | GoogleLoginResponse | AppleProviderResponse;\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 userId?: string;\n}\n\nexport interface FacebookLoginResponse {\n accessToken: AccessToken | 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 authenticationToken: string | null;\n}\n\nexport interface AuthorizationCode {\n /**\n * Jwt\n * @description A JSON web token\n */\n jwt: string;\n}\n\nexport interface AuthorizationCodeOptions {\n /**\n * Provider\n * @description Provider for the authorization code\n */\n provider: \"apple\" | \"google\" | \"facebook\";\n}\n\nexport interface isLoggedInOptions {\n /**\n * Provider\n * @description Provider for the isLoggedIn\n */\n provider: \"apple\" | \"google\" | \"facebook\";\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(options: LoginOptions): Promise<LoginResult>;\n /**\n * Logout\n * @description logout the user\n */\n logout(options: { provider: \"apple\" | \"google\" | \"facebook\" }): Promise<void>;\n /**\n * IsLoggedIn\n * @description logout the user\n */\n isLoggedIn(options: isLoggedInOptions): Promise<{ isLoggedIn: boolean }>;\n\n /**\n * Get the current access token\n * @description get the current access token\n */\n getAuthorizationCode(\n options: AuthorizationCodeOptions,\n ): Promise<AuthorizationCode>;\n /**\n * Refresh the access token\n * @description refresh the access token\n */\n refresh(options: LoginOptions): Promise<void>;\n}\n"]}
@@ -2,9 +2,13 @@ import Foundation
2
2
  import AuthenticationServices
3
3
  import Alamofire
4
4
 
5
- struct AppleProviderResponse {
6
- // let user: String
5
+ struct AppleProviderResponse: Codable {
6
+ let user: String
7
+ let email: String?
8
+ let givenName: String?
9
+ let familyName: String?
7
10
  let identityToken: String
11
+ let authorizationCode: String
8
12
  }
9
13
 
10
14
  // Define the Decodable structs for the response
@@ -86,6 +90,7 @@ class AppleProvider: NSObject, ASAuthorizationControllerDelegate, ASAuthorizatio
86
90
  private let TOKEN_URL = "https://appleid.apple.com/auth/token"
87
91
  private let SHARED_PREFERENCE_NAME = "AppleProviderSharedPrefs_0eda2642"
88
92
  private var redirectUrl = ""
93
+ private let USER_INFO_KEY = "AppleUserInfo"
89
94
 
90
95
  func initialize(redirectUrl: String? = nil) {
91
96
  if let redirectUrl = redirectUrl {
@@ -199,21 +204,27 @@ class AppleProvider: NSObject, ASAuthorizationControllerDelegate, ASAuthorizatio
199
204
  }
200
205
 
201
206
  func getCurrentUser(completion: @escaping (Result<AppleProviderResponse?, Error>) -> Void) {
202
- let appleIDProvider = ASAuthorizationAppleIDProvider()
203
- appleIDProvider.getCredentialState(forUserID: "currentUserIdentifier") { (credentialState, error) in
204
- if let error = error {
205
- completion(.failure(error))
206
- return
207
- }
207
+ retrieveUserInfo { userInfo in
208
+ if let userInfo = userInfo {
209
+ completion(.success(userInfo))
210
+ } else {
211
+ let appleIDProvider = ASAuthorizationAppleIDProvider()
212
+ appleIDProvider.getCredentialState(forUserID: "currentUserIdentifier") { (credentialState, error) in
213
+ if let error = error {
214
+ completion(.failure(error))
215
+ return
216
+ }
208
217
 
209
- switch credentialState {
210
- case .authorized:
211
- // User is authorized, you might want to fetch more details here
212
- completion(.success(nil))
213
- case .revoked, .notFound, .transferred:
214
- completion(.success(nil))
215
- @unknown default:
216
- completion(.failure(NSError(domain: "AppleProvider", code: 0, userInfo: [NSLocalizedDescriptionKey: "Unknown credential state"])))
218
+ switch credentialState {
219
+ case .authorized:
220
+ // User is authorized, but we don't have their info
221
+ completion(.success(nil))
222
+ case .revoked, .notFound, .transferred:
223
+ completion(.success(nil))
224
+ @unknown default:
225
+ completion(.failure(NSError(domain: "AppleProvider", code: 0, userInfo: [NSLocalizedDescriptionKey: "Unknown credential state"])))
226
+ }
227
+ }
217
228
  }
218
229
  }
219
230
  }
@@ -227,52 +238,44 @@ class AppleProvider: NSObject, ASAuthorizationControllerDelegate, ASAuthorizatio
227
238
 
228
239
  func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
229
240
  if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
230
- let _ = appleIDCredential.user
241
+ let userIdentifier = appleIDCredential.user
231
242
  let fullName = appleIDCredential.fullName
232
243
  let email = appleIDCredential.email
233
244
 
234
- // let response = AppleProviderResponse(
235
- // user: userIdentifier,
236
- // email: email,
237
- // givenName: fullName?.givenName,
238
- // familyName: fullName?.familyName,
239
- // identityToken: String(data: appleIDCredential.identityToken ?? Data(), encoding: .utf8) ?? "",
240
- // authorizationCode: String(data: appleIDCredential.authorizationCode ?? Data(), encoding: .utf8) ?? ""
241
- // )
242
-
243
- let errorCompletion: ((Result<AppleProviderResponse, AppleProviderError>) -> Void) = { result in
244
- do {
245
- let finalResult = try result.get()
246
- self.completion?(.success(finalResult))
247
- } catch {
248
- self.completion?(.failure(error))
249
- }
250
- }
251
-
252
- let authorizationCode = String(data: appleIDCredential.authorizationCode ?? Data(), encoding: .utf8) ?? ""
253
- let identityToken = String(data: appleIDCredential.identityToken ?? Data(), encoding: .utf8) ?? ""
254
-
255
- if !self.redirectUrl.isEmpty {
256
- let firstName = fullName?.givenName ?? "Jhon"
257
- let lastName = fullName?.familyName ?? "Doe"
258
-
259
- if let _ = fullName?.givenName {
260
- sendRequest(code: authorizationCode, identityToken: identityToken, email: email ?? "", firstName: firstName, lastName: lastName, completion: errorCompletion, skipUser: false)
245
+ retrieveUserInfo { savedUserInfo in
246
+ let response = AppleProviderResponse(
247
+ user: userIdentifier,
248
+ email: email ?? savedUserInfo?.email,
249
+ givenName: fullName?.givenName ?? savedUserInfo?.givenName,
250
+ familyName: fullName?.familyName ?? savedUserInfo?.familyName,
251
+ identityToken: String(data: appleIDCredential.identityToken ?? Data(), encoding: .utf8) ?? "",
252
+ authorizationCode: String(data: appleIDCredential.authorizationCode ?? Data(), encoding: .utf8) ?? ""
253
+ )
254
+
255
+ // Persist user info
256
+ self.persistUserInfo(userInfo: response)
257
+
258
+ if !self.redirectUrl.isEmpty {
259
+ let firstName = fullName?.givenName ?? ""
260
+ let lastName = fullName?.familyName ?? ""
261
+
262
+ self.sendRequest(code: response.authorizationCode, identityToken: response.identityToken, email: email ?? "", firstName: firstName, lastName: lastName, completion: { result in
263
+ switch result {
264
+ case .success(let appleResponse):
265
+ self.completion?(.success(appleResponse))
266
+ case .failure(let error):
267
+ self.completion?(.failure(error))
268
+ }
269
+ }, skipUser: fullName?.givenName == nil)
261
270
  } else {
262
- sendRequest(code: authorizationCode, identityToken: identityToken, email: email ?? "", firstName: firstName, lastName: lastName, completion: errorCompletion, skipUser: true)
263
- }
264
- } else {
265
- do {
266
- try self.persistState(idToken: identityToken, refreshToken: "", accessToken: "")
267
- let appleResponse = AppleProviderResponse(identityToken: identityToken)
268
- completion?(.success(appleResponse))
269
- return
270
- } catch {
271
- completion?(.failure(AppleProviderError.specificJsonWritingError(error)))
272
- return
271
+ do {
272
+ try self.persistState(idToken: response.identityToken, refreshToken: "", accessToken: "")
273
+ self.completion?(.success(response))
274
+ } catch {
275
+ self.completion?(.failure(AppleProviderError.specificJsonWritingError(error)))
276
+ }
273
277
  }
274
278
  }
275
- // completion?(.success(response))
276
279
  }
277
280
  }
278
281
 
@@ -365,7 +368,14 @@ class AppleProvider: NSObject, ASAuthorizationControllerDelegate, ASAuthorizatio
365
368
 
366
369
  do {
367
370
  try self.persistState(idToken: idToken, refreshToken: refreshToken, accessToken: accessToken)
368
- let appleResponse = AppleProviderResponse(identityToken: idToken)
371
+ let appleResponse = AppleProviderResponse(
372
+ user: "", // We don't have this information at this point
373
+ email: nil, // We don't have this information at this point
374
+ givenName: nil, // We don't have this information at this point
375
+ familyName: nil, // We don't have this information at this point
376
+ identityToken: idToken,
377
+ authorizationCode: "" // We don't have this information at this point
378
+ )
369
379
  completion(.success(appleResponse))
370
380
  return
371
381
  } catch {
@@ -375,8 +385,14 @@ class AppleProvider: NSObject, ASAuthorizationControllerDelegate, ASAuthorizatio
375
385
  }
376
386
 
377
387
  if (pathComponents.filter { $0.name == "ios_no_code" }).first != nil {
378
- // identityToken provided by apple
379
- let appleResponse = AppleProviderResponse(identityToken: identityToken)
388
+ let appleResponse = AppleProviderResponse(
389
+ user: "", // You might want to extract this from the identityToken
390
+ email: email,
391
+ givenName: firstName,
392
+ familyName: lastName,
393
+ identityToken: identityToken,
394
+ authorizationCode: code
395
+ )
380
396
 
381
397
  do {
382
398
  try self.persistState(idToken: identityToken, refreshToken: "", accessToken: "")
@@ -470,7 +486,14 @@ class AppleProvider: NSObject, ASAuthorizationControllerDelegate, ASAuthorizatio
470
486
  let userData = try? JSONSerialization.jsonObject(with: decodedData, options: []) as? [String: Any],
471
487
  let userId = userData["sub"] as? String {
472
488
  // Create the response object
473
- let appleResponse = AppleProviderResponse(identityToken: idToken)
489
+ let appleResponse = AppleProviderResponse(
490
+ user: userId,
491
+ email: nil, // You might want to extract this from the idToken if available
492
+ givenName: nil,
493
+ familyName: nil,
494
+ identityToken: idToken,
495
+ authorizationCode: code
496
+ )
474
497
 
475
498
  // Log the tokens (replace with your logging mechanism)
476
499
  print("Apple Access Token is: \(accessToken)")
@@ -513,4 +536,25 @@ class AppleProvider: NSObject, ASAuthorizationControllerDelegate, ASAuthorizatio
513
536
  func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
514
537
  return UIApplication.shared.windows.first!
515
538
  }
539
+
540
+ func persistUserInfo(userInfo: AppleProviderResponse) {
541
+ let encoder = JSONEncoder()
542
+ if let encoded = try? encoder.encode(userInfo) {
543
+ UserDefaults.standard.set(encoded, forKey: USER_INFO_KEY)
544
+ print("Successfully saved user info locally")
545
+ }
546
+ }
547
+
548
+ func retrieveUserInfo(completion: @escaping (AppleProviderResponse?) -> Void) {
549
+ if let savedUserInfo = UserDefaults.standard.object(forKey: USER_INFO_KEY) as? Data {
550
+ let decoder = JSONDecoder()
551
+ if let loadedUserInfo = try? decoder.decode(AppleProviderResponse.self, from: savedUserInfo) {
552
+ completion(loadedUserInfo)
553
+ } else {
554
+ completion(nil)
555
+ }
556
+ } else {
557
+ completion(nil)
558
+ }
559
+ }
516
560
  }
@@ -52,10 +52,20 @@ class FacebookProvider {
52
52
  return
53
53
  }
54
54
 
55
- DispatchQueue.main.async {
55
+ DispatchQueue.main.async { [weak self] in
56
+ guard let self = self else { return }
57
+
58
+ // Check if a user is already logged in
59
+ if AccessToken.current != nil {
60
+ // User is already logged in, return current session info
61
+ let response = self.createLoginResponse()
62
+ completion(.success(response))
63
+ return
64
+ }
65
+
56
66
  self.loginManager.logIn(configuration: configuration) { result in
57
67
  switch result {
58
- case .success:
68
+ case .success(_, _, _):
59
69
  let response = self.createLoginResponse()
60
70
  completion(.success(response))
61
71
  case .failed(let error):
@@ -63,7 +73,6 @@ class FacebookProvider {
63
73
  case .cancelled:
64
74
  completion(.failure(NSError(domain: "FacebookProvider", code: 0, userInfo: [NSLocalizedDescriptionKey: "Login cancelled"])))
65
75
  }
66
-
67
76
  }
68
77
  }
69
78
  }
@@ -123,28 +132,32 @@ class FacebookProvider {
123
132
  }
124
133
 
125
134
  func logout(completion: @escaping (Result<Void, Error>) -> Void) {
126
- loginManager.logOut()
127
- completion(.success(()))
135
+ DispatchQueue.main.async { [weak self] in
136
+ self?.loginManager.logOut()
137
+ completion(.success(()))
138
+ }
128
139
  }
129
140
 
130
141
  func getCurrentUser(completion: @escaping (Result<[String: Any]?, Error>) -> Void) {
131
- if let accessToken = AccessToken.current {
132
- let response: [String: Any] = [
133
- "accessToken": [
134
- "applicationID": accessToken.appID,
135
- "declinedPermissions": accessToken.declinedPermissions.map { $0.name },
136
- "expirationDate": accessToken.expirationDate,
137
- "isExpired": accessToken.isExpired,
138
- "refreshDate": accessToken.refreshDate,
139
- "permissions": accessToken.permissions.map { $0.name },
140
- "tokenString": accessToken.tokenString,
141
- "userID": accessToken.userID
142
- ],
143
- "profile": [:]
144
- ]
145
- completion(.success(response))
146
- } else {
147
- completion(.success(nil))
142
+ DispatchQueue.main.async {
143
+ if let accessToken = AccessToken.current {
144
+ let response: [String: Any] = [
145
+ "accessToken": [
146
+ "applicationID": accessToken.appID,
147
+ "declinedPermissions": accessToken.declinedPermissions.map { $0.name },
148
+ "expirationDate": accessToken.expirationDate,
149
+ "isExpired": accessToken.isExpired,
150
+ "refreshDate": accessToken.refreshDate,
151
+ "permissions": accessToken.permissions.map { $0.name },
152
+ "tokenString": accessToken.tokenString,
153
+ "userID": accessToken.userID
154
+ ],
155
+ "profile": [:]
156
+ ]
157
+ completion(.success(response))
158
+ } else {
159
+ completion(.success(nil))
160
+ }
148
161
  }
149
162
  }
150
163
 
@@ -199,7 +199,23 @@ public class SocialLoginPlugin: CAPPlugin, CAPBridgedPlugin {
199
199
  }
200
200
  case "apple":
201
201
  apple.getCurrentUser { result in
202
- self.handleCurrentUserResult(result, call: call)
202
+ switch result {
203
+ case .success(let appleResponse):
204
+ if let response = appleResponse {
205
+ call.resolve([
206
+ "user": response.user,
207
+ "email": response.email ?? "",
208
+ "givenName": response.givenName ?? "",
209
+ "familyName": response.familyName ?? "",
210
+ "identityToken": response.identityToken,
211
+ "authorizationCode": response.authorizationCode
212
+ ])
213
+ } else {
214
+ call.resolve([:])
215
+ }
216
+ case .failure(let error):
217
+ call.reject(error.localizedDescription)
218
+ }
203
219
  }
204
220
  default:
205
221
  call.reject("Invalid provider")
@@ -280,10 +296,16 @@ public class SocialLoginPlugin: CAPPlugin, CAPBridgedPlugin {
280
296
  switch result {
281
297
  case .success(let response):
282
298
  if let appleResponse = response as? AppleProviderResponse {
299
+ // The user info is already persisted in the AppleProvider class
283
300
  call.resolve([
284
301
  "provider": "apple",
285
302
  "result": [
286
- "identityToken": appleResponse.identityToken
303
+ "user": appleResponse.user,
304
+ "email": appleResponse.email ?? "",
305
+ "givenName": appleResponse.givenName ?? "",
306
+ "familyName": appleResponse.familyName ?? "",
307
+ "identityToken": appleResponse.identityToken,
308
+ "authorizationCode": appleResponse.authorizationCode
287
309
  ]
288
310
  ])
289
311
  } else if let googleResponse = response as? GoogleLoginResponse {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-social-login",
3
- "version": "0.0.49",
3
+ "version": "0.0.51",
4
4
  "description": "All social logins in one plugin",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",