@capgo/capacitor-social-login 0.0.50 → 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 +1 -1
- package/android/src/main/java/ee/forgr/capacitor/social/login/FacebookProvider.java +4 -6
- package/dist/docs.json +1 -1
- package/dist/esm/definitions.d.ts +4 -0
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Sources/SocialLoginPlugin/AppleProvider.swift +104 -60
- package/ios/Sources/SocialLoginPlugin/FacebookProvider.swift +35 -22
- package/ios/Sources/SocialLoginPlugin/SocialLoginPlugin.swift +24 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -347,7 +347,7 @@ Refresh the access token
|
|
|
347
347
|
|
|
348
348
|
| Prop | Type |
|
|
349
349
|
| -------------- | ---------------------------------------------------------------------------------------- |
|
|
350
|
-
| **`facebook`** | <code>{ appId: string; }</code>
|
|
350
|
+
| **`facebook`** | <code>{ appId: string; clientToken: string; }</code> |
|
|
351
351
|
| **`google`** | <code>{ iOSClientId?: string; iOSServerClientId?: string; webClientId?: string; }</code> |
|
|
352
352
|
| **`apple`** | <code>{ clientId?: string; redirectUrl?: string; }</code> |
|
|
353
353
|
|
|
@@ -30,7 +30,6 @@ public class FacebookProvider implements SocialProvider {
|
|
|
30
30
|
|
|
31
31
|
private Activity activity;
|
|
32
32
|
private CallbackManager callbackManager;
|
|
33
|
-
private PluginCall savedCall;
|
|
34
33
|
|
|
35
34
|
public FacebookProvider(Activity activity) {
|
|
36
35
|
this.activity = activity;
|
|
@@ -76,7 +75,6 @@ public class FacebookProvider implements SocialProvider {
|
|
|
76
75
|
|
|
77
76
|
@Override
|
|
78
77
|
public void login(PluginCall call, JSONObject config) {
|
|
79
|
-
this.savedCall = call;
|
|
80
78
|
try {
|
|
81
79
|
Collection<String> permissions = JsonHelper.jsonArrayToList(
|
|
82
80
|
config.getJSONArray("permissions")
|
|
@@ -94,19 +92,19 @@ public class FacebookProvider implements SocialProvider {
|
|
|
94
92
|
result.put("accessToken", createAccessTokenObject(accessToken));
|
|
95
93
|
result.put("authenticationToken", loginResult.getAuthenticationToken() != null ? loginResult.getAuthenticationToken().getToken() : null);
|
|
96
94
|
// TODO: Fetch profile information and add it to the result
|
|
97
|
-
|
|
95
|
+
call.resolve(result);
|
|
98
96
|
}
|
|
99
97
|
|
|
100
98
|
@Override
|
|
101
99
|
public void onCancel() {
|
|
102
100
|
Log.d(LOG_TAG, "LoginManager.onCancel");
|
|
103
|
-
|
|
101
|
+
call.reject("Login cancelled");
|
|
104
102
|
}
|
|
105
103
|
|
|
106
104
|
@Override
|
|
107
105
|
public void onError(FacebookException exception) {
|
|
108
106
|
Log.e(LOG_TAG, "LoginManager.onError", exception);
|
|
109
|
-
|
|
107
|
+
call.reject(exception.getMessage());
|
|
110
108
|
}
|
|
111
109
|
});
|
|
112
110
|
|
|
@@ -122,7 +120,7 @@ public class FacebookProvider implements SocialProvider {
|
|
|
122
120
|
loginManager.logIn((ActivityResultRegistryOwner) activity, callbackManager, permissions);
|
|
123
121
|
}
|
|
124
122
|
} catch (JSONException e) {
|
|
125
|
-
|
|
123
|
+
call.reject("Invalid login options format");
|
|
126
124
|
}
|
|
127
125
|
}
|
|
128
126
|
|
package/dist/docs.json
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
|
241
|
+
let userIdentifier = appleIDCredential.user
|
|
231
242
|
let fullName = appleIDCredential.fullName
|
|
232
243
|
let email = appleIDCredential.email
|
|
233
244
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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(
|
|
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
|
-
|
|
379
|
-
|
|
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(
|
|
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
|
-
|
|
127
|
-
|
|
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
|
-
|
|
132
|
-
let
|
|
133
|
-
|
|
134
|
-
"
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
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 {
|