Webux Lab

By Studio Webux

AWS Cognito with Flutter

TG
Tommy Gingras Studio Webux 2025-01-14

Introduction

This is my notes to implement AWS Cognito User Pool with a Flutter IOS App. I tried to keep it as simple as possible and using only aws-amplify dependencies.

The User Pool has been created manually and uses the default configuration as it is only a test.

Setup Zed Editor

Install Dart Plugin.

Create Flutter Project

flutter create my_app
cd my_app

open -a Simulator
flutter run

At this point the counter app should be opened.

Setup AWS Cognito

AWS Cognito User Pool

Go to the service AWS Cognito, Select User Pool, Create a new Pool.

Then create a new App Client for Mobile App.

You will need the following:

Create the following configuration file lib/data/amplifyconfiguration.dart

Update the parameters to fit your configuration

const amplifyconfig = '''{
  "version": "1",
  "auth": {
    "aws_region": "<REGION>",
    "user_pool_id": "<USER_POOL_ID>",
    "user_pool_client_id": "<USER_POOL_CLIENT_ID>",
    "identity_pool_id": "<IDENTITY_POOL_ID>",
    "username_attributes": ["email"],
    "standard_required_attributes": ["email"],
    "user_verification_types": ["email"],
    "unauthenticated_identities_enabled": true,
    "password_policy": {
      "min_length": 8,
      "require_lowercase": true,
      "require_uppercase": true,
      "require_numbers": true,
      "require_symbols": true
    }
  }
}''';

Setup Flutter Packages

flutter pub add amplify_flutter
flutter pub add amplify_auth_cognito
flutter pub add amplify_authenticator

My main.dart

import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_authenticator/amplify_authenticator.dart';
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:my_app/data/amplifyconfiguration.dart';

import 'package:flutter/material.dart';

import 'pages/home_page.dart';
Future<void> main() async {
  try {
    WidgetsFlutterBinding.ensureInitialized();
    await _configureAmplify();

    runApp(const MyApp());
  } on AmplifyException catch (e) {
    runApp(Text("Error configuring Amplify: ${e.message}"));
  }
}

Future<void> _configureAmplify() async {
  try {
    await Amplify.addPlugin(AmplifyAuthCognito());
    await Amplify.configure(amplifyconfig);
    safePrint('Successfully configured');
  } on Exception catch (e) {
    safePrint('Error configuring Amplify: $e');
  }
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return Authenticator(
      child: MaterialApp(
        builder: Authenticator.builder(),
        debugShowCheckedModeBanner: false,
        // HomePage is out of scope.
        home: HomePage(),
        theme: ThemeData(
          primarySwatch: Colors.yellow,
        ),
      ),
    );
  }
}

HomePage.dart Example

import "package:amplify_authenticator/amplify_authenticator.dart";
import "package:flutter/material.dart";

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _selectedIndex = 0;

  void _navigate(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  // Out of scope.
  final List _pages = [
    CounterPage(),
    InputPage(),
    ToDoPage(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(

      appBar: AppBar(
        // To add the cognito sign out button.
        actions: [
          SignOutButton(),
        ],
      ),
      // Out of scope
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _selectedIndex,
        onTap: _navigate,
        items: [
          BottomNavigationBarItem(
            label: "Counter",
            icon: Icon(Icons.plus_one),
          ),
          BottomNavigationBarItem(
            label: "Input",
            icon: Icon(Icons.input),
          ),
          BottomNavigationBarItem(
            label: "Todo",
            icon: Icon(Icons.list),
          )
        ],
      ),
      body: _pages[_selectedIndex],
    );
  }
}

Update this file: ios/Podfile

Uncomment the following line and set the version to at least 13.0 (or like the error mesage shows if any.)

# Uncomment this line to define a global platform for your project
platform :ios, '13.0'

Results

Using this simple implementation you get an email/password implementation using AWS Cognito User Pool, meaning that you get out of the box the following features:

Screenshots

References

Getting the tokens (id token)

import "package:amplify_flutter/amplify_flutter.dart";

// ...

Future<void> whoAmI() async {
  final authSession = await Amplify.Auth.fetchAuthSession();

  setState(() {
    // Here you can use either accessToken or idToken.
    // use accessToken if you want to verify a scope and a client id
    token = (authSession.toJson()["userPoolTokens"] as CognitoUserPoolTokens)
        .accessToken
        .raw;

    _controller.text = token;
  });
}

// ...

Button(
  onPressed: whoAmI,
  name: "Click me",
),

// ...

To be able to copy the accessToken for testing purposes. That accessToken will be use to call a custom backend (built with Deno/Hono)

SizedBox(
  child: SingleChildScrollView(
    child: TextField(controller: _controller),
  ),
),

Deno Backend

Using your custom backend service, you can simply create a middleware that does the following:

Documentation: https://www.npmjs.com/package/aws-jwt-verify

import { CognitoJwtVerifier } from "npm:aws-jwt-verify";

// Verifier that expects valid access tokens:
const verifier = CognitoJwtVerifier.create({
  userPoolId: "<USER_POOL_ID>",
  tokenUse: "access", // can be id or access token, be sure that you use the same in the application code as well.
  clientId: "<USER_POOL_CLIENT_ID>",
});

try {
  const payload = await verifier.verify(
    "<ID_TOKEN_RECEIVED_IN_AUTHORIZATION_HEADER>" // the JWT as string
  );
  console.log("Token is valid. Payload:", payload);
} catch {
  console.log("Token not valid!");
}

You can also add the groups and scope

This implementation verifies if the token received is valid and will ensure that the user is authenticated.


Search