【问题标题】:Flutter web google_sign_in: How to retrieve refreshTokenFlutter web google_sign_in:如何检索refreshToken
【发布时间】:2021-12-13 04:19:46
【问题描述】:

google_sign_in 不返回 refreshToken。有没有办法通过 Google 登录并接收可以发送到 API 以进一步访问用户数据的刷新令牌?

刷新令牌也可以通过 serverAuthCode 获得,此时它始终为空。已经创建了多个问题来描述此问题:

有没有办法通过 Google Sign In 进行身份验证并接收 refreshToken 或 serverAuthCode?​​p>

【问题讨论】:

    标签: flutter dart google-signin flutter-web


    【解决方案1】:

    Google 登录基于 oAuth2,可以创建自己的流程实现。

    这是一个 google 登录服务的代码 sn-p,可用于检索 refreshToken:

    import 'dart:async';
    import 'dart:html' as html;
    
    import 'package:oauth2/oauth2.dart' as oauth2;
    
    class GoogleSignInService {
      final authorizationEndpoint =
          Uri.parse('https://accounts.google.com/o/oauth2/v2/auth');
      final tokenEndpoint = Uri.parse('https://oauth2.googleapis.com/token');
    
      final String identifier;
      final String secret;
      final String baseUrl;
      final List<String> scopes;
    
      _SignInSession? _signInSession;
    
      Uri get redirectUrl => Uri.parse('$baseUrl/callback.html');
    
      GoogleSignInService({
        required this.identifier,
        required this.secret,
        required this.baseUrl,
        required this.scopes,
      }) {
        html.window.addEventListener('message', _eventListener);
      }
    
      void _eventListener(html.Event event) {
        _signInSession?.completeWithCode((event as html.MessageEvent).data);
      }
    
      Future<GoogleSignInUser?> signIn() async {
        if (_signInSession != null) {
          return null;
        }
    
        final grant = oauth2.AuthorizationCodeGrant(
          identifier,
          authorizationEndpoint,
          tokenEndpoint,
          secret: secret,
        );
    
        var authorizationUrl = grant.getAuthorizationUrl(
          redirectUrl,
          scopes: scopes,
        );
    
        final url =
            '${authorizationUrl.toString()}&access_type=offline&prompt=select_account+consent';
        _signInSession = _SignInSession(url);
        final code = await _signInSession!.codeCompleter.future;
    
        if (code != null) {
          final client = await grant.handleAuthorizationResponse({'code': code});
          return GoogleSignInUser(
            accessToken: client.credentials.accessToken,
            refreshToken: client.credentials.refreshToken,
            idToken: client.credentials.idToken!,
          );
        } else {
          return null;
        }
      }
    }
    
    class GoogleSignInUser {
      final String accessToken;
      final String? refreshToken;
      final String idToken;
    
      const GoogleSignInUser({
        required this.accessToken,
        required this.refreshToken,
        required this.idToken,
      });
    
      @override
      String toString() {
        return 'GoogleSignInUser{accessToken: $accessToken, refreshToken: $refreshToken, idToken: $idToken}';
      }
    }
    
    class _SignInSession {
      final codeCompleter = Completer<String?>();
      late final html.WindowBase _window;
      late final Timer _timer;
    
      bool get isClosed => codeCompleter.isCompleted;
    
      _SignInSession(String url) {
        _window =
            html.window.open(url, '_blank', 'location=yes,width=550,height=600');
        _timer = Timer.periodic(const Duration(milliseconds: 500), (timer) {
          if (_window.closed == true) {
            if (!isClosed) {
              codeCompleter.complete(null);
            }
            _timer.cancel();
          }
        });
      }
    
      void completeWithCode(String code) {
        if (!isClosed) {
          codeCompleter.complete(code);
        }
      }
    }
    

    确保同时创建web/callback.html 文件:

    <html>
    <body>
    </body>
    <script>
            function findGetParameter(parameterName) {
                var result = null,
                tmp = [];
                location.search
                    .substr(1)
                    .split("&")
                    .forEach(function (item) {
                        tmp = item.split("=");
                        if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]);
                     });
                return result;
             }
            let code = findGetParameter('code');
    
            window.opener.postMessage(code, "http://localhost:5000");
            window.close();
        </script>
    </html>
    

    http://localhost:5000 更改为您在生产中使用的任何域。

    示例用法:

    final googleSignIn = GoogleSignInService(
            identifier: 'CLIENT_ID',
            secret: 'CLIENT_SECRET',
            baseUrl: 'http://localhost:5000',
            scopes: [
              'email',
            ],
          ),
    final user = await googleSignIn.signIn();
    

    【讨论】:

    • 是你的main.dart的最高代码sn-p吗?没看到主线或者,您如何在 main.dart 中调用它?
    • 有一个示例使用代码 sn-p。随心所欲地使用它。例如,在用户点击“登录”按钮后
    • 好的,我会弄清楚如何构建我的 main.dart 以进行测试。
    猜你喜欢
    • 2020-08-03
    • 2019-09-22
    • 2021-04-11
    • 2019-10-16
    • 1970-01-01
    • 2018-06-12
    • 2022-08-23
    • 2021-03-21
    • 2019-06-30
    相关资源
    最近更新 更多