Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Learn Authentication and Tokens | Real Data in UI
Flutter REST API Integration

bookAuthentication and Tokens

Authentication is a critical aspect of secure app development. APIs often require authentication to ensure that only authorized users can access certain resources or perform sensitive actions. Instead of sending a username and password with every request, APIs commonly use tokens, unique strings issued upon successful login, to identify and authorize users. This approach improves both security and convenience by allowing users to authenticate once and use the resulting token for subsequent requests.

main.dart

main.dart

copy
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
// main.dart import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Auth Demo', home: LoginScreen(), ); } } class LoginScreen extends StatefulWidget { @override State<LoginScreen> createState() => _LoginScreenState(); } class _LoginScreenState extends State<LoginScreen> { final _usernameController = TextEditingController(); final _passwordController = TextEditingController(); bool _loading = false; String? _error; Future<void> _login() async { setState(() { _loading = true; _error = null; }); final response = await http.post( Uri.parse('https://example.com/api/login'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({ 'username': _usernameController.text, 'password': _passwordController.text, }), ); if (response.statusCode == 200) { final data = jsonDecode(response.body); final token = data['token']; final prefs = await SharedPreferences.getInstance(); await prefs.setString('bearer_token', token); Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => HomeScreen()), ); } else { setState(() { _error = 'Login failed. Check your credentials.'; }); } setState(() { _loading = false; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Login')), body: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ TextField( controller: _usernameController, decoration: InputDecoration(labelText: 'Username'), ), TextField( controller: _passwordController, obscureText: true, decoration: InputDecoration(labelText: 'Password'), ), if (_error != null) Padding( padding: const EdgeInsets.only(top: 8), child: Text(_error!, style: TextStyle(color: Colors.red)), ), SizedBox(height: 16), ElevatedButton( onPressed: _loading ? null : _login, child: _loading ? CircularProgressIndicator() : Text('Login'), ), ], ), ), ); } } class HomeScreen extends StatelessWidget { Future<String?> _getToken() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('bearer_token'); } Future<String> _fetchProtectedData() async { final token = await _getToken(); final response = await http.get( Uri.parse('https://example.com/api/protected'), headers: { 'Authorization': 'Bearer $token', }, ); if (response.statusCode == 200) { return jsonDecode(response.body)['message']; } else { throw Exception('Failed to fetch protected data'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Home')), body: FutureBuilder<String>( future: _fetchProtectedData(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { return Center(child: Text('Error: {snapshot.error}')); } else { return Center(child: Text('Data: {snapshot.data}')); } }, ), ); } }
Note
Note

Never expose authentication tokens directly in your source code or version control. Always store tokens securely and retrieve them at runtime to reduce the risk of leaks and unauthorized access.

In the code above, when a user logs in, the app sends their credentials to the API and receives a bearer token in response. This token is stored using a secure local storage solution (SharedPreferences in this case). Every time the app makes a protected API call, it retrieves the token and attaches it to the Authorization header as Bearer <token>. This ensures that only authenticated users can access protected endpoints, and the token is never hardcoded or exposed in the source code.

question mark

Why should authentication tokens be stored securely in Flutter apps?

Select the correct answer

Everything was clear?

How can we improve it?

Thanks for your feedback!

SectionΒ 2. ChapterΒ 4

Ask AI

expand

Ask AI

ChatGPT

Ask anything or try one of the suggested questions to begin our chat

Suggested prompts:

Can you explain how to securely store tokens in a mobile app?

What are some best practices for handling token expiration?

How does the app handle token revocation or logout?

bookAuthentication and Tokens

Swipe to show menu

Authentication is a critical aspect of secure app development. APIs often require authentication to ensure that only authorized users can access certain resources or perform sensitive actions. Instead of sending a username and password with every request, APIs commonly use tokens, unique strings issued upon successful login, to identify and authorize users. This approach improves both security and convenience by allowing users to authenticate once and use the resulting token for subsequent requests.

main.dart

main.dart

copy
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
// main.dart import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Auth Demo', home: LoginScreen(), ); } } class LoginScreen extends StatefulWidget { @override State<LoginScreen> createState() => _LoginScreenState(); } class _LoginScreenState extends State<LoginScreen> { final _usernameController = TextEditingController(); final _passwordController = TextEditingController(); bool _loading = false; String? _error; Future<void> _login() async { setState(() { _loading = true; _error = null; }); final response = await http.post( Uri.parse('https://example.com/api/login'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({ 'username': _usernameController.text, 'password': _passwordController.text, }), ); if (response.statusCode == 200) { final data = jsonDecode(response.body); final token = data['token']; final prefs = await SharedPreferences.getInstance(); await prefs.setString('bearer_token', token); Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => HomeScreen()), ); } else { setState(() { _error = 'Login failed. Check your credentials.'; }); } setState(() { _loading = false; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Login')), body: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ TextField( controller: _usernameController, decoration: InputDecoration(labelText: 'Username'), ), TextField( controller: _passwordController, obscureText: true, decoration: InputDecoration(labelText: 'Password'), ), if (_error != null) Padding( padding: const EdgeInsets.only(top: 8), child: Text(_error!, style: TextStyle(color: Colors.red)), ), SizedBox(height: 16), ElevatedButton( onPressed: _loading ? null : _login, child: _loading ? CircularProgressIndicator() : Text('Login'), ), ], ), ), ); } } class HomeScreen extends StatelessWidget { Future<String?> _getToken() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('bearer_token'); } Future<String> _fetchProtectedData() async { final token = await _getToken(); final response = await http.get( Uri.parse('https://example.com/api/protected'), headers: { 'Authorization': 'Bearer $token', }, ); if (response.statusCode == 200) { return jsonDecode(response.body)['message']; } else { throw Exception('Failed to fetch protected data'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Home')), body: FutureBuilder<String>( future: _fetchProtectedData(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { return Center(child: Text('Error: {snapshot.error}')); } else { return Center(child: Text('Data: {snapshot.data}')); } }, ), ); } }
Note
Note

Never expose authentication tokens directly in your source code or version control. Always store tokens securely and retrieve them at runtime to reduce the risk of leaks and unauthorized access.

In the code above, when a user logs in, the app sends their credentials to the API and receives a bearer token in response. This token is stored using a secure local storage solution (SharedPreferences in this case). Every time the app makes a protected API call, it retrieves the token and attaches it to the Authorization header as Bearer <token>. This ensures that only authenticated users can access protected endpoints, and the token is never hardcoded or exposed in the source code.

question mark

Why should authentication tokens be stored securely in Flutter apps?

Select the correct answer

Everything was clear?

How can we improve it?

Thanks for your feedback!

SectionΒ 2. ChapterΒ 4
some-alt