Flutter web google_sign_in:如何检索refreshToken

Posted

技术标签:

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

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

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

https://github.com/flutter/flutter/issues/45847 https://github.com/flutter/flutter/issues/57712 https://github.com/flutter/flutter/issues/15796 https://github.com/flutter/flutter/issues/62474

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

【问题讨论】:

【参考方案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 'GoogleSignInUseraccessToken: $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 以进行测试。

以上是关于Flutter web google_sign_in:如何检索refreshToken的主要内容,如果未能解决你的问题,请参考以下文章

如何将 Flutter 移动应用迁移到 Flutter Web

flutter Web端支持内嵌加载网页

Flutter web 最新进展: 发掘更多可能!

Flutter 调味移动应用和 Flutter web

flutter web开发环境配置

flutter web开发环境配置