Ga naar hoofdinhoud
FDC • MBO Utrecht

Using API Authentication Tokens

Les 21/26 5 min leestijd
Automatisch afspelen

Now that our authentication is completed, we can work on consuming API tokens within our application. Tokens are important to limit access to our application.

In this lesson, we will:

  • Store the token in our Auth Provider
  • Add Headers to our API requests - including the token
  • Refactor our API Service to use the token
  • And some other minor changes

Let's start by refactoring our API service to use the token.


Refactoring API Service - Consuming API Tokens

Currently, we have no way to pass the token to our API service, so let's add that:

Note: We will declare the token as a [non-nullable variable] using the late keyword (https://dart.dev/language/variables#late-variables).

lib/services/api.dart

class ApiService {
ApiService();
late String token;
 
ApiService(String token) {
this.token = token;
}
}

Next, we need to use that token by adding headers to our API requests:

lib/services/api.dart

// ...
 
Future<List<Category>> fetchCategories() async {
final http.Response response =
await http.get(Uri.parse('$baseUrl/api/categories'));
final http.Response response = await http
.get(Uri.parse('$baseUrl/api/categories'), headers: <String, String>{
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer $token'
});
// ...
}
 
Future saveCategory(Category category) async {
String url = '$baseUrl/api/categories/${category.id}';
final http.Response response = await http.put(
Uri.parse(url),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer $token'
},
body: jsonEncode(<String, String>{
'name': category.name,
}),
);
// ...
}
 
Future<void> deleteCategory(id) async {
String url = '$baseUrl/api/categories/$id';
final http.Response response = await http.delete(
Uri.parse(url),
);
final http.Response response = await http.delete(Uri.parse(url), headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer $token'
});
 
// ...
}
 
Future addCategory(String name) async {
String url = '$baseUrl/api/categories';
final http.Response response = await http.post(
Uri.parse(url),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer $token'
},
body: jsonEncode(<String, String>{
'name': name,
}),
);
 
// ...
}
 
// ...

Updating Category Provider

Now that we have the token in our API service, we can update our Category Provider to use the token:

import 'package:laravel_api_flutter_app/providers/auth_provider.dart';
 
// ...
 
class CategoryProvider extends ChangeNotifier {
List<Category> categories = [];
late ApiService apiService;
late AuthProvider authProvider;
 
CategoryProvider() {//
apiService = ApiService();
CategoryProvider(AuthProvider authProvider) {
this.authProvider = authProvider;
apiService = ApiService(authProvider.token);
init();
}
 
// ...
}

Updating Registration and Login

We have to fix one thing that we left out - we are storing the token as a response from our Login/Register functions:

lib/screens/auth/Login.dart

// ...
 
try {
String token = await provider.login(
await provider.login(
emailController.text, passwordController.text, deviceName);
} catch (Exception) {
 
// ...

And:

lib/screens/auth/Register.dart

// ...
 
try {
String token = await provider.register(
await provider.register(
nameController.text,
emailController.text,
 
// ...

While this might work even without the changes, it is better to store the token in the AuthProvider and use it from there.


Removing API Service From Add/Edit Category

We have some redundant code on our Add/Edit Category pages, as we did with Auth pages. We can remove the API service from there:

lib/widgets/category_add.dart

import 'package:laravel_api_flutter_app/services/api.dart';
 
// ...
 
class CategoryAddState extends State<CategoryAdd> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final categoryNameController = TextEditingController();
ApiService apiService = ApiService();
String errorMessage = '';
 
// ...

And:

lib/widgets/category_edit.dart

import 'package:laravel_api_flutter_app/services/api.dart';
 
// ...
 
class CategoryEditState extends State<CategoryEdit> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final categoryNameController = TextEditingController();
ApiService apiService = ApiService();
String errorMessage = '';
 
// ...

Modifying How Auth Provider is Registered

The last change we have to do is to modify how we register our AuthProvider in our main.dart file:

// ...
return MultiProvider(
providers: [
ChangeNotifierProvider<CategoryProvider>(
create: (context) => CategoryProvider()),
create: (context) => CategoryProvider(authProvider)),
ChangeNotifierProvider<AuthProvider>(
create: (context) => AuthProvider()),
],
child: MaterialApp(title: 'Welcome to Flutter', routes: {
'/': (context) {
final authProvider = Provider.of<AuthProvider>(context);
return authProvider.isAuthenticated ? Home() : Login();
return authProvider.isAuthenticated ? CategoriesList() : Login();
},

Updating our Auth Provider

We need to update our AuthProvider to store the token and use it in our API service:

lib/providers/auth_provider.dart

import 'package:flutter/material.dart';
import 'package:laravel_api_flutter_app/services/api.dart';
 
class AuthProvider extends ChangeNotifier {
bool isAuthenticated = false;
late String token;
ApiService apiService = ApiService();
ApiService apiService = ApiService('');
 
AuthProvider();
 
Future<String> register(String name, String email, String password,
Future<void> register(String name, String email, String password,
String password_confirmation, String device_name) async {
String token = await apiService.register(
token = await apiService.register(
name, email, password, password_confirmation, device_name);
isAuthenticated = true;
notifyListeners();
}
 
Future<String> login(String email, String password, String device_name) async {
Future<void> login(String email, String password, String device_name) async {
String token = await apiService.login(email, password, device_name);
token = await apiService.login(email, password, device_name);
isAuthenticated = true;
notifyListeners();
}
}

Laravel Changes

We have to make some changes to our Laravel application to make it work with API tokens. First, we need to add the sanctum middleware to our API routes:

routes/api.php

// ...
 
Route::group([], function () {
Route::group(['middleware' => 'auth:sanctum'], function () {
//...

And restore the CategoryController create() method:

app/Http/Controllers/CategoryController.php

// ...
 
$category = Category::create($request->validated());
$category = auth()->user()->categories()->create($request->validated());
 
// ...

That's it - we should be able to log in normally and use the API token to access our API.


In the next lesson, we will add a bottom navigation bar to navigate between multiple pages like Categories, Transactions, and Home.


Check out the GitHub Commit for this lesson.