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
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:
User pool ID
available on the Overview Page.Client ID
available on the App Clients Page.region
represents the region you created the User Pool in.Identity Pool ID
is the same as theUser pool ID
.
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:
- Power of AWS and all of its services
- Sign In
- Sign Out
- Sign Up
- Email Verification (code sent by email)
- Lost / Recover Password
- And much more (take a look at AWS documentation to configure all the possible features with cognito.)
Screenshots
References
- https://ui.docs.amplify.aws/flutter/connected-components/authenticator/configuration
- https://ui.docs.amplify.aws/flutter/connected-components/authenticator
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.