Eight Rice
Eight Rice

Reputation: 720

How to catch OAuth2 token in Flutter Web?

This dependency supposedly supports web, but the implementation to listen for the callback and retrieve the token is missing. After digging around for the last three days, a few people suggested that it would work using the dart:html library, with onMessage, and postMessage functions.

Here's my setup:

import 'dart:html' as html;
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:oauth2/oauth2.dart' as oauth2;
import 'package:url_launcher/url_launcher.dart';

class AuthYoutube {
var httpClient = http.Client();
  Future<oauth2.Client> getClient() async {
    var grant = new oauth2.AuthorizationCodeGrant(
        CLIENT_ID, AUTH_DOMAIN, AUTH_TOKEN,
        secret: secret);
    var authorizationUrl =
        grant.getAuthorizationUrl(REDIRECT_URI, scopes: scopes);

    html.window.open(authorizationUrl.toString(), "open");
    Completer<LinkedHashMap<dynamic, dynamic>> completer = Completer();
    html.window.onMessage.listen((event) async {
      completer.complete(event.data as LinkedHashMap<dynamic, dynamic>);
    });
    Uri responseUri;
    completer.future.then((value) {
      responseUri = Uri.parse(value.toString());
      return grant.handleAuthorizationResponse(responseUri.queryParameters);
    });
  }

} And the separate route set as the callback URL:

class Special extends StatelessWidget {
  static String route = "/special";
  @override
  Widget build(BuildContext context) {
    final loc = Uri.parse(html.window.location.href);
    final code = loc.queryParameters["code"];
    print("code $code");
    html.window.postMessage(code, html.window.location.origin);
    return Text("All done");
  }
}

What ends up happening is the auth token shows up in the address bar at the last step. I can see it in there after ?code=, but it doesn't get printed out. The listen method gets triggered 5-6 times throughout the entire flow (while clicking on "allow" in the other window). In the console, I only get this one error:

Uncaught (in promise) Error: Bad state: Future already completed
    at Object.throw_ [as throw] (errors.dart:212)
    at _AsyncCompleter.new.complete (future_impl.dart:43)
    at authYT.dart:41
    at Generator.next (<anonymous>)
    at runBody (async_patch.dart:84)
    at Object._async [as async] (async_patch.dart:123)
    at authYT.dart:40
    at Object._checkAndCall (operations.dart:324)
    at Object.dcall (operations.dart:329)
    at html_dart2js.dart:37246

Upvotes: 13

Views: 3642

Answers (2)

Jeff S.
Jeff S.

Reputation: 1006

There is a new plugin Flutter plugin that supports the complete authentication flow in web and also on mobile:

https://pub.dev/packages/flutter_web_auth

Upvotes: 2

Muhammad Shahrukh
Muhammad Shahrukh

Reputation: 61

You can capture the access token in the onGenerateRoute method through the URL.

Here’s a link to the blog that does it properly: https://aws.plainenglish.io/oauth2-in-flutter-web-using-aws-cognito-f7051be92bdf

Upvotes: 1

Related Questions