react-native-nitro-auth 0.5.12 → 0.6.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.
Files changed (93) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +132 -32
  3. package/android/build.gradle +4 -4
  4. package/android/gradle.properties +2 -2
  5. package/android/src/main/cpp/PlatformAuth+Android.cpp +87 -4
  6. package/android/src/main/java/com/auth/AuthAdapter.kt +85 -48
  7. package/android/src/main/java/com/auth/GoogleSignInActivity.kt +12 -2
  8. package/cpp/HybridAuth.cpp +168 -18
  9. package/cpp/HybridAuth.hpp +7 -0
  10. package/cpp/PlatformAuth.hpp +1 -0
  11. package/ios/AuthAdapter.swift +101 -24
  12. package/ios/PlatformAuth+iOS.mm +37 -1
  13. package/lib/commonjs/Auth.web.js +74 -21
  14. package/lib/commonjs/Auth.web.js.map +1 -1
  15. package/lib/commonjs/create-auth-service.js +10 -0
  16. package/lib/commonjs/create-auth-service.js.map +1 -1
  17. package/lib/commonjs/index.js +12 -0
  18. package/lib/commonjs/index.js.map +1 -1
  19. package/lib/commonjs/index.web.js +12 -0
  20. package/lib/commonjs/index.web.js.map +1 -1
  21. package/lib/commonjs/provider-options.js +6 -0
  22. package/lib/commonjs/provider-options.js.map +1 -0
  23. package/lib/commonjs/service.js.map +1 -1
  24. package/lib/commonjs/service.web.js.map +1 -1
  25. package/lib/commonjs/use-auth.js +21 -1
  26. package/lib/commonjs/use-auth.js.map +1 -1
  27. package/lib/module/Auth.web.js +74 -21
  28. package/lib/module/Auth.web.js.map +1 -1
  29. package/lib/module/create-auth-service.js +10 -0
  30. package/lib/module/create-auth-service.js.map +1 -1
  31. package/lib/module/global.d.js.map +1 -1
  32. package/lib/module/index.js +1 -0
  33. package/lib/module/index.js.map +1 -1
  34. package/lib/module/index.web.js +1 -0
  35. package/lib/module/index.web.js.map +1 -1
  36. package/lib/module/provider-options.js +4 -0
  37. package/lib/module/provider-options.js.map +1 -0
  38. package/lib/module/service.js.map +1 -1
  39. package/lib/module/service.web.js.map +1 -1
  40. package/lib/module/use-auth.js +21 -1
  41. package/lib/module/use-auth.js.map +1 -1
  42. package/lib/typescript/commonjs/Auth.nitro.d.ts +11 -0
  43. package/lib/typescript/commonjs/Auth.nitro.d.ts.map +1 -1
  44. package/lib/typescript/commonjs/Auth.web.d.ts +4 -0
  45. package/lib/typescript/commonjs/Auth.web.d.ts.map +1 -1
  46. package/lib/typescript/commonjs/create-auth-service.d.ts +2 -1
  47. package/lib/typescript/commonjs/create-auth-service.d.ts.map +1 -1
  48. package/lib/typescript/commonjs/index.d.ts +1 -0
  49. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  50. package/lib/typescript/commonjs/index.web.d.ts +1 -0
  51. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  52. package/lib/typescript/commonjs/provider-options.d.ts +23 -0
  53. package/lib/typescript/commonjs/provider-options.d.ts.map +1 -0
  54. package/lib/typescript/commonjs/service.d.ts +2 -2
  55. package/lib/typescript/commonjs/service.d.ts.map +1 -1
  56. package/lib/typescript/commonjs/service.web.d.ts +2 -2
  57. package/lib/typescript/commonjs/service.web.d.ts.map +1 -1
  58. package/lib/typescript/commonjs/use-auth.d.ts +4 -2
  59. package/lib/typescript/commonjs/use-auth.d.ts.map +1 -1
  60. package/lib/typescript/module/Auth.nitro.d.ts +11 -0
  61. package/lib/typescript/module/Auth.nitro.d.ts.map +1 -1
  62. package/lib/typescript/module/Auth.web.d.ts +4 -0
  63. package/lib/typescript/module/Auth.web.d.ts.map +1 -1
  64. package/lib/typescript/module/create-auth-service.d.ts +2 -1
  65. package/lib/typescript/module/create-auth-service.d.ts.map +1 -1
  66. package/lib/typescript/module/index.d.ts +1 -0
  67. package/lib/typescript/module/index.d.ts.map +1 -1
  68. package/lib/typescript/module/index.web.d.ts +1 -0
  69. package/lib/typescript/module/index.web.d.ts.map +1 -1
  70. package/lib/typescript/module/provider-options.d.ts +23 -0
  71. package/lib/typescript/module/provider-options.d.ts.map +1 -0
  72. package/lib/typescript/module/service.d.ts +2 -2
  73. package/lib/typescript/module/service.d.ts.map +1 -1
  74. package/lib/typescript/module/service.web.d.ts +2 -2
  75. package/lib/typescript/module/service.web.d.ts.map +1 -1
  76. package/lib/typescript/module/use-auth.d.ts +4 -2
  77. package/lib/typescript/module/use-auth.d.ts.map +1 -1
  78. package/nitrogen/generated/shared/c++/AuthUser.hpp +17 -1
  79. package/nitrogen/generated/shared/c++/HybridAuthSpec.cpp +1 -0
  80. package/nitrogen/generated/shared/c++/HybridAuthSpec.hpp +1 -0
  81. package/nitrogen/generated/shared/c++/LoginOptions.hpp +25 -1
  82. package/package.json +7 -7
  83. package/react-native-nitro-auth.podspec +1 -1
  84. package/src/Auth.nitro.ts +11 -0
  85. package/src/Auth.web.ts +99 -16
  86. package/src/create-auth-service.ts +19 -9
  87. package/src/global.d.ts +2 -1
  88. package/src/index.ts +1 -0
  89. package/src/index.web.ts +1 -0
  90. package/src/provider-options.ts +62 -0
  91. package/src/service.ts +2 -1
  92. package/src/service.web.ts +2 -2
  93. package/src/use-auth.ts +22 -8
@@ -1,10 +1,4 @@
1
1
  @file:Suppress("DEPRECATION")
2
- // The legacy com.google.android.gms.auth.api.signin.* API is used intentionally for:
3
- // • getLastSignedInAccount – persists session across app restarts via GMS store; no drop-in replacement
4
- // • silentSignIn – AuthorizationClient.authorize() still requires an Activity for interactive fallback
5
- // • revokeAccess – no equivalent in Credential Manager or Identity.getAuthorizationClient()
6
- // All modern entry-points use Credential Manager (One-Tap) unless the caller explicitly needs
7
- // Android's account chooser semantics, which still require the legacy Google Sign-In flow.
8
2
 
9
3
  package com.auth
10
4
 
@@ -33,6 +27,7 @@ import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
33
27
  import kotlinx.coroutines.CoroutineScope
34
28
  import kotlinx.coroutines.Dispatchers
35
29
  import kotlinx.coroutines.SupervisorJob
30
+ import kotlinx.coroutines.CancellationException
36
31
  import kotlinx.coroutines.cancel
37
32
  import kotlinx.coroutines.launch
38
33
  import kotlinx.coroutines.withContext
@@ -71,6 +66,8 @@ object AuthAdapter {
71
66
  private var pendingMicrosoftB2cDomain: String? = null
72
67
  @Volatile
73
68
  private var microsoftAuthInProgress = false
69
+ @Volatile
70
+ private var hasLegacyGoogleSession = false
74
71
 
75
72
  @Volatile
76
73
  private var inMemoryMicrosoftRefreshToken: String? = null
@@ -78,7 +75,6 @@ object AuthAdapter {
78
75
  private var inMemoryMicrosoftScopes: List<String> =
79
76
  defaultMicrosoftScopes
80
77
 
81
- // Module-scoped coroutine scope — cancelled on module invalidation via dispose().
82
78
  private var moduleScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
83
79
 
84
80
  @JvmStatic
@@ -96,6 +92,9 @@ object AuthAdapter {
96
92
  idToken: String?,
97
93
  accessToken: String?,
98
94
  serverAuthCode: String?,
95
+ userId: String?,
96
+ phoneNumber: String?,
97
+ hostedDomain: String?,
99
98
  scopes: Array<String>?,
100
99
  expirationTime: Long?
101
100
  )
@@ -131,9 +130,6 @@ object AuthAdapter {
131
130
  }
132
131
 
133
132
  try {
134
- // The native library is already loaded by NitroAuthOnLoad.initializeNative()
135
- // before this method is called from NitroAuthModule. We only need to wire
136
- // the Android context so that native methods can call back into the JVM.
137
133
  nativeInitialize(applicationContext)
138
134
  isInitialized = true
139
135
  } catch (e: Exception) {
@@ -159,10 +155,11 @@ object AuthAdapter {
159
155
 
160
156
  fun onSignInSuccess(account: GoogleSignInAccount, scopes: List<String>, origin: String = "login") {
161
157
  appContext ?: return
158
+ hasLegacyGoogleSession = true
162
159
  val expirationTime = getJwtExpirationTimeMs(account.idToken)
163
160
  nativeOnLoginSuccess(origin, "google", account.email, account.displayName,
164
161
  account.photoUrl?.toString(), account.idToken, null, account.serverAuthCode,
165
- scopes.toTypedArray(), expirationTime)
162
+ account.id, null, null, scopes.toTypedArray(), expirationTime)
166
163
  }
167
164
 
168
165
  fun onSignInError(errorCode: Int, message: String?, origin: String = "login") {
@@ -182,11 +179,17 @@ object AuthAdapter {
182
179
  googleClientId: String?,
183
180
  scopes: Array<String>?,
184
181
  loginHint: String?,
182
+ nonce: String?,
185
183
  useOneTap: Boolean,
186
184
  forceAccountPicker: Boolean = false,
187
185
  useLegacyGoogleSignIn: Boolean = false,
186
+ filterByAuthorizedAccounts: Boolean = false,
187
+ forceCodeForRefreshToken: Boolean = false,
188
+ requestVerifiedPhoneNumber: Boolean = false,
188
189
  tenant: String? = null,
189
- prompt: String? = null
190
+ prompt: String? = null,
191
+ hostedDomain: String? = null,
192
+ openIDRealm: String? = null
190
193
  ) {
191
194
  if (provider == "apple") {
192
195
  nativeOnLoginError("login", "unsupported_provider", "Apple Sign-In is not supported on Android.")
@@ -212,10 +215,10 @@ object AuthAdapter {
212
215
  pendingScopes = requestedScopes
213
216
 
214
217
  if (useLegacyGoogleSignIn || forceAccountPicker) {
215
- loginLegacy(context, clientId, requestedScopes, loginHint, forceAccountPicker, "login")
218
+ loginLegacy(context, clientId, requestedScopes, loginHint, forceAccountPicker, forceCodeForRefreshToken, hostedDomain, "login")
216
219
  return
217
220
  }
218
- loginOneTap(context, clientId, requestedScopes, loginHint, forceAccountPicker, useOneTap, "login")
221
+ loginOneTap(context, clientId, requestedScopes, loginHint, nonce, forceAccountPicker, useOneTap, filterByAuthorizedAccounts, requestVerifiedPhoneNumber, hostedDomain, "login")
219
222
  }
220
223
 
221
224
  private fun loginMicrosoft(context: Context, scopes: Array<String>?, loginHint: String?, tenant: String?, prompt: String?, origin: String = "login") {
@@ -377,6 +380,8 @@ object AuthAdapter {
377
380
  } finally {
378
381
  connection.disconnect()
379
382
  }
383
+ } catch (e: CancellationException) {
384
+ clearPkceState()
380
385
  } catch (e: Exception) {
381
386
  withContext(Dispatchers.Main) {
382
387
  clearPkceState()
@@ -432,7 +437,7 @@ object AuthAdapter {
432
437
  clearPkceState()
433
438
  nativeOnLoginSuccess(
434
439
  origin, "microsoft", email, name, null, idToken, accessToken, null,
435
- grantedScopes.toTypedArray(), expirationTime
440
+ null, null, null, grantedScopes.toTypedArray(), expirationTime
436
441
  )
437
442
  } catch (e: Exception) {
438
443
  clearPkceState()
@@ -440,6 +445,30 @@ object AuthAdapter {
440
445
  }
441
446
  }
442
447
 
448
+ private fun clearCredentialManagerState(context: Context) {
449
+ moduleScope.launch {
450
+ try {
451
+ CredentialManager.create(context).clearCredentialState(ClearCredentialStateRequest())
452
+ } catch (e: Exception) {
453
+ Log.w(TAG, "clearCredentialState failed: ${e.message}")
454
+ }
455
+ }
456
+ }
457
+
458
+ private fun getLegacyGoogleClient(context: Context): GoogleSignInClient? {
459
+ val clientId = getClientIdFromResources(context) ?: return null
460
+ val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
461
+ .requestIdToken(clientId)
462
+ .requestServerAuthCode(clientId)
463
+ .requestEmail()
464
+ .build()
465
+ return GoogleSignIn.getClient(context, gso)
466
+ }
467
+
468
+ private fun hasLegacyGoogleAccount(context: Context): Boolean {
469
+ return hasLegacyGoogleSession || GoogleSignIn.getLastSignedInAccount(context) != null
470
+ }
471
+
443
472
  @Synchronized
444
473
  private fun clearPkceState() {
445
474
  pendingOrigin = "login"
@@ -487,6 +516,11 @@ object AuthAdapter {
487
516
  return expSeconds * 1000
488
517
  }
489
518
 
519
+ private fun hostedDomainFromEmail(email: String?): String? {
520
+ val parts = email?.split("@", limit = 2) ?: return null
521
+ return parts.getOrNull(1)
522
+ }
523
+
490
524
  private fun getMicrosoftClientIdFromResources(context: Context): String? {
491
525
  val resId = context.resources.getIdentifier("nitro_auth_microsoft_client_id", "string", context.packageName)
492
526
  return if (resId != 0) context.getString(resId) else null
@@ -518,21 +552,30 @@ object AuthAdapter {
518
552
  clientId: String,
519
553
  scopes: List<String>,
520
554
  loginHint: String?,
555
+ nonce: String?,
521
556
  forceAccountPicker: Boolean,
522
557
  useOneTap: Boolean,
558
+ filterByAuthorizedAccounts: Boolean,
559
+ requestVerifiedPhoneNumber: Boolean,
560
+ hostedDomain: String?,
523
561
  origin: String = "login"
524
562
  ) {
525
563
  val activity = currentActivity ?: context as? Activity
526
564
  if (activity == null) {
527
565
  Log.w(TAG, "No Activity context available for One-Tap, falling back to legacy")
528
- return loginLegacy(context, clientId, scopes, loginHint, forceAccountPicker, origin)
566
+ return loginLegacy(context, clientId, scopes, loginHint, forceAccountPicker, false, hostedDomain, origin)
529
567
  }
530
568
 
531
569
  val credentialManager = CredentialManager.create(activity)
532
570
  val googleIdOption = GetGoogleIdOption.Builder()
533
- .setFilterByAuthorizedAccounts(false)
571
+ .setFilterByAuthorizedAccounts(filterByAuthorizedAccounts)
534
572
  .setServerClientId(clientId)
535
573
  .setAutoSelectEnabled(useOneTap && !forceAccountPicker)
574
+ .setRequestVerifiedPhoneNumber(requestVerifiedPhoneNumber)
575
+ .apply {
576
+ if (nonce != null) setNonce(nonce)
577
+ if (hostedDomain != null) setHostedDomainFilter(hostedDomain)
578
+ }
536
579
  .build()
537
580
 
538
581
  val request = GetCredentialRequest.Builder()
@@ -543,9 +586,11 @@ object AuthAdapter {
543
586
  try {
544
587
  val result = credentialManager.getCredential(context = activity, request = request)
545
588
  handleCredentialResponse(result, scopes, origin)
589
+ } catch (e: CancellationException) {
590
+ return@launch
546
591
  } catch (e: Exception) {
547
592
  Log.w(TAG, "One-Tap failed, falling back to legacy: ${e.message}")
548
- loginLegacy(context, clientId, scopes, loginHint, forceAccountPicker, origin)
593
+ loginLegacy(context, clientId, scopes, loginHint, forceAccountPicker, false, hostedDomain, origin)
549
594
  }
550
595
  }
551
596
  }
@@ -556,11 +601,13 @@ object AuthAdapter {
556
601
  scopes: List<String>,
557
602
  loginHint: String?,
558
603
  forceAccountPicker: Boolean,
604
+ forceCodeForRefreshToken: Boolean,
605
+ hostedDomain: String?,
559
606
  origin: String = "login"
560
607
  ) {
561
608
  val ctx = appContext ?: context.applicationContext
562
609
  val intent = GoogleSignInActivity.createIntent(
563
- ctx, clientId, scopes.toTypedArray(), loginHint, forceAccountPicker, origin
610
+ ctx, clientId, scopes.toTypedArray(), loginHint, forceAccountPicker, forceCodeForRefreshToken, hostedDomain, origin
564
611
  )
565
612
  intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK)
566
613
  ctx.startActivity(intent)
@@ -585,11 +632,14 @@ object AuthAdapter {
585
632
  val expirationTime = getJwtExpirationTimeMs(googleIdTokenCredential.idToken)
586
633
  nativeOnLoginSuccess(
587
634
  origin, "google",
588
- googleIdTokenCredential.id,
635
+ googleIdTokenCredential.email,
589
636
  googleIdTokenCredential.displayName,
590
637
  googleIdTokenCredential.profilePictureUri?.toString(),
591
638
  googleIdTokenCredential.idToken,
592
639
  null, null,
640
+ googleIdTokenCredential.id,
641
+ googleIdTokenCredential.phoneNumber,
642
+ hostedDomainFromEmail(googleIdTokenCredential.email),
593
643
  scopes.toTypedArray(),
594
644
  expirationTime
595
645
  )
@@ -599,8 +649,6 @@ object AuthAdapter {
599
649
  }
600
650
  }
601
651
 
602
- // requestScopesSync uses the legacy GoogleSignIn API to check the last signed-in account
603
- // because Credential Manager has no equivalent for querying existing account state.
604
652
  @JvmStatic
605
653
  fun requestScopesSync(context: Context, scopes: Array<String>) {
606
654
  val ctx = appContext ?: context.applicationContext
@@ -631,8 +679,6 @@ object AuthAdapter {
631
679
  nativeOnLoginError("scopes", "not_signed_in", "No user logged in")
632
680
  }
633
681
 
634
- // refreshTokenSync uses the legacy silentSignIn because AuthorizationClient (the recommended
635
- // replacement) requires an Activity context which is not always available at refresh time.
636
682
  @JvmStatic
637
683
  fun refreshTokenSync(context: Context) {
638
684
  val ctx = appContext ?: context.applicationContext
@@ -676,27 +722,14 @@ object AuthAdapter {
676
722
  .isGooglePlayServicesAvailable(ctx) == ConnectionResult.SUCCESS
677
723
  }
678
724
 
679
- // revokeAccessSync uses the legacy GoogleSignIn client because Credential Manager has no
680
- // equivalent revoke API for the Google ID token flow.
681
725
  @JvmStatic
682
726
  fun logoutSync(context: Context) {
683
727
  val ctx = appContext ?: context.applicationContext
684
728
  clearPkceState()
685
- // Clear Credential Manager state (covers One-Tap / passkey credentials).
686
- moduleScope.launch {
687
- try {
688
- CredentialManager.create(ctx).clearCredentialState(ClearCredentialStateRequest())
689
- } catch (e: Exception) {
690
- Log.w(TAG, "clearCredentialState failed: ${e.message}")
691
- }
692
- }
693
- // Also clear legacy GMS sign-in state so getLastSignedInAccount returns null.
694
- val clientId = getClientIdFromResources(ctx)
695
- if (clientId != null) {
696
- val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
697
- .requestIdToken(clientId).requestEmail().build()
698
- GoogleSignIn.getClient(ctx, gso).signOut()
729
+ if (hasLegacyGoogleAccount(ctx)) {
730
+ getLegacyGoogleClient(ctx)?.signOut()
699
731
  }
732
+ hasLegacyGoogleSession = false
700
733
  inMemoryMicrosoftRefreshToken = null
701
734
  inMemoryMicrosoftScopes = defaultMicrosoftScopes
702
735
  }
@@ -705,12 +738,11 @@ object AuthAdapter {
705
738
  fun revokeAccessSync(context: Context) {
706
739
  val ctx = appContext ?: context.applicationContext
707
740
  clearPkceState()
708
- val clientId = getClientIdFromResources(ctx)
709
- if (clientId != null) {
710
- val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
711
- .requestIdToken(clientId).requestServerAuthCode(clientId).requestEmail().build()
712
- GoogleSignIn.getClient(ctx, gso).revokeAccess()
741
+ clearCredentialManagerState(ctx)
742
+ if (hasLegacyGoogleAccount(ctx)) {
743
+ getLegacyGoogleClient(ctx)?.revokeAccess()
713
744
  }
745
+ hasLegacyGoogleSession = false
714
746
  inMemoryMicrosoftRefreshToken = null
715
747
  inMemoryMicrosoftScopes = defaultMicrosoftScopes
716
748
  }
@@ -726,10 +758,11 @@ object AuthAdapter {
726
758
  @Suppress("DEPRECATION")
727
759
  val account = GoogleSignIn.getLastSignedInAccount(ctx)
728
760
  if (account != null) {
761
+ hasLegacyGoogleSession = true
729
762
  val expirationTime = getJwtExpirationTimeMs(account.idToken)
730
763
  nativeOnLoginSuccess("silent", "google", account.email, account.displayName,
731
764
  account.photoUrl?.toString(), account.idToken, null, account.serverAuthCode,
732
- account.grantedScopes?.map { it.scopeUri }?.toTypedArray(), expirationTime)
765
+ account.id, null, null, account.grantedScopes?.map { it.scopeUri }?.toTypedArray(), expirationTime)
733
766
  } else {
734
767
  val refreshToken = inMemoryMicrosoftRefreshToken
735
768
  if (refreshToken != null) {
@@ -795,10 +828,10 @@ object AuthAdapter {
795
828
  nativeOnLoginSuccess("silent", "microsoft",
796
829
  claims["preferred_username"] ?: claims["email"],
797
830
  claims["name"], null,
798
- newIdToken, newAccessToken, null, effectiveScopes.toTypedArray(), expirationTime)
831
+ newIdToken, newAccessToken, null, null, null, null, effectiveScopes.toTypedArray(), expirationTime)
799
832
  } else {
800
833
  if (responseCode in 400..499) {
801
- inMemoryMicrosoftRefreshToken = null // Token is invalid, clear it
834
+ inMemoryMicrosoftRefreshToken = null
802
835
  }
803
836
  val mappedError = try {
804
837
  val json = org.json.JSONObject(responseBody)
@@ -814,6 +847,8 @@ object AuthAdapter {
814
847
  } finally {
815
848
  connection.disconnect()
816
849
  }
850
+ } catch (e: CancellationException) {
851
+ nativeOnLoginError("silent", "cancelled", e.message)
817
852
  } catch (e: Exception) {
818
853
  withContext(Dispatchers.Main) {
819
854
  nativeOnLoginError("silent", "network_error", e.message)
@@ -897,6 +932,8 @@ object AuthAdapter {
897
932
  } finally {
898
933
  connection.disconnect()
899
934
  }
935
+ } catch (e: CancellationException) {
936
+ nativeOnRefreshError("cancelled", e.message)
900
937
  } catch (e: Exception) {
901
938
  withContext(Dispatchers.Main) {
902
939
  nativeOnRefreshError("network_error", e.message)
@@ -21,14 +21,18 @@ class GoogleSignInActivity : ComponentActivity() {
21
21
  private const val EXTRA_SCOPES = "scopes"
22
22
  private const val EXTRA_LOGIN_HINT = "login_hint"
23
23
  private const val EXTRA_FORCE_PICKER = "force_picker"
24
+ private const val EXTRA_FORCE_CODE_FOR_REFRESH_TOKEN = "force_code_for_refresh_token"
25
+ private const val EXTRA_HOSTED_DOMAIN = "hosted_domain"
24
26
  private const val EXTRA_ORIGIN = "origin"
25
27
 
26
- fun createIntent(context: Context, clientId: String, scopes: Array<String>, loginHint: String?, forcePicker: Boolean = false, origin: String = "login"): Intent {
28
+ fun createIntent(context: Context, clientId: String, scopes: Array<String>, loginHint: String?, forcePicker: Boolean = false, forceCodeForRefreshToken: Boolean = false, hostedDomain: String? = null, origin: String = "login"): Intent {
27
29
  return Intent(context, GoogleSignInActivity::class.java).apply {
28
30
  putExtra(EXTRA_CLIENT_ID, clientId)
29
31
  putExtra(EXTRA_SCOPES, scopes)
30
32
  putExtra(EXTRA_LOGIN_HINT, loginHint)
31
33
  putExtra(EXTRA_FORCE_PICKER, forcePicker)
34
+ putExtra(EXTRA_FORCE_CODE_FOR_REFRESH_TOKEN, forceCodeForRefreshToken)
35
+ putExtra(EXTRA_HOSTED_DOMAIN, hostedDomain)
32
36
  putExtra(EXTRA_ORIGIN, origin)
33
37
  addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
34
38
  }
@@ -56,6 +60,8 @@ class GoogleSignInActivity : ComponentActivity() {
56
60
  val scopes = intent.getStringArrayExtra(EXTRA_SCOPES) ?: arrayOf("email", "profile")
57
61
  val loginHint = intent.getStringExtra(EXTRA_LOGIN_HINT)
58
62
  val forcePicker = intent.getBooleanExtra(EXTRA_FORCE_PICKER, false)
63
+ val forceCodeForRefreshToken = intent.getBooleanExtra(EXTRA_FORCE_CODE_FOR_REFRESH_TOKEN, false)
64
+ val hostedDomain = intent.getStringExtra(EXTRA_HOSTED_DOMAIN)
59
65
 
60
66
  val origin = intent.getStringExtra(EXTRA_ORIGIN) ?: "login"
61
67
  if (clientId == null) {
@@ -66,8 +72,12 @@ class GoogleSignInActivity : ComponentActivity() {
66
72
 
67
73
  val gsoBuilder = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
68
74
  .requestIdToken(clientId)
69
- .requestServerAuthCode(clientId)
75
+ .requestServerAuthCode(clientId, forceCodeForRefreshToken)
70
76
  .requestEmail()
77
+
78
+ if (hostedDomain != null) {
79
+ gsoBuilder.setHostedDomain(hostedDomain)
80
+ }
71
81
 
72
82
  scopes.forEach { scopeStr ->
73
83
  if (scopeStr != "email" && scopeStr != "profile" && scopeStr != "openid") {