How to Create a Secure Login Page with Keycloak and Flutter using flutter_appauth Package

How to Create a Secure Login Page with Keycloak and Flutter using flutter_appauth Package

Integrating a robust authentication system in your mobile application is crucial for security and user management. Keycloak, an open-source identity and access management solution, simplifies this process by providing comprehensive authentication and authorization capabilities. In this article, we will walk you through creating a secure login page in a Flutter application using the flutter_appauth package and Keycloak. This step-by-step guide covers everything from setting up a Keycloak realm and client to configuring your Flutter app for authentication.

Introduction to Keycloak and Flutter AppAuth

Keycloak

Keycloak is an open-source identity and access management solution that provides features such as single sign-on (SSO), user federation, identity brokering, and social login. It simplifies securing applications by handling user authentication and authorization.

Flutter AppAuth

flutter_appauth is a Flutter wrapper for the AppAuth library, which provides SDKs for communicating with OAuth 2.0 and OpenID Connect providers. This package allows Flutter apps to authenticate users using external identity providers.

Setting Up Keycloak

Installing Keycloak

  1. Download Keycloak: Visit the Keycloak download page and download the latest version.

  2. Extract the Archive: Extract the downloaded archive to a preferred directory.

  3. Start Keycloak: Navigate to the Keycloak bin directory and start the server:

     cd keycloak/bin
     ./standalone.sh
    
  4. Access Keycloak Admin Console: Open your browser and navigate to http://localhost:8080/auth. Log in using the default admin credentials and create a new admin user.

Creating a Realm

  1. Log in to the Admin Console: Navigate to http://localhost:8080/auth/admin and log in.

  2. Create a New Realm:

    • Click on the drop-down menu in the top-left corner.

    • Click on Add realm.

    • Enter a name for the new realm (e.g., myrealm) and click Create.

Creating a Client

  1. Navigate to Clients:

    • In the new realm, click on Clients in the left sidebar.

    • Click Create.

  2. Configure the Client:

    • Client ID: Enter a client ID (e.g., flutter-client).

    • Client Protocol: Ensure it is set to openid-connect.

    • Root URL: Enter the root URL of your Flutter app (e.g., http://localhost:8080 for testing).

    • Click Save.

  3. Client Settings:

    • Access Type: Set to public.

    • Valid Redirect URIs: Enter the redirect URI for your app (e.g., com.example.app:/oauthredirect for mobile apps).

    • Web Origins: Enter * or the specific origins that are allowed.

    • Click Save.

  4. Credentials: Note the Client Secret if the client is configured as confidential.

Setting Up Flutter Project

Installing flutter_appauth

  1. Create a Flutter Project:

     flutter create myapp
     cd myapp
    
  2. Add Dependencies: Open pubspec.yaml and add flutter_appauth and flutter_secure_storage (for securely storing tokens):

     dependencies:
       flutter:
         sdk: flutter
       flutter_appauth: ^0.9.1+2
       flutter_secure_storage: ^5.0.2
    
  3. Install Dependencies:

     flutter pub get
    

Configuring OAuth2 Settings

  1. Android Configuration:

    • Open android/app/src/main/AndroidManifest.xml and add the intent filter:
    <activity
        android:name="com.linusu.flutter_appauth.CallbackActivity"
        android:exported="true">
      <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="com.example.app" android:host="oauthredirect" />
      </intent-filter>
    </activity>
  1. iOS Configuration:

    • Open ios/Runner/Info.plist and add the URL types:
    <key>CFBundleURLTypes</key>
    <array>
      <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLSchemes</key>
        <array>
          <string>com.example.app</string>
        </array>
      </dict>
    </array>

Implementing the Login Page

  1. Main Dart File (lib/main.dart):

     import 'package:flutter/material.dart';
     import 'package:flutter_appauth/flutter_appauth.dart';
     import 'package:flutter_secure_storage/flutter_secure_storage.dart';
    
     void main() => runApp(MyApp());
    
     class MyApp extends StatelessWidget {
       @override
       Widget build(BuildContext context) {
         return MaterialApp(
           title: 'Flutter AppAuth Example',
           theme: ThemeData(
             primarySwatch: Colors.blue,
           ),
           home: LoginPage(),
         );
       }
     }
    
     class LoginPage extends StatefulWidget {
       @override
       _LoginPageState createState() => _LoginPageState();
     }
    
     class _LoginPageState extends State<LoginPage> {
       final FlutterAppAuth _appAuth = FlutterAppAuth();
       final FlutterSecureStorage _secureStorage = FlutterSecureStorage();
    
       final String _clientId = 'flutter-client';
       final String _redirectUri = 'com.example.app:/oauthredirect';
       final String _issuer = 'http://localhost:8080/auth/realms/myrealm';
    
       @override
       Widget build(BuildContext context) {
         return Scaffold(
           appBar: AppBar(
             title: Text('Login'),
           ),
           body: Center(
             child: ElevatedButton(
               onPressed: _login,
               child: Text('Login with Keycloak'),
             ),
           ),
         );
       }
    
       Future<void> _login() async {
         try {
           final AuthorizationTokenResponse? result = await _appAuth.authorizeAndExchangeCode(
             AuthorizationTokenRequest(
               _clientId,
               _redirectUri,
               issuer: _issuer,
               scopes: ['openid', 'profile', 'email'],
             ),
           );
    
           if (result != null) {
             await _secureStorage.write(key: 'access_token', value: result.accessToken);
             await _secureStorage.write(key: 'refresh_token', value: result.refreshToken);
             print('Login successful: ${result.accessToken}');
           }
         } catch (e) {
           print('Login error: $e');
         }
       }
     }
    

Conclusion

Setting up a secure login page with Keycloak and Flutter using the flutter_appauth package is a powerful way to manage authentication in your mobile applications. Keycloak provides a comprehensive and flexible identity management solution, while flutter_appauth simplifies the integration process. By following the steps outlined in this guide, you can implement a robust authentication system that enhances security and user experience.

Start integrating Keycloak with your Flutter applications today to leverage the benefits of centralized identity management and secure authentication. Happy coding!