Anto Subash.

Table of contents

Intro

In this post we will implement the OAuth for the Flutter app.

1. Adding a new client to the IdentityServer

first step is to create a new client for the nextjs application. list of client is available in the appsettings.json file at DbMigrator project.

1.1 Add the new json entry

1"Todos_Flutter_2": {
2  "ClientId": "Todos_Flutter_2",
3  "RedirectUri": "http://localhost:3000/"
4},

1.2 Update the CreateClientsAsync method

In the Domain project there is a IdentityServerDataSeedContributor class which has the CreateClientsAsync method. This method creates the Identity server clients during the migrations. so we will update this method to include the new json entry.

1// Flutter2 Client
2var flutter2Client = configurationSection["Todos_Flutter_2:ClientId"];
3if (!flutter2Client.IsNullOrWhiteSpace())
4{
5    var redirectUrl = configurationSection["Todos_Flutter_2:RedirectUri"];
6    await CreateClientAsync(
7        name: flutter2Client,
8        scopes: commonScopes,
9        grantTypes: new[] { "authorization_code" },
10        requireClientSecret: false,
11        redirectUri: redirectUrl
12    );
13}

1.3 Run the migration

Now run the migration to add the client to the DB.

2. Exposing localhost using ngrok

.\ngrok.exe http https://localhost:44354

3. Creating a flutter app

Create the flutter app

1flutter create mytodoapp

Move into the folder

1cd mytodoapp

List the devices

1flutter devices

Run the app

1flutter run

4. Add dependencies

1dependencies:
2  openid_client: ^0.4.1
3  url_launcher: ^6.0.4

4.1 Update the Android Manifest

Update the android app to use the usesCleartextTraffic

you can find the android manifest in mytodoapp\android\app\src\main

1<application
2    android:usesCleartextTraffic="true"
3    android:label="mytodos"
4    android:icon="@mipmap/ic_launcher">

5. Create flutter page to login and logout

1import 'package:flutter/material.dart';
2import 'package:openid_client/openid_client.dart';
3import 'package:openid_client/openid_client_io.dart';
4import 'package:url_launcher/url_launcher.dart';
5import 'dart:async';
6
7class HomePage extends StatefulWidget {
8  HomePage({Key? key}) : super(key: key);
9
10  
11  _HomePageState createState() => _HomePageState();
12}
13
14class _HomePageState extends State<HomePage> {
15  final String _clientId = 'Todos_Flutter_2';
16  static const String _issuer = 'https://d78170304b87.ngrok.io';
17  final List<String> _scopes = <String>[
18    'openid',
19    'profile',
20    'email',
21    'offline_access',
22    'Todos'
23  ];
24  String logoutUrl = "";
25  
26  Widget build(BuildContext context) {
27    return Scaffold(
28      appBar: AppBar(
29        title: Text("Home"),
30      ),
31      body: Container(
32        child: Center(
33          child: Column(
34            crossAxisAlignment: CrossAxisAlignment.center,
35            mainAxisAlignment: MainAxisAlignment.center,
36            children: [
37              ElevatedButton(
38                child: Text("Login"),
39                onPressed: () async {
40                  var tokenInfo = await authenticate(
41                      Uri.parse(_issuer), _clientId, _scopes);
42                  print(tokenInfo.accessToken);
43                },
44              ),
45              ElevatedButton(
46                child: Text("Logout"),
47                onPressed: () async {
48                  logout();
49                },
50              ),
51            ],
52          ),
53        ),
54      ),
55    );
56  }
57
58  Future<TokenResponse> authenticate(
59      Uri uri, String clientId, List<String> scopes) async {
60    // create the client
61    var issuer = await Issuer.discover(uri);
62    var client = new Client(issuer, clientId);
63
64    // create a function to open a browser with an url
65    urlLauncher(String url) async {
66      if (await canLaunch(url)) {
67        await launch(url, forceWebView: true, enableJavaScript: true);
68      } else {
69        throw 'Could not launch $url';
70      }
71    }
72
73    // create an authenticator
74    var authenticator = new Authenticator(
75      client,
76      scopes: scopes,
77      urlLancher: urlLauncher,
78      port: 3000,
79    );
80
81    // starts the authentication
82    var c = await authenticator.authorize();
83    // close the webview when finished
84    closeWebView();
85
86    var res = await c.getTokenResponse();
87    setState(() {
88      logoutUrl = c.generateLogoutUrl().toString();
89    });
90    print(res.accessToken);
91    return res;
92  }
93
94  Future<void> logout() async {
95    if (await canLaunch(logoutUrl)) {
96      await launch(logoutUrl, forceWebView: true);
97    } else {
98      throw 'Could not launch $logoutUrl';
99    }
100    await Future.delayed(Duration(seconds: 2));
101    closeWebView();
102  }
103}
Buy Me a Coffee at ko-fi.com