Flutter Authentication with Auth0

Flutter Authentication with Auth0

·

32 min read

Prerequisites

Tools required to follow the article.

  • Flutter SDK.
  • Android Studio or VS Code.
  • Emulator or an Android device
  • Background knowledge of Flutter and Dart

If you have checked out the official documentation of Auth0 you will find out that there is no SDK for flutter yet, I have a positive feeling it might come in later. But nothing to worry Flutter developers are not left out from using their services, hence Auth0 has an official package that gives you access to their product service.

Setup

Building UI is not our main focus, So please clone the started code here So let’s get into the real business

Auth0

Auth0 is a flexible, drop-in solution to add authentication and authorization services to your applications. Your team and organization can avoid the cost, time, and risk that comes with building your own solution to authenticate and authorize users. Lets get started by installing the Auth0 package form the package manager, Open the clone code in our favourite text editor, go to ourpubspec.yaml` and add the following code to our dependency

flutter_auth0: ^0.5.0

Time to write some code One more thing before we write code.

Auth0 Account Setup

In other to use the Auth0 service we will have to create an account if we already have an account, Login head over to our dashboard. Click on the Create Application and give our app a name and select Native as the app type. Head over to the setting and copy the Domain and ClientId and fill them in the constant.dart respectively. Lastly, Scroll down and click on the Show Advance Settings, on the Grant Types ensure to check the Password checkbox.

Signup

Alright at this point I believe we have an account and created our application with Auth0, with let’s head on to implementing signup. Open the signup.dart on your editor import constant.dart and the Auth0 package we installed earlier by adding the following code at the top of our signup.dart

import 'package:flutter_auth0/flutter_auth0.dart';
import 'constant.dart';

Let’s declare a variable with an instance of Auth0 which is from the package and also declare it inside an initState method. After the password variable, let’s add the code below.

Auth0 auth;
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    Auth0 auth = Auth0(baseUrl: 'https://$domain/', clientId: clientId);
  }

Let’s create a method to handle the signup process by adding the code above our build method:

signup({String email, String password}) async {
    try {
      await auth.auth.createUser({
        'email': email,
        'password': password,
        'connection': 'Username-Password-Authentication'
      });
    } catch (e) {
      print(e);
    }
  }

Before calling the signup function let us handle errors and events. First, let’s create a boolean variable and set it false. Just after the declaration of the auth variable let’s add the following code:

  • bool busy = false;

Also, let’s update the Sign button with the following code:

InkWell(
  onTap: null,
  child: Container(
    height: 40,
    decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(5),
      color: Colors.blue,
    ),
    child: Center(
      child: !busy
          ? Text(
              "Signup",
              style: TextStyle(
                color: Colors.white,
                fontSize: 20,
                fontWeight: FontWeight.bold,
              ),
            )
          : CircularProgressIndicator(
              strokeWidth: 2,
              valueColor:
                  AlwaysStoppedAnimation<Color>(Colors.white),
            ),
    ),
  ),
),

Also, let’s update our signup method with the following code

signup({String email, String password}) async {
    try {
      setState(() {
        busy = true;
      });
      await auth.auth.createUser({
        'email': email,
        'password': password,
        'connection': 'Username-Password-Authentication'
      });
      setState(() {
        busy = false;
      });
      _scaffoldKey.currentState.showSnackBar(
        SnackBar(
          backgroundColor: Colors.green,
          content: new Text("$email has successfully logged in"),
        ),
      );
    } catch (e) {
      setState(() {
        busy = false;
      });
      _scaffoldKey.currentState.showSnackBar(
        SnackBar(
          backgroundColor: Colors.red,
          content: new Text(e),
        ),
      );
      print(e);
    }
  }

Now we have to call our signup method immediately the signup button is clicked, we can achieve by updating the onTapparameter with the code below:

onTap: () async {
  await signup(
    email: emailController.text,
    password: passwordController.text,
  );
},

our signup.dart should look similar to the code below

import 'package:auth0/login.dart';
import 'package:flutter/material.dart';
import 'package:flutter_auth0/flutter_auth0.dart';
import 'constant.dart';

class Signup extends StatefulWidget {
  @override
  _SignupState createState() => _SignupState();
}

class _SignupState extends State<Signup> {
  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
  final emailController = TextEditingController();

  final passwordController = TextEditingController();

  Auth0 auth;
  bool busy = false;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    Auth0 auth = Auth0(baseUrl: 'https://$domain/', clientId: clientId);
  }

  signup({String email, String password}) async {
    try {
      setState(() {
        busy = true;
      });
      await auth.auth.createUser({
        'email': email,
        'password': password,
        'connection': 'Username-Password-Authentication'
      });
      setState(() {
        busy = false;
      });
      _scaffoldKey.currentState.showSnackBar(
        SnackBar(
          backgroundColor: Colors.green,
          content: new Text("$email has successfully logged in"),
        ),
      );
    } catch (e) {
      setState(() {
        busy = false;
      });
      _scaffoldKey.currentState.showSnackBar(
        SnackBar(
          backgroundColor: Colors.red,
          content: new Text(e),
        ),
      );
      print(e);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(
        title: Text("Signup"),
      ),
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 20.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                "Signup",
                style: TextStyle(
                  fontSize: 36,
                  fontWeight: FontWeight.bold,
                  color: Colors.black,
                ),
              ),
              SizedBox(height: 20),
              TextField(
                controller: emailController,
                decoration: InputDecoration(
                  labelText: "Email",
                  border: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.black, width: 1.0),
                  ),
                  enabledBorder: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.black, width: 2.0),
                  ),
                  focusedBorder: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.black, width: 2.0),
                  ),
                ),
              ),
              SizedBox(height: 20),
              TextField(
                controller: passwordController,
                decoration: InputDecoration(
                  labelText: "Password",
                  border: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.black, width: 1.0),
                  ),
                  enabledBorder: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.black, width: 2.0),
                  ),
                  focusedBorder: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.black, width: 2.0),
                  ),
                ),
              ),
              SizedBox(height: 20),
              InkWell(
                onTap: () async {
                  await signup(
                    email: emailController.text,
                    password: passwordController.text,
                  );
                },
                child: Container(
                  height: 40,
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(5),
                    color: Colors.blue,
                  ),
                  child: Center(
                    child: !busy
                        ? Text(
                            "Signup",
                            style: TextStyle(
                              color: Colors.white,
                              fontSize: 20,
                              fontWeight: FontWeight.bold,
                            ),
                          )
                        : CircularProgressIndicator(
                            strokeWidth: 2,
                            valueColor:
                                AlwaysStoppedAnimation<Color>(Colors.white),
                          ),
                  ),
                ),
              ),
              SizedBox(height: 30),
              InkWell(
                onTap: () => Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => Login(),
                  ),
                ),
                child: Row(
                  children: [
                    Text(
                      "Already have Account? ",
                      style: TextStyle(
                        fontSize: 13,
                        color: Colors.black,
                      ),
                    ),
                    Text(
                      "Login",
                      style: TextStyle(
                        fontSize: 13,
                        color: Colors.blue,
                      ),
                    )
                  ],
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

Login

The login will be a replica of what we just did for signup on that it will have It’s on method instead of the signup method. So let’s go ahead and replicate what we did for signup in the login.dart and replace the signup method with the code below:

login({String email, String password}) async {
    try {
      setState(() {
        busy = true;
      });
      await auth.auth.passwordRealm({
        'username': email,
        'password': password,
        'realm': 'Username-Password-Authentication'
      });
      setState(() {
        busy = false;
      });
      _scaffoldKey.currentState.showSnackBar(
        SnackBar(
          backgroundColor: Colors.green,
          content: new Text("$email has successfully logged in"),
        ),
      );
    } catch (e) {
      setState(() {
        busy = false;
      });
      _scaffoldKey.currentState.showSnackBar(
        SnackBar(
          backgroundColor: Colors.red,
          content: new Text(e),
        ),
      );
      print(e);
    }
  }

and the login onTab :

onTap: () async {
  await login(
    email: emailController.text,
    password: passwordController.text,
  );
},

Our Login Should be similar to the code below:

import 'package:auth0/constant.dart';
import 'package:auth0/signup.dart';
import 'package:flutter/material.dart';
import 'package:flutter_auth0/flutter_auth0.dart';

class Login extends StatefulWidget {
  @override
  _LoginState createState() => _LoginState();
}

class _LoginState extends State<Login> {
  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();

  final emailController = TextEditingController();

  final passwordController = TextEditingController();
  Auth0 auth;
  bool busy = false;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    Auth0 auth = Auth0(baseUrl: 'https://$domain/', clientId: clientId);
  }

  login({String email, String password}) async {
    try {
      setState(() {
        busy = true;
      });
      await auth.auth.passwordRealm({
        'username': email,
        'password': password,
        'realm': 'Username-Password-Authentication'
      });
      setState(() {
        busy = false;
      });
      _scaffoldKey.currentState.showSnackBar(
        SnackBar(
          backgroundColor: Colors.green,
          content: new Text("$email has successfully logged in"),
        ),
      );
    } catch (e) {
      setState(() {
        busy = false;
      });
      _scaffoldKey.currentState.showSnackBar(
        SnackBar(
          backgroundColor: Colors.red,
          content: new Text(e),
        ),
      );
      print(e);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Login"),
      ),
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 20.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                "Login",
                style: TextStyle(
                  fontSize: 36,
                  fontWeight: FontWeight.bold,
                  color: Colors.black,
                ),
              ),
              SizedBox(height: 20),
              TextField(
                controller: emailController,
                decoration: InputDecoration(
                  labelText: "Email",
                  border: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.black, width: 1.0),
                  ),
                  enabledBorder: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.black, width: 2.0),
                  ),
                  focusedBorder: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.black, width: 2.0),
                  ),
                ),
              ),
              SizedBox(height: 20),
              TextField(
                controller: passwordController,
                decoration: InputDecoration(
                  labelText: "Password",
                  border: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.black, width: 1.0),
                  ),
                  enabledBorder: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.black, width: 2.0),
                  ),
                  focusedBorder: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.black, width: 2.0),
                  ),
                ),
              ),
              SizedBox(height: 20),
              InkWell(
                onTap: () async {
                  await login(
                    email: emailController.text,
                    password: passwordController.text,
                  );
                },
                child: Container(
                  height: 40,
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(5),
                    color: Colors.blue,
                  ),
                  child: Center(
                    child: !busy
                        ? Text(
                            "Login",
                            style: TextStyle(
                              color: Colors.white,
                              fontSize: 20,
                              fontWeight: FontWeight.bold,
                            ),
                          )
                        : CircularProgressIndicator(
                            strokeWidth: 2,
                            valueColor:
                                AlwaysStoppedAnimation<Color>(Colors.white),
                          ),
                  ),
                ),
              ),
              SizedBox(height: 30),
              InkWell(
                onTap: () => Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => Signup(),
                  ),
                ),
                child: Row(
                  children: [
                    Text(
                      "You are new? ",
                      style: TextStyle(
                        fontSize: 13,
                        color: Colors.black,
                      ),
                    ),
                    Text(
                      "Create new",
                      style: TextStyle(
                        fontSize: 13,
                        color: Colors.blue,
                      ),
                    )
                  ],
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

Feel free to share your thoughts.