Check for pending invitations before creating new Family during SSO log in/sign up (#1171)

* Check for pending invitations before creating new Family during SSO account creation

When a user signs in via Google SSO and doesn't have an account yet, the
system now checks for pending invitations before creating a new Family.
If an invitation exists, the user joins the invited family instead.

- OidcAccountsController: check Invitation.pending in link/create_user
- API AuthController: check pending invitations in sso_create_account
- SessionsController: pass has_pending_invitation to mobile SSO callback
- Web view: show "Accept Invitation" button when invitation exists
- Flutter: show "Accept Invitation" tab/button when invitation pending

https://claude.ai/code/session_019Tr6edJa496V1ErGmsbqFU

* Fix external assistant tests: clear Settings cache to prevent test pollution

The tests relied solely on with_env_overrides to clear configuration, but
rails-settings-cached may retain stale Setting values across tests when
the cache isn't explicitly invalidated. Ensure both ENV vars AND Setting
values are cleared with Setting.clear_cache before assertions.

https://claude.ai/code/session_019Tr6edJa496V1ErGmsbqFU

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Juan José Mata
2026-03-10 13:38:42 +01:00
committed by GitHub
parent f6e7234ead
commit c09362b880
10 changed files with 93 additions and 24 deletions

View File

@@ -29,6 +29,7 @@ class AuthProvider with ChangeNotifier {
String? _ssoFirstName;
String? _ssoLastName;
bool _ssoAllowAccountCreation = false;
bool _ssoHasPendingInvitation = false;
User? get user => _user;
bool get isIntroLayout => _user?.isIntroLayout ?? false;
@@ -51,6 +52,7 @@ class AuthProvider with ChangeNotifier {
String? get ssoFirstName => _ssoFirstName;
String? get ssoLastName => _ssoLastName;
bool get ssoAllowAccountCreation => _ssoAllowAccountCreation;
bool get ssoHasPendingInvitation => _ssoHasPendingInvitation;
AuthProvider() {
_loadStoredAuth();
@@ -294,6 +296,7 @@ class AuthProvider with ChangeNotifier {
_ssoFirstName = result['first_name'] as String?;
_ssoLastName = result['last_name'] as String?;
_ssoAllowAccountCreation = result['allow_account_creation'] == true;
_ssoHasPendingInvitation = result['has_pending_invitation'] == true;
_isLoading = false;
notifyListeners();
return false;
@@ -410,6 +413,7 @@ class AuthProvider with ChangeNotifier {
_ssoFirstName = null;
_ssoLastName = null;
_ssoAllowAccountCreation = false;
_ssoHasPendingInvitation = false;
}
Future<void> logout() async {

View File

@@ -148,7 +148,9 @@ class _SsoOnboardingScreenState extends State<SsoOnboardingScreen> {
),
Expanded(
child: _TabButton(
label: 'Create New',
label: authProvider.ssoHasPendingInvitation
? 'Accept Invitation'
: 'Create New',
isSelected: !_showLinkForm,
onTap: () =>
setState(() => _showLinkForm = false),
@@ -258,6 +260,7 @@ class _SsoOnboardingScreenState extends State<SsoOnboardingScreen> {
}
Widget _buildCreateForm(AuthProvider authProvider, ColorScheme colorScheme) {
final hasPendingInvitation = authProvider.ssoHasPendingInvitation;
return Form(
key: _createFormKey,
child: Column(
@@ -271,11 +274,16 @@ class _SsoOnboardingScreenState extends State<SsoOnboardingScreen> {
),
child: Row(
children: [
Icon(Icons.person_add, color: colorScheme.primary),
Icon(
hasPendingInvitation ? Icons.mail_outline : Icons.person_add,
color: colorScheme.primary,
),
const SizedBox(width: 12),
Expanded(
child: Text(
'Create a new account using your Google identity.',
hasPendingInvitation
? 'You have a pending invitation. Accept it to join an existing household.'
: 'Create a new account using your Google identity.',
style: TextStyle(color: colorScheme.onSurface),
),
),
@@ -318,7 +326,9 @@ class _SsoOnboardingScreenState extends State<SsoOnboardingScreen> {
width: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Create Account'),
: Text(hasPendingInvitation
? 'Accept Invitation'
: 'Create Account'),
),
],
),

View File

@@ -374,6 +374,7 @@ class AuthService {
'first_name': params['first_name'] ?? '',
'last_name': params['last_name'] ?? '',
'allow_account_creation': params['allow_account_creation'] == 'true',
'has_pending_invitation': params['has_pending_invitation'] == 'true',
};
}