SNS 계정 연결의 이메일 일치 조건 제거 — 현재 로그인과 새 제공자 인증을 서버에서 확인 · 다른 계정에 연결된 제공자는 거부 · 세션 만료 시 재로그인 안내
This commit is contained in:
@@ -127,8 +127,27 @@ void main() {
|
||||
expect(linker.linkedAccounts.keys, contains(AuthProviderKind.line));
|
||||
});
|
||||
|
||||
test('다른 이메일의 LINE 계정은 붙였다가 바로 떼고 E-AUTH-08', () async {
|
||||
final user = _LinkingUser(linkedEmail: 'someone@else.com');
|
||||
test('이메일이 다르거나 없는 LINE 계정도 붙는다 — 이메일 일치는 소유 확인이 아니다', () async {
|
||||
for (final email in ['someone@else.com', null]) {
|
||||
final user = _LinkingUser(linkedEmail: email);
|
||||
final auth = MockFirebaseAuth(signedIn: true, mockUser: user);
|
||||
final linker = FirebaseAccountLinker(
|
||||
providers: [line(auth)],
|
||||
firebaseAuth: auth,
|
||||
);
|
||||
|
||||
final outcome = await linker.link(AuthProviderKind.line);
|
||||
|
||||
expect(outcome, isA<AuthSucceeded>(), reason: '$email');
|
||||
expect(linker.linkedAccounts.keys, contains(AuthProviderKind.line));
|
||||
}
|
||||
});
|
||||
|
||||
test('다른 FANUZU 계정에 붙은 LINE 은 Firebase 거부를 E-AUTH-08 로 · 연결되지 않는다', () async {
|
||||
final user = _LinkingUser(
|
||||
linkedEmail: null,
|
||||
error: fb.FirebaseAuthException(code: 'credential-already-in-use'),
|
||||
);
|
||||
final auth = MockFirebaseAuth(signedIn: true, mockUser: user);
|
||||
final linker = FirebaseAccountLinker(
|
||||
providers: [line(auth)],
|
||||
@@ -138,14 +157,59 @@ void main() {
|
||||
final outcome = await linker.link(AuthProviderKind.line);
|
||||
|
||||
expect((outcome as AuthFailed).code, 'E-AUTH-08');
|
||||
expect(
|
||||
linker.linkedAccounts.keys,
|
||||
isNot(contains(AuthProviderKind.line)),
|
||||
expect(outcome.message, '다른 FANUZU 계정에 이미 연결된 소셜 계정입니다.');
|
||||
expect(linker.linkedAccounts.keys, isNot(contains(AuthProviderKind.line)));
|
||||
});
|
||||
|
||||
test('Google · Apple(자격증명 경로)도 이메일이 달라도 붙는다', () async {
|
||||
final auth = MockFirebaseAuth(
|
||||
signedIn: true,
|
||||
mockUser: MockUser(uid: 'u-1', email: 'fan@fanuzu.co.kr'),
|
||||
);
|
||||
final outcome = await FirebaseAccountLinker(
|
||||
providers: [const _FakeCredential('other@gmail.com')],
|
||||
firebaseAuth: auth,
|
||||
).link(AuthProviderKind.google);
|
||||
expect(outcome, isA<AuthSucceeded>());
|
||||
});
|
||||
|
||||
test('세션이 만료됐으면 다시 로그인 안내(E-AUTH-12)', () async {
|
||||
final user = _LinkingUser(
|
||||
linkedEmail: null,
|
||||
error: fb.FirebaseAuthException(code: 'user-token-expired'),
|
||||
);
|
||||
final auth = MockFirebaseAuth(signedIn: true, mockUser: user);
|
||||
final outcome = await FirebaseAccountLinker(
|
||||
providers: [line(auth)],
|
||||
firebaseAuth: auth,
|
||||
).link(AuthProviderKind.line);
|
||||
expect((outcome as AuthFailed).code, 'E-AUTH-12');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// 이메일을 함께 주는 자격증명 제공자(Google 흉내).
|
||||
final class _FakeCredential implements SocialAuthProvider, CredentialProvider {
|
||||
const _FakeCredential(this.email);
|
||||
final String email;
|
||||
|
||||
@override
|
||||
AuthProviderKind get provider => AuthProviderKind.google;
|
||||
@override
|
||||
bool get isLive => true;
|
||||
@override
|
||||
List<String> get missingConfig => const [];
|
||||
@override
|
||||
Future<AuthOutcome> signIn() async => const AuthCancelled();
|
||||
@override
|
||||
Future<void> signOut() async {}
|
||||
@override
|
||||
Future<SocialCredential?> obtainCredential() async => (
|
||||
credential: fb.GoogleAuthProvider.credential(idToken: 'id'),
|
||||
email: email,
|
||||
);
|
||||
}
|
||||
|
||||
class _ThrowingAuth extends MockFirebaseAuth {
|
||||
_ThrowingAuth(this.code);
|
||||
final String code;
|
||||
@@ -165,17 +229,19 @@ class _HangingAuth extends MockFirebaseAuth {
|
||||
/// Google 로 가입한 사용자. 연결하면 [linkedEmail] 을 가진 제공자 정보가 붙는다.
|
||||
// ignore: must_be_immutable
|
||||
class _LinkingUser extends MockUser {
|
||||
_LinkingUser({required this.linkedEmail})
|
||||
_LinkingUser({required this.linkedEmail, this.error})
|
||||
: super(
|
||||
uid: 'u-1',
|
||||
email: 'fan@fanuzu.co.kr',
|
||||
providerData: [_info('google.com', 'fan@fanuzu.co.kr')],
|
||||
);
|
||||
|
||||
final String linkedEmail;
|
||||
final String? linkedEmail;
|
||||
final fb.FirebaseAuthException? error;
|
||||
|
||||
@override
|
||||
Future<fb.UserCredential> linkWithProvider(fb.AuthProvider provider) async {
|
||||
if (error case final e?) throw e;
|
||||
final result = await super.linkWithProvider(provider);
|
||||
providerData
|
||||
..removeLast()
|
||||
@@ -184,7 +250,7 @@ class _LinkingUser extends MockUser {
|
||||
}
|
||||
}
|
||||
|
||||
fb.UserInfo _info(String providerId, String email) =>
|
||||
fb.UserInfo _info(String providerId, String? email) =>
|
||||
// ignore: invalid_use_of_protected_member
|
||||
fb.UserInfo.fromJson({
|
||||
'uid': 'p-$providerId',
|
||||
|
||||
Reference in New Issue
Block a user