feat(auth): 接入 Google 登录
- 登录页保留邮箱与 Google 两个入口,Google 按钮按官方规范改为白底四色图标 - 新增 GoogleAuthService:封装 initialize/authenticate,失败以结果对象返回 - AuthApi 增加 googleLogin,AuthController 增加 signInWithGoogle - iOS 补充 CFBundleURLTypes 与 GIDClientID,Bundle ID 统一为 com.stormg.lumadrama - Google 请求期间按钮显示转圈并禁止重复点击
This commit is contained in:
1 parent
7b90bd59b9
commit
67670d46b1
18 files changed
+335
-55
No files matched your search
@@ -103,6 +103,17 @@ class AuthController extends ChangeNotifier {
|
||||
await _apply(session);
|
||||
}
|
||||
|
||||
/// Google 登录:把 Google 返回的 idToken 交给后端校验并落登录态。
|
||||
Future<void> signInWithGoogle(String idToken) async {
|
||||
final session = await _api.googleLogin(
|
||||
idToken: idToken,
|
||||
deviceId: await _store.deviceId(),
|
||||
platform: _platform,
|
||||
osVersion: _osVersion,
|
||||
);
|
||||
await _apply(session);
|
||||
}
|
||||
|
||||
Future<void> clear() async {
|
||||
_session = null;
|
||||
await _store.clear();
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:google_sign_in/google_sign_in.dart';
|
||||
|
||||
/// Google 授权结局。
|
||||
///
|
||||
/// 插件调用与 IO 一样用结果对象返回,不把异常抛到页面:
|
||||
/// 页面只需要区分"成功 / 用户取消 / 不可用 / 失败"四种情况。
|
||||
class GoogleSignInOutcome {
|
||||
const GoogleSignInOutcome._({
|
||||
this.idToken,
|
||||
this.canceled = false,
|
||||
this.unavailable = false,
|
||||
this.failed = false,
|
||||
});
|
||||
|
||||
/// 成功拿到可交给后端校验的 idToken。
|
||||
const GoogleSignInOutcome.success(String idToken) : this._(idToken: idToken);
|
||||
|
||||
/// 用户主动取消授权,页面不应提示错误。
|
||||
const GoogleSignInOutcome.canceled() : this._(canceled: true);
|
||||
|
||||
/// 当前平台或构建不支持交互式登录。
|
||||
const GoogleSignInOutcome.unavailable() : this._(unavailable: true);
|
||||
|
||||
/// 授权过程失败(网络、配置缺失、Google 拒绝等)。
|
||||
const GoogleSignInOutcome.failed() : this._(failed: true);
|
||||
|
||||
/// Google 返回的 idToken;未成功时为 null。
|
||||
final String? idToken;
|
||||
|
||||
/// 用户是否主动取消。
|
||||
final bool canceled;
|
||||
|
||||
/// 平台是否不支持。
|
||||
final bool unavailable;
|
||||
|
||||
/// 是否失败。
|
||||
final bool failed;
|
||||
|
||||
/// 是否拿到了可用的 idToken。
|
||||
bool get isSuccess => idToken != null && idToken!.isNotEmpty;
|
||||
}
|
||||
|
||||
/// Google 登录客户端封装。
|
||||
///
|
||||
/// 只负责拉起 Google 授权并取出 idToken;换发本应用令牌由 AuthController 完成,
|
||||
/// 插件调用与网络层各自独立,页面通过 [GoogleSignInOutcome] 判断结局。
|
||||
class GoogleAuthService {
|
||||
GoogleAuthService({GoogleSignIn? signIn})
|
||||
: _signIn = signIn ?? GoogleSignIn.instance;
|
||||
|
||||
/// Android 必须显式传 serverClientId 才会返回 idToken,且要传 Web 客户端 id;
|
||||
/// iOS 由 Info.plist 的 GIDClientID 决定,无需再传。
|
||||
static const String _serverClientId =
|
||||
'11115008580-dbqe47fhp5560nhrocrppiq7hcnlvl1s.apps.googleusercontent.com';
|
||||
|
||||
final GoogleSignIn _signIn;
|
||||
bool _initialized = false;
|
||||
|
||||
/// 拉起授权并返回结局;不会抛异常。
|
||||
Future<GoogleSignInOutcome> signIn() async {
|
||||
try {
|
||||
await _ensureInitialized();
|
||||
if (!_signIn.supportsAuthenticate()) {
|
||||
return const GoogleSignInOutcome.unavailable();
|
||||
}
|
||||
final account = await _signIn.authenticate();
|
||||
final idToken = account.authentication.idToken;
|
||||
if (idToken == null || idToken.isEmpty) {
|
||||
return const GoogleSignInOutcome.failed();
|
||||
}
|
||||
return GoogleSignInOutcome.success(idToken);
|
||||
} on GoogleSignInException catch (error) {
|
||||
if (error.code == GoogleSignInExceptionCode.canceled) {
|
||||
return const GoogleSignInOutcome.canceled();
|
||||
}
|
||||
return const GoogleSignInOutcome.failed();
|
||||
} catch (_) {
|
||||
return const GoogleSignInOutcome.failed();
|
||||
}
|
||||
}
|
||||
|
||||
/// 插件要求先 initialize 才能 authenticate;只做一次。
|
||||
Future<void> _ensureInitialized() async {
|
||||
if (_initialized) return;
|
||||
await _signIn.initialize(
|
||||
serverClientId: _isAndroid ? _serverClientId : null,
|
||||
);
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
bool get _isAndroid => !kIsWeb && Platform.isAndroid;
|
||||
}
|
||||
Reference in new issue
Block a user