가입 수단 5종(이메일·Google·Apple·Kakao·LINE) · 로그인 에뮬레이터 연결 · 최소 지원 Android 10

This commit is contained in:
Sn-Kinos
2026-09-01 01:03:56 +09:00
parent 1d462ebe31
commit 304ad105d6
14 changed files with 770 additions and 54 deletions
+102 -1
View File
@@ -148,7 +148,7 @@ final class AppleSignInProvider implements SocialAuthProvider {
kIsWeb || defaultTargetPlatform == TargetPlatform.android;
}
/// 앱 키가 없어 아직 붙이지 못한 제공자 — 카카오·네이버.
/// 앱 키가 없어 아직 붙이지 못한 제공자 — 카카오·LINE.
///
/// 예외를 던지지 않는다. [AuthNotConfigured] 로 "무엇이 없어서 못 쓰는지"를 돌려준다.
/// 키가 들어오면 이 자리에 실 구현체를 넣고 [AuthRepository] 목록만 바꾸면 된다.
@@ -197,3 +197,104 @@ AuthAccount _toAccount(fb.User? user, SocialProvider provider) => AuthAccount(
displayName: user?.displayName,
email: user?.email,
);
/// 이메일 · 비밀번호 — 실 코드 경로.
///
/// 정책 설계서 §P-01 이 가입 수단 5종에 이메일을 넣어 확정했다(답변서 Q7).
/// 초안 당시 CD-18 미확정이라 비워 두었던 자리다.
///
/// **가입과 로그인을 한 버튼에 합치지 않는다.** 합치면 오타로 친 이메일이
/// 새 계정을 만들어 버리고, 사용자는 「비밀번호가 틀렸다」가 아니라 「내 데이터가
/// 사라졌다」를 겪는다. Figma 도 로그인 화면과 회원가입 화면을 나눠 두었다.
final class EmailPasswordProvider implements EmailAuthProvider {
const EmailPasswordProvider();
@override
AuthProviderKind get provider => AuthProviderKind.email;
@override
bool get isLive => true;
@override
List<String> get missingConfig =>
FirebaseBoot.isLive ? const [] : FirebaseBoot.missingLabels;
/// 폼 없이 부르면 아무 일도 하지 않는다. 화면이 입력을 받아
/// [signInWithEmail] 을 부른다.
@override
Future<AuthOutcome> signIn() async {
final missing = missingConfig;
if (missing.isNotEmpty) return AuthNotConfigured(provider, missing);
return const AuthCancelled();
}
@override
Future<AuthOutcome> signInWithEmail(String email, String password) =>
_run(() => fb.FirebaseAuth.instance.signInWithEmailAndPassword(
email: email.trim(),
password: password,
));
@override
Future<AuthOutcome> signUpWithEmail(String email, String password) =>
_run(() => fb.FirebaseAuth.instance.createUserWithEmailAndPassword(
email: email.trim(),
password: password,
));
@override
Future<AuthOutcome> sendPasswordReset(String email) async {
final missing = missingConfig;
if (missing.isNotEmpty) return AuthNotConfigured(provider, missing);
try {
await fb.FirebaseAuth.instance.sendPasswordResetEmail(email: email.trim());
return const AuthCancelled();
} on fb.FirebaseAuthException catch (error) {
return AuthFailed(_code(error), _message(error));
}
}
Future<AuthOutcome> _run(Future<fb.UserCredential> Function() action) async {
final missing = missingConfig;
if (missing.isNotEmpty) return AuthNotConfigured(provider, missing);
try {
final result = await action();
return AuthSucceeded(_toAccount(result.user, provider));
} on fb.FirebaseAuthException catch (error) {
return AuthFailed(_code(error), _message(error));
}
}
@override
Future<void> signOut() async {
// Firebase 세션 종료는 AuthSession 이 한 번만 담당한다.
}
/// SDK 코드를 에러 카탈로그 ID 로 옮긴다.
///
/// **계정 존재 여부를 드러내지 않는다.** 「등록되지 않은 이메일」과 「비밀번호
/// 오류」를 갈라 주면 어느 이메일이 가입돼 있는지 훑을 수 있다.
static String _code(fb.FirebaseAuthException error) => switch (error.code) {
'email-already-in-use' => 'E-AUTH-04',
'weak-password' => 'E-AUTH-05',
'invalid-email' => 'E-AUTH-06',
'too-many-requests' => 'E-AUTH-07',
_ => 'E-AUTH-01',
};
static String _message(fb.FirebaseAuthException error) => switch (error.code) {
'email-already-in-use' => '이미 가입된 이메일입니다.',
'weak-password' => '비밀번호가 너무 짧습니다. 8자 이상으로 정해 주세요.',
'invalid-email' => '이메일 형식이 올바르지 않습니다.',
'too-many-requests' => '잠시 후 다시 시도해 주세요.',
_ => '이메일 또는 비밀번호를 확인해 주세요.',
};
}
/// LINE — 앱 키가 없어 아직 붙이지 못했다.
///
/// 네이버 자리를 대신한다. 정책 §P-01 이 확정한 5종에 LINE 이 있고 네이버는 없다.
const lineProvider = KeylessSocialProvider(AuthProviderKind.line, [
'LINE Login 채널 ID / 채널 시크릿',
'LINE Developers 콘솔 채널 등록(사업자 정보 필요)',
]);