Authentication 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
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}')); } }, ), ); } }
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.
Thanks for your feedback!
Ask AI
Ask AI
Ask anything or try one of the suggested questions to begin our chat
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?
Awesome!
Completion rate improved to 6.67
Authentication 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
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}')); } }, ), ); } }
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.
Thanks for your feedback!