react-native-nitro-auth 0.5.9 → 0.5.10

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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.10 - 2026-04-27
4
+
5
+ ### Fixed
6
+
7
+ - Fixed iOS Microsoft sign-in so `ASWebAuthenticationSession` is retained until callback or cancellation and duplicate sessions fail with `operation_in_progress`.
8
+ - Fixed the example app header so it displays the current package version.
9
+
10
+ ### Verified
11
+
12
+ - `bun run check:ci`
13
+ - `bunx expo install --check --cwd apps/example`
14
+ - `bunx expo-doctor@latest apps/example`
15
+ - `bun run example:prebuild`
16
+ - `bun run publish-package:dry-run`
17
+
3
18
  ## 0.5.9 - 2026-04-24
4
19
 
5
20
  ### Added
package/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # react-native-nitro-auth
2
2
 
3
+ ![npm](https://img.shields.io/badge/npm-v0.5.10-f97316?style=flat-square)
4
+ ![license](https://img.shields.io/badge/license-MIT-007ec6?style=flat-square)
5
+ ![react-native](https://img.shields.io/badge/react--native-%3E%3D0.75-61dafb?style=flat-square)
6
+ ![nitro-modules](https://img.shields.io/badge/nitro--modules-%3E%3D0.35.0-black?style=flat-square)
7
+
3
8
  Fast React Native authentication for Google Sign-In, Apple Sign-In, and Microsoft Entra ID, built on Nitro Modules and JSI.
4
9
 
5
10
  `react-native-nitro-auth` gives Expo and React Native apps one typed API for native social login, web OAuth, token refresh, incremental scopes, and auth state listeners without owning your app's long-term token storage.
@@ -10,6 +10,7 @@ public class AuthAdapter: NSObject {
10
10
  private static var inMemoryMicrosoftRefreshToken: String?
11
11
  private static var inMemoryMicrosoftScopes: [String] = defaultMicrosoftScopes
12
12
  private static var inMemoryGoogleServerAuthCode: String?
13
+ private static var activeMicrosoftWebAuthSession: ASWebAuthenticationSession?
13
14
  private static let tokenStoreLock = NSLock()
14
15
 
15
16
  @objc
@@ -135,22 +136,32 @@ public class AuthAdapter: NSObject {
135
136
  let callbackScheme = "msauth.\(bundleId)"
136
137
 
137
138
  DispatchQueue.main.async {
139
+ guard self.activeMicrosoftWebAuthSession == nil else {
140
+ completion(nil, "operation_in_progress")
141
+ return
142
+ }
143
+
144
+ let completeAndClearSession = { (data: NSDictionary?, error: String?) in
145
+ self.activeMicrosoftWebAuthSession = nil
146
+ completion(data, error)
147
+ }
148
+
138
149
  let session = ASWebAuthenticationSession(url: authUrl, callbackURLScheme: callbackScheme) { callbackURL, error in
139
150
  if let error = error {
140
151
  let nsError = error as NSError
141
152
  if nsError.code == ASWebAuthenticationSessionError.canceledLogin.rawValue {
142
- completion(nil, "cancelled")
153
+ completeAndClearSession(nil, "cancelled")
143
154
  } else if nsError.domain.lowercased().contains("network") || nsError.code == NSURLErrorNotConnectedToInternet {
144
- completion(nil, "network_error")
155
+ completeAndClearSession(nil, "network_error")
145
156
  } else {
146
- completion(nil, "unknown")
157
+ completeAndClearSession(nil, "unknown")
147
158
  }
148
159
  return
149
160
  }
150
161
 
151
162
  guard let callbackURL = callbackURL,
152
163
  let components = URLComponents(url: callbackURL, resolvingAgainstBaseURL: false) else {
153
- completion(nil, "unknown")
164
+ completeAndClearSession(nil, "unknown")
154
165
  return
155
166
  }
156
167
 
@@ -163,20 +174,21 @@ public class AuthAdapter: NSObject {
163
174
  // OAuth error codes are already structured (e.g. "access_denied").
164
175
  // Map well-known ones; fall back to "unknown".
165
176
  let mapped = mapOAuthError(errorCode)
166
- completion(nil, mapped)
177
+ completeAndClearSession(nil, mapped)
167
178
  return
168
179
  }
169
180
 
170
181
  guard let returnedState = params["state"], returnedState == state else {
171
- completion(nil, "invalid_state")
182
+ completeAndClearSession(nil, "invalid_state")
172
183
  return
173
184
  }
174
185
 
175
186
  guard let code = params["code"] else {
176
- completion(nil, "unknown")
187
+ completeAndClearSession(nil, "unknown")
177
188
  return
178
189
  }
179
190
 
191
+ self.activeMicrosoftWebAuthSession = nil
180
192
  exchangeCodeForTokens(
181
193
  code: code,
182
194
  codeVerifier: codeVerifier,
@@ -191,14 +203,17 @@ public class AuthAdapter: NSObject {
191
203
  }
192
204
 
193
205
  guard let window = activeWindow() else {
194
- completion(nil, "no_window")
206
+ completeAndClearSession(nil, "no_window")
195
207
  return
196
208
  }
197
209
  let contextProvider = WebAuthContextProvider(anchor: window)
198
210
  session.presentationContextProvider = contextProvider
199
211
  objc_setAssociatedObject(session, &contextProviderHandle, contextProvider, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
200
212
  session.prefersEphemeralWebBrowserSession = false
201
- session.start()
213
+ self.activeMicrosoftWebAuthSession = session
214
+ if !session.start() {
215
+ completeAndClearSession(nil, "unknown")
216
+ }
202
217
  }
203
218
  }
204
219
 
@@ -685,6 +700,10 @@ public class AuthAdapter: NSObject {
685
700
  @objc
686
701
  public static func logout() {
687
702
  GIDSignIn.sharedInstance.signOut()
703
+ DispatchQueue.main.async {
704
+ self.activeMicrosoftWebAuthSession?.cancel()
705
+ self.activeMicrosoftWebAuthSession = nil
706
+ }
688
707
  tokenStoreLock.lock()
689
708
  inMemoryMicrosoftRefreshToken = nil
690
709
  inMemoryMicrosoftScopes = defaultMicrosoftScopes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nitro-auth",
3
- "version": "0.5.9",
3
+ "version": "0.5.10",
4
4
  "description": "High-performance authentication library for React Native with Google Sign-In, Apple Sign-In, and Microsoft Sign-In support, powered by Nitro Modules (JSI)",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",