Skip to content

Added JWT Auth feature #657

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions lib/utils/api_client.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'jwt_utils.dart';

class ApiClient {
final String baseUrl;

ApiClient({required this.baseUrl});

// Get request headers with JWT authentication
Future<Map<String, String>> _getAuthHeaders() async {
final token = await JwtUtils.getToken();
return {
'Content-Type': 'application/json',
if (token != null) 'Authorization': 'Bearer $token',
};
}

// GET request with authentication
Future<http.Response> get(String endpoint) async {
return http.get(
Uri.parse('$baseUrl/$endpoint'),
headers: await _getAuthHeaders(),
);
}

// POST request with authentication
Future<http.Response> post(String endpoint, Map<String, dynamic> body) async {
return http.post(
Uri.parse('$baseUrl/$endpoint'),
headers: await _getAuthHeaders(),
body: jsonEncode(body),
);
}

// PUT request with authentication
Future<http.Response> put(String endpoint, Map<String, dynamic> body) async {
return http.put(
Uri.parse('$baseUrl/$endpoint'),
headers: await _getAuthHeaders(),
body: jsonEncode(body),
);
}

// DELETE request with authentication
Future<http.Response> delete(String endpoint) async {
return http.delete(
Uri.parse('$baseUrl/$endpoint'),
headers: await _getAuthHeaders(),
);
}
}
50 changes: 50 additions & 0 deletions lib/utils/auth_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../utils/jwt_utils.dart';

class AuthService {
final String baseUrl;

AuthService({required this.baseUrl});

// Login and store token
Future<bool> login(String username, String password) async {
try {
final response = await http.post(
Uri.parse('$baseUrl/login'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'username': username,
'password': password,
}),
);

if (response.statusCode == 200) {
final data = jsonDecode(response.body);
final token = data['token'];
if (token != null) {
await JwtUtils.saveToken(token);
return true;
}
}
} catch (e) {
print("Login error: $e");
}
return false;
}

// Logout (delete token)
Future<void> logout() async {
await JwtUtils.deleteToken();
}

// Check if the user is authenticated
Future<bool> isAuthenticated() async {
return await JwtUtils.isAuthenticated();
}

// Retrieve user details from the stored JWT
Future<Map<String, dynamic>> getUserInfo() async {
return await JwtUtils.getPayload();
}
}
18 changes: 16 additions & 2 deletions lib/utils/http_utils.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import 'package:apidash_core/apidash_core.dart';
import '../consts.dart';
import '../utils/jwt_utils.dart'; // Import JWT utilities

String getRequestTitleFromUrl(String? url) {
if (url == null || url.trim() == "") {
if (url == null || url.trim().isEmpty) {
return kUntitled;
}
if (url.contains("://")) {
String rem = url.split("://")[1];
if (rem.trim() == "") {
if (rem.trim().isEmpty) {
return kUntitled;
}
return rem;
Expand Down Expand Up @@ -48,3 +49,16 @@ String getRequestTitleFromUrl(String? url) {
}
return (kNoBodyViewOptions, null);
}

// Utility function to get authorization header for API requests
Future<Map<String, String>> getAuthHeaders() async {
final token = await JwtUtils.getToken();
final headers = <String, String>{
'Content-Type': 'application/json',
};

if (token != null) {
headers['Authorization'] = 'Bearer $token';
}
return headers;
}
61 changes: 61 additions & 0 deletions lib/utils/jwt_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import 'package:flutter/foundation.dart' show kIsWeb, defaultTargetPlatform, TargetPlatform;
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:jwt_decoder/jwt_decoder.dart';

class JwtUtils {
static const String _tokenKey = 'jwt_token';

// Determine if the platform is mobile (Android/iOS)
static bool _isMobile() {
return defaultTargetPlatform == TargetPlatform.android ||
defaultTargetPlatform == TargetPlatform.iOS;
}

// Save JWT token securely
static Future<void> saveToken(String token) async {
if (kIsWeb || !_isMobile()) {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_tokenKey, token);
} else {
const storage = FlutterSecureStorage();
await storage.write(key: _tokenKey, value: token);
}
}

// Retrieve stored token
static Future<String?> getToken() async {
if (kIsWeb || !_isMobile()) {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(_tokenKey);
} else {
const storage = FlutterSecureStorage();
return await storage.read(key: _tokenKey);
}
}

// Delete token when logging out
static Future<void> deleteToken() async {
if (kIsWeb || !_isMobile()) {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_tokenKey);
} else {
const storage = FlutterSecureStorage();
await storage.delete(key: _tokenKey);
}
}

// Check if a stored token is valid (not expired)
static Future<bool> isAuthenticated() async {
final token = await getToken();
if (token == null) return false;
return !JwtDecoder.isExpired(token);
}

// Extract payload data from the token
static Future<Map<String, dynamic>> getPayload() async {
final token = await getToken();
if (token == null) return {};
return JwtDecoder.decode(token);
}
}
Loading