Extracting and Storing Code Parameter After Logging In on Flutter App

24 Views Asked by At

Hi guys!

I'm building a Flutter app, and I want to implement logging in via a web browser within the app when the login button is clicked.

Here's the link: https://XXX.sandbox.my.site.com/services/oauth2/authorize?response_type=code&client_id=$clientId&redirect_uri=$redirectUri;

Upon logging in, I receive this URL as the redirect_uri: https://XXX.sandbox.my.site.com/services/oauth2/callback

and it provides me with this URL: https://XXX.sandbox.my.site.com/services/oauth2/callback?code=aPrxLwJ_Q1_93GLJYLcITEmaCP9l7ael258XF6M79BG_xRhdGb6YVNCCSc8G_.64kh3e3R.jww%3D%3D&sfdc_community_url=https://XXX.sandbox.my.site.com&sfdc_community_id=0DB7Q000001MvPYWA0

I want to extract the code parameter from this URL and store it locally for the subsequent process in my Flutter app.

Currently, I have this code, but when I log in and get redirected to the callback where I want to extract the code, nothing happens. Does anyone have any idea what I'm doing wrong or what I'm missing?

My login screen code:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_web_browser/flutter_web_browser.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:uni_links/uni_links.dart';

class LoginScreen extends StatefulWidget {
  const LoginScreen({Key? key}) : super(key: key);

  @override
  _LoginScreenState createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {
  var _isLoading = false;
  final String clientId = 'CLIENT_ID';
  final String clientSecret = 'CLIENT_SECRET';
  final String redirectUri = 'https://XXX.sandbox.my.site.com/services/oauth2/callback';
  late StreamSubscription _sub;

  @override
  void initState() {
    super.initState();
    initUniLinks();
  }

  void initUniLinks() async {
    await initPlatformState();
    _sub = uriLinkStream.listen((Uri? uri) {
      print('Got url: $uri');
      if (uri != null && uri.queryParameters.containsKey('code')) {
        String code = uri.queryParameters['code']!;
        print('Get code: $code');
        _getToken(code);
      }
    }, onError: (err) {
      print('Error: $err');
    });
  }

  Future<void> initPlatformState() async {
    await Future.delayed(Duration(seconds: 1));
  }

  @override
  void dispose() {
    _sub.cancel();
    super.dispose();
  }

  void _tryLogin() async {
    setState(() {
      _isLoading = true;
    });

    String authUrl =
        'https://XXX.sandbox.my.site.com/services/oauth2/authorize?response_type=code&client_id=$clientId&redirect_uri=$redirectUri';
    await FlutterWebBrowser.openWebPage(url: authUrl);

    setState(() {
      _isLoading = false;
    });
  }

  Future<void> _getToken(String code) async {
    final response = await http.post(
      Uri.parse('https://XXX.sandbox.my.site.com/services/oauth2/token'),
      headers: {'Content-Type': 'application/x-www-form-urlencoded'},
      body: {
        'grant_type': 'authorization_code',
        'code': code,
        'client_id': clientId,
        'client_secret': clientSecret,
        'redirect_uri': redirectUri,
      },
    );

    if (response.statusCode == 200) {
      print('Response: ${response.body}');
      final data = jsonDecode(response.body);
      print('Access Token: ${data['access_token']}');
    } else {
      //TODO: Handle login failure
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sign in'),
      ),
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              if (_isLoading)
                const CircularProgressIndicator()
              else
                ElevatedButton(
                  onPressed: _tryLogin,
                  style: ElevatedButton.styleFrom(
                    minimumSize: const Size.fromHeight(50),
                    textStyle: const TextStyle(fontSize: 18),
                  ),
                  child: const Text('Sign in'),
                ),
            ],
          ),
        ),
      ),
    );
  }
}

AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application
            android:label="app"
            android:name="${applicationName}"
            android:icon="@mipmap/ic_launcher">
        <activity
                android:name=".MainActivity"
                android:exported="true"
                android:launchMode="singleTop"
                android:theme="@style/LaunchTheme"
                android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
                android:hardwareAccelerated="true"
                android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
                    android:name="io.flutter.embedding.android.NormalTheme"
                    android:resource="@style/NormalTheme"
            />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>

            <!-- Deep Links -->
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data
                        android:scheme="https"
                        android:host="XXX--qa.sandbox.my.site.com" />
            </intent-filter>
        </activity>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
                android:name="flutterEmbedding"
                android:value="2" />
    </application>
</manifest>

Thanks in advance!

0

There are 0 best solutions below