Published on

Flutter Authentication using OpenID, ABP and IdentityServer4. Part 5

Table of Contents


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

"Todos_Flutter_2": {
  "ClientId": "Todos_Flutter_2",
  "RedirectUri": "http://localhost:3000/"

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.

// Flutter2 Client
var flutter2Client = configurationSection["Todos_Flutter_2:ClientId"];
if (!flutter2Client.IsNullOrWhiteSpace())
    var redirectUrl = configurationSection["Todos_Flutter_2:RedirectUri"];
    await CreateClientAsync(
        name: flutter2Client,
        scopes: commonScopes,
        grantTypes: new[] { "authorization_code" },
        requireClientSecret: false,
        redirectUri: redirectUrl

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

flutter create mytodoapp

Move into the folder

cd mytodoapp

List the devices

flutter devices

Run the app

flutter run

4. Add dependencies

  openid_client: ^0.4.1
  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


5. Create flutter page to login and logout

import 'package:flutter/material.dart';
import 'package:openid_client/openid_client.dart';
import 'package:openid_client/openid_client_io.dart';
import 'package:url_launcher/url_launcher.dart';
import 'dart:async';

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

  _HomePageState createState() => _HomePageState();

class _HomePageState extends State<HomePage> {
  final String _clientId = 'Todos_Flutter_2';
  static const String _issuer = '';
  final List<String> _scopes = <String>[
  String logoutUrl = "";
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Home"),
      body: Container(
        child: Center(
          child: Column(
            children: [
                child: Text("Login"),
                onPressed: () async {
                  var tokenInfo = await authenticate(
                      Uri.parse(_issuer), _clientId, _scopes);
                child: Text("Logout"),
                onPressed: () async {

  Future<TokenResponse> authenticate(
      Uri uri, String clientId, List<String> scopes) async {
    // create the client
    var issuer = await;
    var client = new Client(issuer, clientId);

    // create a function to open a browser with an url
    urlLauncher(String url) async {
      if (await canLaunch(url)) {
        await launch(url, forceWebView: true, enableJavaScript: true);
      } else {
        throw 'Could not launch $url';

    // create an authenticator
    var authenticator = new Authenticator(
      scopes: scopes,
      urlLancher: urlLauncher,
      port: 3000,

    // starts the authentication
    var c = await authenticator.authorize();
    // close the webview when finished

    var res = await c.getTokenResponse();
    setState(() {
      logoutUrl = c.generateLogoutUrl().toString();
    return res;

  Future<void> logout() async {
    if (await canLaunch(logoutUrl)) {
      await launch(logoutUrl, forceWebView: true);
    } else {
      throw 'Could not launch $logoutUrl';
    await Future.delayed(Duration(seconds: 2));