pbp_django_auth_extended

Forked from pbp_django_auth. A Flutter package for helping students to implement authentication from Django web service in Flutter. This forked (extended) package adds features that is not present in pbp_django_auth packages.

Getting Started

Django's Part

To use the package, you need to make asynchronous JavaScript (AJAX) login view in your Django project.

  1. Run python manage.py startapp authentication to make a new app module for handling the AJAX login.

  2. Add "authentication" to INSTALLED_APPS in settings.py.

  3. Run pip install django-cors-headers to install the required library.

  4. Add "corsheaders" to INSTALLED_APPS in settings.py.

  5. Add "corsheaders.middleware.CorsMiddleware" to MIDDLEWARE in settings.py.

  6. Create a new variable in settings.py called CORS_ALLOW_ALL_ORIGINS and set the value to True (CORS_ALLOW_ALL_ORIGINS=True).

  7. Create a new variable in settings.py called CORS_ALLOW_CREDENTIALS and set the value to True, (CORS_ALLOW_CREDENTIALS=True).

  8. Create the following variables in settings.py.

    CSRF_COOKIE_SECURE = True
    SESSION_COOKIE_SECURE = True
    CSRF_COOKIE_SAMESITE = 'None'
    SESSION_COOKIE_SAMESITE = 'None'
    
  9. Create a login view method in authentication/views.py.

    Example Login View

    from django.shortcuts import render
    from django.contrib.auth import authenticate, login as auth_login
    from django.http import JsonResponse
    from django.views.decorators.csrf import csrf_exempt
    
    @csrf_exempt
    def login(request):
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(username=username, password=password)
        if user is not None:
            if user.is_active:
                auth_login(request, user)
                # Redirect to a success page.
                return JsonResponse({
                  "status": True,
                  "message": "Successfully Logged In!"
                  # Insert any extra data if you want to pass data to Flutter
                }, status=200)
            else:
                return JsonResponse({
                  "status": False,
                  "message": "Failed to Login, Account Disabled."
                }, status=401)
    
        else:
            return JsonResponse({
              "status": False,
              "message": "Failed to Login, check your email/password."
            }, status=401)
    

    This view will set cookies to the user and allow authenticated requests with @login_required decorator.

  10. Create a check_is_anonymous view method in authentication/views.py.

    def check_is_anonymous(request):
      print(request.user.is_anonymous)
      return JsonResponse({
          "anonymous": request.user.is_anonymous
      }, status=200)
    
  11. Add url to authentication/urls.py

    path('is-anonymous/', check_is_anonymous, name='check_is_anonymous'),
    

Flutter's Part

To use the package, modify application root widget to provide the CookieRequest library to all child widgets by using Provider.

For example, if the previous app initialization was:

class MyApp extends StatelessWidget {
    const MyApp({Key? key}) : super(key: key);
    
    @override
    Widget build(BuildContext context) {
        return MaterialApp(
            title: 'Flutter App',
            theme: ThemeData(
                primarySwatch: Colors.blue,
            ),
            home: const MyHomePage(title: 'Flutter App'),
            routes: {
                "/login": (BuildContext context) => const LoginPage(),
            },
        );
    }
}

Change it to:

class MyApp extends StatelessWidget {
    const MyApp({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
        return Provider(
            create: (_) {
                CookieRequest request = CookieRequest(baseUrl: <DJANGO BASE URL>); // DO NOT use trailing '/' here
                return request;
            },
            child: MaterialApp(
                title: 'Flutter App',
                theme: ThemeData(
                    primarySwatch: Colors.blue,
                ),
                home: const MyHomePage(title: 'Flutter App'),
                routes: {
                    "/login": (BuildContext context) => const LoginPage(),
                },
            ),
        );
    }
}

This creates a new Provider object that will share the CookieRequest instance with all components in the application.

Usage

To use the package in your project, follow these steps below.

  1. Import the Provider library and this package to the component.

    import 'package:provider/provider.dart';
    import 'package:pbp_django_auth/pbp_django_auth.dart';
    ...
    
  2. Instantiate the request object by calling context.watch in the Widget build(BuildContext context) function.

    Example

    class _LoginPageState extends State<LoginPage> {
      final _loginFormKey = GlobalKey<FormState>();
      bool isPasswordVisible = false;
      void togglePasswordView() {
        setState(() {
          isPasswordVisible = !isPasswordVisible;
        });
      }
    
      String username = "";
      String password1 = "";
      @override
      Widget build(BuildContext context) {
        final request = context.watch<CookieRequest>();
        // The rest of your widgets are down below
        ...
      }
    }
    
  3. To log in using the package, use the request.login(url, data) method.

      // 'username' and 'password' should be the values of the user login form.
      final response = await request.login("/auth/login", {
        'username': username,
        'password': password1,
      });
      if (request.loggedIn) {
        // Code here will run if the login succeeded.
      } else {
        // Code here will run if the login failed (wrong username/password).
      }
    
  4. To fetch or insert data using the library, use the request.get(url) or request.post(url, data) method.

    /* GET request example: */
    final response = await request.get(<PATH TO ACCESS (DO NOT INCLUDE BASE URL)>);
    // The returned response will be a Map object with the keys of the JsonResponse
       
    /* POST request example: */
    final response = await request.post(<PATH TO ACCESS (DO NOT INCLUDE BASE URL)>, {
      "data1": "THIS IS EXAMPLE DATA",
      "data2": "THIS IS EXAMPLE DATA 2",
    });
    // The data argument should be the keys of the Django form.
    // The returned response will be a Map obejct with the keys of JsonResponse.
    

    You can also use request.postJson(url, encodedJsonData) with jsonEncode function from 'dart:convert' library to send the submitted data without manually converting the data into JSON format one by one.

Contributors