From 99b921d51da67cc893482fad3cbbdbff8528dbe5 Mon Sep 17 00:00:00 2001 From: WcaleNieWolny Date: Sun, 10 Nov 2024 16:02:48 +0100 Subject: [PATCH 1/3] feat(iOS,web): implement scopes --- .../SocialLoginPlugin/GoogleProvider.swift | 30 +++++++++++++++++-- src/web.ts | 13 ++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/ios/Sources/SocialLoginPlugin/GoogleProvider.swift b/ios/Sources/SocialLoginPlugin/GoogleProvider.swift index 3e9acb5..1c7edfe 100644 --- a/ios/Sources/SocialLoginPlugin/GoogleProvider.swift +++ b/ios/Sources/SocialLoginPlugin/GoogleProvider.swift @@ -36,17 +36,28 @@ class GoogleProvider { } func login(payload: [String: Any], completion: @escaping (Result) -> Void) { + var scopes = payload["scopes"] as? [String] ?? self.defaultGrantedScopes + if (!scopes.contains(where: { $0 == "https://www.googleapis.com/auth/userinfo.email" })) { + scopes.append("https://www.googleapis.com/auth/userinfo.email") + } + if (!scopes.contains(where: { $0 == "https://www.googleapis.com/auth/userinfo.profile" })) { + scopes.append("https://www.googleapis.com/auth/userinfo.profile") + } + if (!scopes.contains(where: { $0 == "openid" })) { + scopes.append("openid") + } + DispatchQueue.main.async { + func login() { guard let presentingVc = UIApplication.shared.windows.first?.rootViewController else { completion(.failure(NSError(domain: "GoogleProvider", code: 0, userInfo: [NSLocalizedDescriptionKey: "No presenting view controller found"]))) return } - GIDSignIn.sharedInstance.signIn( withPresenting: presentingVc, hint: nil, - additionalScopes: payload["scopes"] as? [String] ?? self.defaultGrantedScopes + additionalScopes: scopes ) { result, error in if let error = error { completion(.failure(error)) @@ -72,6 +83,21 @@ class GoogleProvider { login() return } + guard let grantedScopes = user?.grantedScopes else { + completion(.failure(NSError(domain: "GoogleProvider", code: 0, userInfo: [NSLocalizedDescriptionKey: "grantedScopes is null (?)"]))) + return + } + var sharedScopes = 0; + for scope in scopes { + if (grantedScopes.contains(scope)) { + sharedScopes += 1 + } + } + // scopes do not match. Perhaps the user has requested an additional scope + if (sharedScopes != scopes.count) { + login() + return + } self.createLoginResponse(user: user!, completion: completion) } } else { diff --git a/src/web.ts b/src/web.ts index 7c64069..0407e83 100644 --- a/src/web.ts +++ b/src/web.ts @@ -11,6 +11,7 @@ import type { AuthorizationCodeOptions, FacebookLoginOptions, FacebookLoginResponse, + GoogleLoginOptions, } from "./definitions"; declare const AppleID: any; @@ -368,7 +369,7 @@ export class SocialLoginWeb extends WebPlugin implements SocialLoginPlugin { } } - private async loginWithGoogle(options: any): Promise { + private async loginWithGoogle(options: GoogleLoginOptions): Promise { if (!this.googleClientId) { throw new Error("Google Client ID not set. Call initialize() first."); } @@ -377,7 +378,15 @@ export class SocialLoginWeb extends WebPlugin implements SocialLoginPlugin { if (scopes.length > 0) { // If scopes are provided, directly use the traditional OAuth flow - return Promise.reject("Not yet implemented"); + if (!scopes.includes("https://www.googleapis.com/auth/userinfo.email")) { + scopes.push("https://www.googleapis.com/auth/userinfo.email") + } + if (!scopes.includes("https://www.googleapis.com/auth/userinfo.profile")) { + scopes.push("https://www.googleapis.com/auth/userinfo.profile") + } + if (!scopes.includes('openid')) { + scopes.push('openid') + } } else { scopes = [ "https://www.googleapis.com/auth/userinfo.email", From 185fa0fe538cc5f8c86b9bf982a70ba13810be57 Mon Sep 17 00:00:00 2001 From: WcaleNieWolny Date: Sun, 10 Nov 2024 19:52:01 +0100 Subject: [PATCH 2/3] feat: scopes for Android --- .../social/login/GoogleProvider.java | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/android/src/main/java/ee/forgr/capacitor/social/login/GoogleProvider.java b/android/src/main/java/ee/forgr/capacitor/social/login/GoogleProvider.java index 92e3afa..9969bec 100644 --- a/android/src/main/java/ee/forgr/capacitor/social/login/GoogleProvider.java +++ b/android/src/main/java/ee/forgr/capacitor/social/login/GoogleProvider.java @@ -133,6 +133,15 @@ public void initialize(String clientId, GoogleProviderLoginType mode) { } } + public String arrayFind(String[] array, String search) { + for (int i = 0; i < array.length; i++) { + if (array[i].equals(search)) { + return array[i]; + } + } + return null; + } + @Override public void login(PluginCall call, JSONObject config) { if (this.clientId == null || this.clientId.isEmpty()) { @@ -149,9 +158,29 @@ public void login(PluginCall call, JSONObject config) { for (int i = 0; i < scopesArray.length(); i++) { this.scopes[i] = scopesArray.optString(i); } + + if (arrayFind(this.scopes, "https://www.googleapis.com/auth/userinfo.email") == null) { + String[] newScopes = new String[this.scopes.length + 1]; + System.arraycopy(this.scopes, 0, newScopes, 0, this.scopes.length); + newScopes[this.scopes.length] = "https://www.googleapis.com/auth/userinfo.email"; + this.scopes = newScopes; + } + if (arrayFind(this.scopes, "https://www.googleapis.com/auth/userinfo.profile") == null) { + String[] newScopes = new String[this.scopes.length + 1]; + System.arraycopy(this.scopes, 0, newScopes, 0, this.scopes.length); + newScopes[this.scopes.length] = "https://www.googleapis.com/auth/userinfo.profile"; + this.scopes = newScopes; + } + if (arrayFind(this.scopes, "openid") == null) { + String[] newScopes = new String[this.scopes.length + 1]; + System.arraycopy(this.scopes, 0, newScopes, 0, this.scopes.length); + newScopes[this.scopes.length] = "openid"; + this.scopes = newScopes; + } + } else { // Default scopes if not provided - this.scopes = new String[] { "profile", "email" }; + this.scopes = new String[] { "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email", "openid" }; } GetSignInWithGoogleOption.Builder googleIdOptionBuilder = @@ -638,11 +667,10 @@ private ListenableFuture getAuthorizationResult( ListenableFuture future = CallbackToFutureAdapter.getFuture(completer -> { - List scopes = Arrays.asList( - new Scope(Scopes.EMAIL), - new Scope(Scopes.PROFILE), - new Scope(Scopes.OPEN_ID) - ); + List scopes = new ArrayList<>(this.scopes.length); + for (int i = 0; i < this.scopes.length; i++) { + scopes.add(new Scope(this.scopes[i])); + } AuthorizationRequest.Builder authorizationRequestBuilder = AuthorizationRequest.builder().setRequestedScopes(scopes); // .requestOfflineAccess(this.clientId) From 6c1597a33d95a26070fa853bcfb46c7557ca41a2 Mon Sep 17 00:00:00 2001 From: WcaleNieWolny Date: Sun, 10 Nov 2024 19:52:30 +0100 Subject: [PATCH 3/3] chore: lint --- .../social/login/GoogleProvider.java | 29 +++++++++++++++---- src/web.ts | 16 ++++++---- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/android/src/main/java/ee/forgr/capacitor/social/login/GoogleProvider.java b/android/src/main/java/ee/forgr/capacitor/social/login/GoogleProvider.java index 9969bec..5d36925 100644 --- a/android/src/main/java/ee/forgr/capacitor/social/login/GoogleProvider.java +++ b/android/src/main/java/ee/forgr/capacitor/social/login/GoogleProvider.java @@ -159,16 +159,30 @@ public void login(PluginCall call, JSONObject config) { this.scopes[i] = scopesArray.optString(i); } - if (arrayFind(this.scopes, "https://www.googleapis.com/auth/userinfo.email") == null) { + if ( + arrayFind( + this.scopes, + "https://www.googleapis.com/auth/userinfo.email" + ) == + null + ) { String[] newScopes = new String[this.scopes.length + 1]; System.arraycopy(this.scopes, 0, newScopes, 0, this.scopes.length); - newScopes[this.scopes.length] = "https://www.googleapis.com/auth/userinfo.email"; + newScopes[this.scopes.length] = + "https://www.googleapis.com/auth/userinfo.email"; this.scopes = newScopes; } - if (arrayFind(this.scopes, "https://www.googleapis.com/auth/userinfo.profile") == null) { + if ( + arrayFind( + this.scopes, + "https://www.googleapis.com/auth/userinfo.profile" + ) == + null + ) { String[] newScopes = new String[this.scopes.length + 1]; System.arraycopy(this.scopes, 0, newScopes, 0, this.scopes.length); - newScopes[this.scopes.length] = "https://www.googleapis.com/auth/userinfo.profile"; + newScopes[this.scopes.length] = + "https://www.googleapis.com/auth/userinfo.profile"; this.scopes = newScopes; } if (arrayFind(this.scopes, "openid") == null) { @@ -177,10 +191,13 @@ public void login(PluginCall call, JSONObject config) { newScopes[this.scopes.length] = "openid"; this.scopes = newScopes; } - } else { // Default scopes if not provided - this.scopes = new String[] { "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email", "openid" }; + this.scopes = new String[] { + "https://www.googleapis.com/auth/userinfo.profile", + "https://www.googleapis.com/auth/userinfo.email", + "openid", + }; } GetSignInWithGoogleOption.Builder googleIdOptionBuilder = diff --git a/src/web.ts b/src/web.ts index 0407e83..53c8300 100644 --- a/src/web.ts +++ b/src/web.ts @@ -369,7 +369,9 @@ export class SocialLoginWeb extends WebPlugin implements SocialLoginPlugin { } } - private async loginWithGoogle(options: GoogleLoginOptions): Promise { + private async loginWithGoogle( + options: GoogleLoginOptions, + ): Promise { if (!this.googleClientId) { throw new Error("Google Client ID not set. Call initialize() first."); } @@ -379,13 +381,15 @@ export class SocialLoginWeb extends WebPlugin implements SocialLoginPlugin { if (scopes.length > 0) { // If scopes are provided, directly use the traditional OAuth flow if (!scopes.includes("https://www.googleapis.com/auth/userinfo.email")) { - scopes.push("https://www.googleapis.com/auth/userinfo.email") + scopes.push("https://www.googleapis.com/auth/userinfo.email"); } - if (!scopes.includes("https://www.googleapis.com/auth/userinfo.profile")) { - scopes.push("https://www.googleapis.com/auth/userinfo.profile") + if ( + !scopes.includes("https://www.googleapis.com/auth/userinfo.profile") + ) { + scopes.push("https://www.googleapis.com/auth/userinfo.profile"); } - if (!scopes.includes('openid')) { - scopes.push('openid') + if (!scopes.includes("openid")) { + scopes.push("openid"); } } else { scopes = [