react-native-oauth-essentials 1.0.12 → 1.1.1
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 +384 -362
- package/android/src/main/java/com/oauthessentials/CredentialError.kt +4 -1
- package/android/src/main/java/com/oauthessentials/CredentialFactory.kt +24 -2
- package/android/src/main/java/com/oauthessentials/CustomTabsActivity.kt +14 -1
- package/android/src/main/java/com/oauthessentials/OauthEssentialsModule.kt +445 -187
- package/app.plugin.js +1 -0
- package/ios/AppleIdRetriever.swift +25 -26
- package/ios/CredentialError.swift +3 -0
- package/ios/CredentialFactory.swift +38 -20
- package/ios/CredentialsType.swift +1 -0
- package/ios/GoogleIdRetriever.swift +132 -56
- package/ios/HybridRetriever.swift +12 -18
- package/ios/OauthEssentials.h +0 -3
- package/ios/OauthEssentials.mm +45 -2
- package/ios/OauthEssentialsModule.swift +266 -57
- package/ios/PasskeyRetriever.swift +31 -49
- package/ios/PasswordRetriever.swift +10 -30
- package/ios/RCTPromiseSettler.swift +125 -0
- package/jest/index.js +149 -0
- package/lib/module/NativeOauthEssentials.js +10 -1
- package/lib/module/NativeOauthEssentials.js.map +1 -1
- package/lib/module/errors.js +38 -0
- package/lib/module/errors.js.map +1 -0
- package/lib/module/index.js +432 -58
- package/lib/module/index.js.map +1 -1
- package/lib/module/oauth.js +97 -0
- package/lib/module/oauth.js.map +1 -0
- package/lib/module/platform.js +108 -0
- package/lib/module/platform.js.map +1 -0
- package/lib/module/types.js +46 -1
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/src/NativeOauthEssentials.d.ts +70 -16
- package/lib/typescript/src/NativeOauthEssentials.d.ts.map +1 -1
- package/lib/typescript/src/errors.d.ts +18 -0
- package/lib/typescript/src/errors.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +235 -40
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/oauth.d.ts +32 -0
- package/lib/typescript/src/oauth.d.ts.map +1 -0
- package/lib/typescript/src/platform.d.ts +39 -0
- package/lib/typescript/src/platform.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +190 -2
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +8 -4
- package/plugin/withOauthEssentials.js +291 -0
- package/src/NativeOauthEssentials.ts +91 -17
- package/src/errors.ts +49 -0
- package/src/index.tsx +587 -80
- package/src/oauth.ts +140 -0
- package/src/platform.ts +121 -0
- package/src/types.ts +200 -1
package/app.plugin.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./plugin/withOauthEssentials');
|
|
@@ -5,7 +5,10 @@ public class AppleIdRetriever: RCTPromiseSettler,
|
|
|
5
5
|
ASAuthorizationControllerDelegate,
|
|
6
6
|
ASAuthorizationControllerPresentationContextProviding {
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
private var controller: ASAuthorizationController?
|
|
9
|
+
|
|
10
|
+
init(
|
|
11
|
+
options: NSDictionary,
|
|
9
12
|
resolve: @escaping RCTPromiseResolveBlock,
|
|
10
13
|
reject: @escaping RCTPromiseRejectBlock,
|
|
11
14
|
emitEvent: ((String, NSDictionary) -> Void)?
|
|
@@ -13,49 +16,45 @@ public class AppleIdRetriever: RCTPromiseSettler,
|
|
|
13
16
|
super.init(resolve: resolve, reject: reject, emitEvent: emitEvent)
|
|
14
17
|
|
|
15
18
|
let controller = ASAuthorizationController(authorizationRequests: [
|
|
16
|
-
|
|
19
|
+
AppleIdRetriever.createRequest(options: options),
|
|
17
20
|
])
|
|
18
|
-
|
|
19
21
|
controller.delegate = self
|
|
20
22
|
controller.presentationContextProvider = self
|
|
23
|
+
self.controller = controller
|
|
24
|
+
controller.performRequests()
|
|
25
|
+
}
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
/// Apple only returns the email and full name on the first sign in, and only when requested.
|
|
28
|
+
static func createRequest(options: NSDictionary?) -> ASAuthorizationAppleIDRequest {
|
|
29
|
+
let request = ASAuthorizationAppleIDProvider().createRequest()
|
|
30
|
+
request.requestedScopes = [.fullName, .email]
|
|
31
|
+
if let nonce = options?["nonce"] as? String {
|
|
32
|
+
request.nonce = nonce
|
|
26
33
|
}
|
|
34
|
+
if let state = options?["state"] as? String {
|
|
35
|
+
request.state = state
|
|
36
|
+
}
|
|
37
|
+
return request
|
|
27
38
|
}
|
|
28
39
|
|
|
29
40
|
public func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
|
|
30
|
-
return
|
|
41
|
+
return PresentationContext.anchor()
|
|
31
42
|
}
|
|
32
43
|
|
|
33
44
|
public func authorizationController(
|
|
34
45
|
controller: ASAuthorizationController,
|
|
35
46
|
didCompleteWithAuthorization authorization: ASAuthorization
|
|
36
47
|
) {
|
|
48
|
+
self.controller = nil
|
|
37
49
|
if let appleIdCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
cleanup()
|
|
50
|
+
succeed(CredentialFactory.fromAppleId(appleIdCredential: appleIdCredential))
|
|
51
|
+
} else {
|
|
52
|
+
fail(.invalidResult, "Unexpected credential type")
|
|
42
53
|
}
|
|
43
54
|
}
|
|
44
55
|
|
|
45
56
|
public func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
nsError.code == ASAuthorizationError.canceled.rawValue {
|
|
49
|
-
|
|
50
|
-
let result = CredentialFactory.fromCancelled()
|
|
51
|
-
self.resolve?(result)
|
|
52
|
-
self.emitEvent?("onCredentialSuccess", result as NSDictionary)
|
|
53
|
-
} else {
|
|
54
|
-
self.emitEvent?("onCredentialFailure", [
|
|
55
|
-
"code": CredentialError.invalidResult.rawValue,
|
|
56
|
-
"message": error.localizedDescription
|
|
57
|
-
])
|
|
58
|
-
}
|
|
59
|
-
cleanup()
|
|
57
|
+
self.controller = nil
|
|
58
|
+
finish(authorizationError: error)
|
|
60
59
|
}
|
|
61
60
|
}
|
|
@@ -3,4 +3,7 @@ public enum CredentialError: String {
|
|
|
3
3
|
case invalidResult = "INVALID_RESULT_ERROR"
|
|
4
4
|
case simultaneousCallError = "SIMULTANEOUS_CALL_ERROR"
|
|
5
5
|
case notSupportedError = "NOT_SUPPORTED_ERROR"
|
|
6
|
+
case browserUnavailable = "BROWSER_UNAVAILABLE"
|
|
7
|
+
case noActivity = "NO_ACTIVITY_ERROR"
|
|
8
|
+
case configuration = "CONFIGURATION_ERROR"
|
|
6
9
|
}
|
|
@@ -29,7 +29,8 @@ class CredentialFactory {
|
|
|
29
29
|
"user": appleIdCredential.user,
|
|
30
30
|
"email": appleIdCredential.email ?? "",
|
|
31
31
|
"fullName": fullName,
|
|
32
|
-
"likelyReal": appleIdCredential.realUserStatus == .likelyReal
|
|
32
|
+
"likelyReal": appleIdCredential.realUserStatus == .likelyReal,
|
|
33
|
+
"state": appleIdCredential.state ?? NSNull()
|
|
33
34
|
]
|
|
34
35
|
|
|
35
36
|
let result: [String: Any] = [
|
|
@@ -54,12 +55,12 @@ class CredentialFactory {
|
|
|
54
55
|
@available(iOS 16.0, *)
|
|
55
56
|
public static func fromPasskeyRegistration(credential: ASAuthorizationPlatformPublicKeyCredentialRegistration) -> [String: Any] {
|
|
56
57
|
let responseJson: [String: Any] = [
|
|
57
|
-
"id":
|
|
58
|
-
"rawId":
|
|
58
|
+
"id": credential.credentialID.base64URLEncodedString(),
|
|
59
|
+
"rawId": credential.credentialID.base64URLEncodedString(),
|
|
59
60
|
"type": "public-key",
|
|
60
61
|
"response": [
|
|
61
|
-
"clientDataJSON":
|
|
62
|
-
"attestationObject":
|
|
62
|
+
"clientDataJSON": credential.rawClientDataJSON.base64URLEncodedString(),
|
|
63
|
+
"attestationObject": (credential.rawAttestationObject ?? Data()).base64URLEncodedString()
|
|
63
64
|
]
|
|
64
65
|
]
|
|
65
66
|
|
|
@@ -80,14 +81,14 @@ class CredentialFactory {
|
|
|
80
81
|
@available(iOS 16.0, *)
|
|
81
82
|
public static func fromPasskeyAssertion(credential: ASAuthorizationPlatformPublicKeyCredentialAssertion) -> [String: Any] {
|
|
82
83
|
let responseJson: [String: Any] = [
|
|
83
|
-
"id":
|
|
84
|
-
"rawId":
|
|
84
|
+
"id": credential.credentialID.base64URLEncodedString(),
|
|
85
|
+
"rawId": credential.credentialID.base64URLEncodedString(),
|
|
85
86
|
"type": "public-key",
|
|
86
87
|
"response": [
|
|
87
|
-
"clientDataJSON":
|
|
88
|
-
"authenticatorData":
|
|
89
|
-
"signature":
|
|
90
|
-
"userHandle":
|
|
88
|
+
"clientDataJSON": credential.rawClientDataJSON.base64URLEncodedString(),
|
|
89
|
+
"authenticatorData": credential.rawAuthenticatorData.base64URLEncodedString(),
|
|
90
|
+
"signature": credential.signature.base64URLEncodedString(),
|
|
91
|
+
"userHandle": credential.userID.base64URLEncodedString()
|
|
91
92
|
]
|
|
92
93
|
]
|
|
93
94
|
|
|
@@ -105,14 +106,7 @@ class CredentialFactory {
|
|
|
105
106
|
]
|
|
106
107
|
}
|
|
107
108
|
|
|
108
|
-
|
|
109
|
-
return data.base64EncodedString()
|
|
110
|
-
.replacingOccurrences(of: "+", with: "-")
|
|
111
|
-
.replacingOccurrences(of: "/", with: "_")
|
|
112
|
-
.replacingOccurrences(of: "=", with: "")
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
public static func fromGoogleId(user: GIDGoogleUser) -> [String: Any] {
|
|
109
|
+
public static func fromGoogleId(user: GIDGoogleUser, serverAuthCode: String?) -> [String: Any] {
|
|
116
110
|
let profile = user.profile
|
|
117
111
|
let data: [String: Any] = [
|
|
118
112
|
"idToken": user.idToken?.tokenString ?? "",
|
|
@@ -121,7 +115,10 @@ class CredentialFactory {
|
|
|
121
115
|
"givenName": profile?.givenName ?? "",
|
|
122
116
|
"familyName": profile?.familyName ?? "",
|
|
123
117
|
"profilePictureUri": profile?.imageURL(withDimension: 200)?.absoluteString ?? "",
|
|
124
|
-
"email": profile?.email ?? ""
|
|
118
|
+
"email": profile?.email ?? "",
|
|
119
|
+
"accessToken": user.accessToken.tokenString,
|
|
120
|
+
"serverAuthCode": serverAuthCode ?? NSNull(),
|
|
121
|
+
"grantedScopes": user.grantedScopes ?? []
|
|
125
122
|
]
|
|
126
123
|
|
|
127
124
|
let result: [String: Any] = [
|
|
@@ -131,3 +128,24 @@ class CredentialFactory {
|
|
|
131
128
|
return result
|
|
132
129
|
}
|
|
133
130
|
}
|
|
131
|
+
|
|
132
|
+
/// base64url without padding (RFC 4648 §5), used by WebAuthn and PKCE.
|
|
133
|
+
extension Data {
|
|
134
|
+
func base64URLEncodedString() -> String {
|
|
135
|
+
return base64EncodedString()
|
|
136
|
+
.replacingOccurrences(of: "+", with: "-")
|
|
137
|
+
.replacingOccurrences(of: "/", with: "_")
|
|
138
|
+
.replacingOccurrences(of: "=", with: "")
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
init?(base64URLEncoded string: String) {
|
|
142
|
+
var base64 = string
|
|
143
|
+
.replacingOccurrences(of: "-", with: "+")
|
|
144
|
+
.replacingOccurrences(of: "_", with: "/")
|
|
145
|
+
let remainder = base64.count % 4
|
|
146
|
+
if remainder > 0 {
|
|
147
|
+
base64.append(String(repeating: "=", count: 4 - remainder))
|
|
148
|
+
}
|
|
149
|
+
self.init(base64Encoded: base64)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -1,83 +1,159 @@
|
|
|
1
1
|
import GoogleSignIn
|
|
2
2
|
import React
|
|
3
3
|
|
|
4
|
+
/// Must be created on the main thread: GIDSignIn presents ASWebAuthenticationSession synchronously.
|
|
4
5
|
public class GoogleIdRetriever: RCTPromiseSettler {
|
|
5
6
|
|
|
6
7
|
init(
|
|
7
8
|
clientId: String,
|
|
8
9
|
options: NSDictionary,
|
|
10
|
+
silent: Bool,
|
|
9
11
|
resolve: @escaping RCTPromiseResolveBlock,
|
|
10
12
|
reject: @escaping RCTPromiseRejectBlock,
|
|
11
13
|
emitEvent: ((String, NSDictionary) -> Void)?
|
|
12
14
|
) {
|
|
13
15
|
super.init(resolve: resolve, reject: reject, emitEvent: emitEvent)
|
|
14
|
-
performGoogleSignIn(clientId:clientId, options: options)
|
|
15
|
-
}
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
rootVC = UIApplication.shared.keyWindow?.rootViewController
|
|
28
|
-
}
|
|
17
|
+
// GIDSignIn raises an NSException (crashing the app) for a missing client id or, in the
|
|
18
|
+
// interactive flow only, URL scheme, so they are validated here and reported as
|
|
19
|
+
// CONFIGURATION_ERROR instead.
|
|
20
|
+
guard let clientId = Self.resolveClientId(clientId) else {
|
|
21
|
+
fail(
|
|
22
|
+
.configuration,
|
|
23
|
+
"Missing Google iOS client id: pass { iosClientId }, set GIDClientID in Info.plist (the Expo "
|
|
24
|
+
+ "plugin does it) or add the Firebase GoogleService-Info.plist to the app target"
|
|
25
|
+
)
|
|
26
|
+
return
|
|
29
27
|
}
|
|
30
|
-
|
|
31
|
-
guard
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
28
|
+
let scheme = Self.urlScheme(for: clientId)
|
|
29
|
+
guard silent || Self.registeredUrlSchemes().contains(scheme) else {
|
|
30
|
+
fail(
|
|
31
|
+
.configuration,
|
|
32
|
+
"Info.plist is missing the URL scheme \"\(scheme)\" required by Google Sign-In. Add it to "
|
|
33
|
+
+ "CFBundleURLTypes > CFBundleURLSchemes (it is REVERSED_CLIENT_ID in GoogleService-Info.plist; "
|
|
34
|
+
+ "the Expo config plugin adds it). "
|
|
35
|
+
+ "\(clientId) must be the iOS client id; web client ids go in serverClientId."
|
|
36
36
|
)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
GIDSignIn.sharedInstance.configuration = GIDConfiguration(
|
|
41
|
+
clientID: clientId,
|
|
42
|
+
serverClientID: options["serverClientId"] as? String ?? Self.infoPlistString("GIDServerClientID"),
|
|
43
|
+
hostedDomain: options["hostedDomain"] as? String,
|
|
44
|
+
openIDRealm: nil
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if silent {
|
|
48
|
+
restorePreviousSignIn()
|
|
49
|
+
} else {
|
|
50
|
+
performGoogleSignIn(options: options)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
41
53
|
|
|
42
|
-
|
|
54
|
+
private func performGoogleSignIn(options: NSDictionary) {
|
|
55
|
+
whenBrowserCanOpen("Google Sign-In") { presenter in
|
|
56
|
+
GIDSignIn.sharedInstance.signIn(
|
|
57
|
+
withPresenting: presenter,
|
|
58
|
+
hint: options["hint"] as? String,
|
|
59
|
+
additionalScopes: options["scopes"] as? [String],
|
|
60
|
+
nonce: options["nonce"] as? String
|
|
61
|
+
) { result, error in
|
|
62
|
+
self.handle(user: result?.user, serverAuthCode: result?.serverAuthCode, error: error)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private func restorePreviousSignIn() {
|
|
68
|
+
guard GIDSignIn.sharedInstance.hasPreviousSignIn() else {
|
|
69
|
+
fail(.noSignInAvailable, "No previous Google sign in to restore")
|
|
43
70
|
return
|
|
44
71
|
}
|
|
45
72
|
|
|
46
|
-
GIDSignIn.sharedInstance.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if nsError.code == GIDSignInError.canceled.rawValue {
|
|
52
|
-
let result = CredentialFactory.fromCancelled()
|
|
53
|
-
self.resolve?(result)
|
|
54
|
-
self.emitEvent?("onCredentialSuccess", result as NSDictionary)
|
|
55
|
-
} else {
|
|
56
|
-
self.reject?(CredentialError.invalidResult.rawValue, error.localizedDescription, error)
|
|
57
|
-
self.emitEvent?("onCredentialFailure", [
|
|
58
|
-
"code": CredentialError.invalidResult.rawValue,
|
|
59
|
-
"message": error.localizedDescription
|
|
60
|
-
])
|
|
61
|
-
}
|
|
62
|
-
self.cleanup()
|
|
73
|
+
GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
|
|
74
|
+
if let error = error as NSError?,
|
|
75
|
+
error.domain == kGIDSignInErrorDomain,
|
|
76
|
+
error.code == GIDSignInError.hasNoAuthInKeychain.rawValue {
|
|
77
|
+
self.fail(.noSignInAvailable, "No previous Google sign in to restore", error)
|
|
63
78
|
return
|
|
64
79
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
self.handle(user: user, serverAuthCode: nil, error: error)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private func handle(user: GIDGoogleUser?, serverAuthCode: String?, error: Error?) {
|
|
85
|
+
if let error = error {
|
|
86
|
+
let nsError = error as NSError
|
|
87
|
+
if nsError.domain == kGIDSignInErrorDomain,
|
|
88
|
+
nsError.code == GIDSignInError.canceled.rawValue {
|
|
89
|
+
succeed(CredentialFactory.fromCancelled())
|
|
90
|
+
} else if Self.isBrowserUnavailable(nsError) {
|
|
91
|
+
fail(.browserUnavailable, error.localizedDescription, error)
|
|
92
|
+
} else {
|
|
93
|
+
fail(.invalidResult, error.localizedDescription, error)
|
|
75
94
|
}
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
guard let user = user else {
|
|
99
|
+
fail(.invalidResult, "No user returned")
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
succeed(CredentialFactory.fromGoogleId(user: user, serverAuthCode: serverAuthCode))
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/// The client id passed from JS or, when it is empty, GIDClientID from Info.plist, then CLIENT_ID
|
|
107
|
+
/// from the Firebase GoogleService-Info.plist.
|
|
108
|
+
private static func resolveClientId(_ clientId: String) -> String? {
|
|
109
|
+
let trimmed = clientId.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
110
|
+
if !trimmed.isEmpty {
|
|
111
|
+
return trimmed
|
|
112
|
+
}
|
|
113
|
+
return infoPlistString("GIDClientID") ?? googleServiceInfoString("CLIENT_ID")
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private static func infoPlistString(_ key: String) -> String? {
|
|
117
|
+
return nonEmpty(Bundle.main.object(forInfoDictionaryKey: key) as? String)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private static func googleServiceInfoString(_ key: String) -> String? {
|
|
121
|
+
guard let path = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist"),
|
|
122
|
+
let values = NSDictionary(contentsOfFile: path) else {
|
|
123
|
+
return nil
|
|
124
|
+
}
|
|
125
|
+
return nonEmpty(values[key] as? String)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private static func nonEmpty(_ value: String?) -> String? {
|
|
129
|
+
guard let value = value?.trimmingCharacters(in: .whitespacesAndNewlines), !value.isEmpty else {
|
|
130
|
+
return nil
|
|
131
|
+
}
|
|
132
|
+
return value
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/// Same rule as GIDSignInCallbackSchemes: the reversed client id, lowercased.
|
|
136
|
+
private static func urlScheme(for clientId: String) -> String {
|
|
137
|
+
return clientId.split(separator: ".").reversed().joined(separator: ".").lowercased()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private static func registeredUrlSchemes() -> Set<String> {
|
|
141
|
+
let urlTypes = Bundle.main.object(forInfoDictionaryKey: "CFBundleURLTypes") as? [[String: Any]] ?? []
|
|
142
|
+
let schemes = urlTypes.flatMap { $0["CFBundleURLSchemes"] as? [String] ?? [] }
|
|
143
|
+
return Set(schemes.map { $0.lowercased() })
|
|
144
|
+
}
|
|
76
145
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
146
|
+
/// Fallback for the causes `whenBrowserCanOpen` does not catch. GIDSignIn reports a failed
|
|
147
|
+
/// ASWebAuthenticationSession start as an unknown error that only keeps AppAuth's (not localized)
|
|
148
|
+
/// "Unable to open Safari." message (OIDErrorCodeSafariOpenError = -9).
|
|
149
|
+
private static func isBrowserUnavailable(_ error: NSError) -> Bool {
|
|
150
|
+
if let underlying = error.userInfo[NSUnderlyingErrorKey] as? NSError,
|
|
151
|
+
underlying.domain == "org.openid.appauth.general",
|
|
152
|
+
underlying.code == -9 {
|
|
153
|
+
return true
|
|
81
154
|
}
|
|
155
|
+
return error.domain == kGIDSignInErrorDomain
|
|
156
|
+
&& error.code == GIDSignInError.unknown.rawValue
|
|
157
|
+
&& error.localizedDescription.contains("Unable to open Safari")
|
|
82
158
|
}
|
|
83
159
|
}
|
|
@@ -5,6 +5,8 @@ public class HybridRetriever: RCTPromiseSettler,
|
|
|
5
5
|
ASAuthorizationControllerDelegate,
|
|
6
6
|
ASAuthorizationControllerPresentationContextProviding {
|
|
7
7
|
|
|
8
|
+
private var controller: ASAuthorizationController?
|
|
9
|
+
|
|
8
10
|
init(
|
|
9
11
|
clientId: String,
|
|
10
12
|
options: NSDictionary,
|
|
@@ -15,12 +17,13 @@ ASAuthorizationControllerPresentationContextProviding {
|
|
|
15
17
|
super.init(resolve: resolve, reject: reject, emitEvent: emitEvent)
|
|
16
18
|
|
|
17
19
|
let controller = ASAuthorizationController(authorizationRequests: [
|
|
18
|
-
|
|
20
|
+
AppleIdRetriever.createRequest(options: options),
|
|
19
21
|
ASAuthorizationPasswordProvider().createRequest()
|
|
20
22
|
])
|
|
21
23
|
|
|
22
24
|
controller.delegate = self
|
|
23
25
|
controller.presentationContextProvider = self
|
|
26
|
+
self.controller = controller
|
|
24
27
|
|
|
25
28
|
if #available(iOS 16.0, *) {
|
|
26
29
|
controller.performRequests(options: .preferImmediatelyAvailableCredentials)
|
|
@@ -30,35 +33,26 @@ ASAuthorizationControllerPresentationContextProviding {
|
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
public func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
|
|
33
|
-
return
|
|
36
|
+
return PresentationContext.anchor()
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
public func authorizationController(
|
|
37
40
|
controller: ASAuthorizationController,
|
|
38
41
|
didCompleteWithAuthorization authorization: ASAuthorization
|
|
39
42
|
) {
|
|
43
|
+
self.controller = nil
|
|
40
44
|
if let appleIdCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
|
|
41
|
-
|
|
42
|
-
self.resolve?(result)
|
|
43
|
-
self.emitEvent?("onCredentialSuccess", result as NSDictionary)
|
|
45
|
+
succeed(CredentialFactory.fromAppleId(appleIdCredential: appleIdCredential))
|
|
44
46
|
}
|
|
45
47
|
else if let credential = authorization.credential as? ASPasswordCredential {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
succeed(CredentialFactory.fromPassword(credential: credential))
|
|
49
|
+
} else {
|
|
50
|
+
fail(.invalidResult, "Unexpected credential type")
|
|
49
51
|
}
|
|
50
|
-
cleanup()
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
public func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
|
|
54
|
-
self.
|
|
55
|
-
|
|
56
|
-
error.localizedDescription,
|
|
57
|
-
error
|
|
58
|
-
)
|
|
59
|
-
self.emitEvent?("onCredentialFailure", [
|
|
60
|
-
"code": CredentialError.invalidResult.rawValue,
|
|
61
|
-
"message": error.localizedDescription
|
|
62
|
-
])
|
|
55
|
+
self.controller = nil
|
|
56
|
+
finish(authorizationError: error)
|
|
63
57
|
}
|
|
64
58
|
}
|
package/ios/OauthEssentials.h
CHANGED
package/ios/OauthEssentials.mm
CHANGED
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
initOnCredentialSuccess:^(NSDictionary * _Nonnull value) {
|
|
18
18
|
[weakSelf emitOnCredentialSuccess:value];
|
|
19
19
|
} onCredentialFailure:^(NSDictionary * _Nonnull value) {
|
|
20
|
-
[weakSelf
|
|
20
|
+
[weakSelf emitOnCredentialFailure:value];
|
|
21
|
+
} onAppleIdCredentialRevoked:^(NSDictionary * _Nonnull value) {
|
|
22
|
+
[weakSelf emitOnAppleIdCredentialRevoked:value];
|
|
21
23
|
}];
|
|
22
24
|
}
|
|
23
25
|
return self;
|
|
@@ -52,6 +54,23 @@
|
|
|
52
54
|
rejecter:reject];
|
|
53
55
|
}
|
|
54
56
|
|
|
57
|
+
- (void)googleSilentSignIn:(NSString *)clientId
|
|
58
|
+
options:(NSDictionary *)options
|
|
59
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
60
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
61
|
+
{
|
|
62
|
+
[_sModule googleSilentSignIn:clientId
|
|
63
|
+
options:options
|
|
64
|
+
resolver:resolve
|
|
65
|
+
rejecter:reject];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
- (void)signOut:(RCTPromiseResolveBlock)resolve
|
|
69
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
70
|
+
{
|
|
71
|
+
[_sModule signOut:resolve rejecter:reject];
|
|
72
|
+
}
|
|
73
|
+
|
|
55
74
|
- (void)passwordSignIn:(NSString *)username
|
|
56
75
|
password:(NSString *)password
|
|
57
76
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
@@ -80,9 +99,16 @@
|
|
|
80
99
|
}
|
|
81
100
|
|
|
82
101
|
- (void)appleSignIn:(NSString *)webAndroidUrl
|
|
102
|
+
options:(NSDictionary *)options
|
|
83
103
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
84
104
|
reject:(RCTPromiseRejectBlock)reject {
|
|
85
|
-
[_sModule appleSignIn:webAndroidUrl resolver:resolve rejecter:reject];
|
|
105
|
+
[_sModule appleSignIn:webAndroidUrl options:options resolver:resolve rejecter:reject];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
- (void)getAppleIdCredentialState:(NSString *)userId
|
|
109
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
110
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
111
|
+
[_sModule getAppleIdCredentialState:userId resolver:resolve rejecter:reject];
|
|
86
112
|
}
|
|
87
113
|
|
|
88
114
|
- (void)createPassKey:(NSDictionary *)options
|
|
@@ -97,6 +123,23 @@
|
|
|
97
123
|
[_sModule getPassKey:options resolver:resolve rejecter:reject];
|
|
98
124
|
}
|
|
99
125
|
|
|
126
|
+
- (void)cancelPassKeyAutoFill:(RCTPromiseResolveBlock)resolve
|
|
127
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
128
|
+
[_sModule cancelPassKeyAutoFill:resolve rejecter:reject];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
- (void)generatePkce:(RCTPromiseResolveBlock)resolve
|
|
132
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
133
|
+
[_sModule generatePkce:resolve rejecter:reject];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
- (void)openAuthSession:(NSString *)url
|
|
137
|
+
options:(NSDictionary *)options
|
|
138
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
139
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
140
|
+
[_sModule openAuthSession:url options:options resolver:resolve rejecter:reject];
|
|
141
|
+
}
|
|
142
|
+
|
|
100
143
|
- (facebook::react::ModuleConstants<JS::NativeOauthEssentials::Constants>) constantsToExport {
|
|
101
144
|
return (facebook::react::ModuleConstants<JS::NativeOauthEssentials::Constants>)[_sModule getConstants];
|
|
102
145
|
}
|