diff --git a/lib/services/storage.dart b/lib/services/storage.dart
index 23d09224..7b5247f0 100644
--- a/lib/services/storage.dart
+++ b/lib/services/storage.dart
@@ -1,215 +1,266 @@
 part of appwrite;
 
-    /// The Storage service allows you to manage your project files.
+/// The Storage service allows you to manage your project files.
 class Storage extends Service {
-    Storage(super.client);
-
-    /// List Files
-    ///
-    /// Get a list of all the user files. You can use the query params to filter
-    /// your results.
-    ///
-    Future<models.FileList> listFiles({required String bucketId, List<String>? queries, String? search}) async {
-        final String path = '/storage/buckets/{bucketId}/files'.replaceAll('{bucketId}', bucketId);
-
-        final Map<String, dynamic> params = {
-            'queries': queries,
-            'search': search,
-        };
-
-        final Map<String, String> headers = {
-            'content-type': 'application/json',
-        };
-
-        final res = await client.call(HttpMethod.get, path: path, params: params, headers: headers);
-
-        return models.FileList.fromMap(res.data);
-
-    }
-
-    /// Create File
-    ///
-    /// Create a new file. Before using this route, you should create a new bucket
-    /// resource using either a [server
-    /// integration](/docs/server/storage#storageCreateBucket) API or directly from
-    /// your Appwrite console.
-    /// 
-    /// Larger files should be uploaded using multiple requests with the
-    /// [content-range](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Range)
-    /// header to send a partial request with a maximum supported chunk of `5MB`.
-    /// The `content-range` header values should always be in bytes.
-    /// 
-    /// When the first request is sent, the server will return the **File** object,
-    /// and the subsequent part request must include the file's **id** in
-    /// `x-appwrite-id` header to allow the server to know that the partial upload
-    /// is for the existing file and not for a new one.
-    /// 
-    /// If you're creating a new file using one of the Appwrite SDKs, all the
-    /// chunking logic will be managed by the SDK internally.
-    /// 
-    ///
-    Future<models.File> createFile({required String bucketId, required String fileId, required InputFile file, List<String>? permissions, Function(UploadProgress)? onProgress}) async {
-        final String path = '/storage/buckets/{bucketId}/files'.replaceAll('{bucketId}', bucketId);
-
-        final Map<String, dynamic> params = {
-
-
-            'fileId': fileId,
-            'file': file,
-            'permissions': permissions,
-        };
-
-        final Map<String, String> headers = {
-            'content-type': 'multipart/form-data',
-        };
-
-        String idParamName = '';
-        idParamName = 'fileId';
-        final paramName = 'file';
-        final res = await client.chunkedUpload(
-            path: path,
-            params: params,
-            paramName: paramName,
-            idParamName: idParamName,
-            headers: headers,
-            onProgress: onProgress,
-          );
-
-        return models.File.fromMap(res.data);
-
-    }
-
-    /// Get File
-    ///
-    /// Get a file by its unique ID. This endpoint response returns a JSON object
-    /// with the file metadata.
-    ///
-    Future<models.File> getFile({required String bucketId, required String fileId}) async {
-        final String path = '/storage/buckets/{bucketId}/files/{fileId}'.replaceAll('{bucketId}', bucketId).replaceAll('{fileId}', fileId);
-
-        final Map<String, dynamic> params = {
-        };
-
-        final Map<String, String> headers = {
-            'content-type': 'application/json',
-        };
-
-        final res = await client.call(HttpMethod.get, path: path, params: params, headers: headers);
-
-        return models.File.fromMap(res.data);
-
-    }
-
-    /// Update File
-    ///
-    /// Update a file by its unique ID. Only users with write permissions have
-    /// access to update this resource.
-    ///
-    Future<models.File> updateFile({required String bucketId, required String fileId, List<String>? permissions}) async {
-        final String path = '/storage/buckets/{bucketId}/files/{fileId}'.replaceAll('{bucketId}', bucketId).replaceAll('{fileId}', fileId);
-
-        final Map<String, dynamic> params = {
-            'permissions': permissions,
-        };
-
-        final Map<String, String> headers = {
-            'content-type': 'application/json',
-        };
-
-        final res = await client.call(HttpMethod.put, path: path, params: params, headers: headers);
-
-        return models.File.fromMap(res.data);
-
-    }
-
-    /// Delete File
-    ///
-    /// Delete a file by its unique ID. Only users with write permissions have
-    /// access to delete this resource.
-    ///
-    Future deleteFile({required String bucketId, required String fileId}) async {
-        final String path = '/storage/buckets/{bucketId}/files/{fileId}'.replaceAll('{bucketId}', bucketId).replaceAll('{fileId}', fileId);
-
-        final Map<String, dynamic> params = {
-        };
-
-        final Map<String, String> headers = {
-            'content-type': 'application/json',
-        };
-
-        final res = await client.call(HttpMethod.delete, path: path, params: params, headers: headers);
-
-        return  res.data;
-
-    }
-
-    /// Get File for Download
-    ///
-    /// Get a file content by its unique ID. The endpoint response return with a
-    /// 'Content-Disposition: attachment' header that tells the browser to start
-    /// downloading the file to user downloads directory.
-    ///
-    Future<Uint8List> getFileDownload({required String bucketId, required String fileId}) async {
-        final String path = '/storage/buckets/{bucketId}/files/{fileId}/download'.replaceAll('{bucketId}', bucketId).replaceAll('{fileId}', fileId);
-
-        final Map<String, dynamic> params = {
-            
-            
-            'project': client.config['project'],
-        };
-
-        final res = await client.call(HttpMethod.get, path: path, params: params, responseType: ResponseType.bytes);
-        return res.data;
-    }
-
-    /// Get File Preview
-    ///
-    /// Get a file preview image. Currently, this method supports preview for image
-    /// files (jpg, png, and gif), other supported formats, like pdf, docs, slides,
-    /// and spreadsheets, will return the file icon image. You can also pass query
-    /// string arguments for cutting and resizing your preview image. Preview is
-    /// supported only for image files smaller than 10MB.
-    ///
-    Future<Uint8List> getFilePreview({required String bucketId, required String fileId, int? width, int? height, String? gravity, int? quality, int? borderWidth, String? borderColor, int? borderRadius, double? opacity, int? rotation, String? background, String? output}) async {
-        final String path = '/storage/buckets/{bucketId}/files/{fileId}/preview'.replaceAll('{bucketId}', bucketId).replaceAll('{fileId}', fileId);
-
-        final Map<String, dynamic> params = {
-            
-            'width': width,
-            'height': height,
-            'gravity': gravity,
-            'quality': quality,
-            'borderWidth': borderWidth,
-            'borderColor': borderColor,
-            'borderRadius': borderRadius,
-            'opacity': opacity,
-            'rotation': rotation,
-            'background': background,
-            'output': output,
-            
-            'project': client.config['project'],
-        };
-
-        final res = await client.call(HttpMethod.get, path: path, params: params, responseType: ResponseType.bytes);
-        return res.data;
-    }
-
-    /// Get File for View
-    ///
-    /// Get a file content by its unique ID. This endpoint is similar to the
-    /// download method but returns with no  'Content-Disposition: attachment'
-    /// header.
-    ///
-    Future<Uint8List> getFileView({required String bucketId, required String fileId}) async {
-        final String path = '/storage/buckets/{bucketId}/files/{fileId}/view'.replaceAll('{bucketId}', bucketId).replaceAll('{fileId}', fileId);
-
-        final Map<String, dynamic> params = {
-            
-            
-            'project': client.config['project'],
-        };
-
-        final res = await client.call(HttpMethod.get, path: path, params: params, responseType: ResponseType.bytes);
-        return res.data;
-    }
-}
\ No newline at end of file
+  Storage(super.client);
+
+  /// List Files
+  ///
+  /// Get a list of all the user files. You can use the query params to filter
+  /// your results.
+  ///
+  Future<models.FileList> listFiles(
+      {required String bucketId, List<String>? queries, String? search}) async {
+    final String path =
+        '/storage/buckets/{bucketId}/files'.replaceAll('{bucketId}', bucketId);
+
+    final Map<String, dynamic> params = {
+      'queries': queries,
+      'search': search,
+    };
+
+    final Map<String, String> headers = {
+      'content-type': 'application/json',
+    };
+
+    final res = await client.call(HttpMethod.get,
+        path: path, params: params, headers: headers);
+
+    return models.FileList.fromMap(res.data);
+  }
+
+  /// Create File
+  ///
+  /// Create a new file. Before using this route, you should create a new bucket
+  /// resource using either a [server
+  /// integration](/docs/server/storage#storageCreateBucket) API or directly from
+  /// your Appwrite console.
+  ///
+  /// Larger files should be uploaded using multiple requests with the
+  /// [content-range](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Range)
+  /// header to send a partial request with a maximum supported chunk of `5MB`.
+  /// The `content-range` header values should always be in bytes.
+  ///
+  /// When the first request is sent, the server will return the **File** object,
+  /// and the subsequent part request must include the file's **id** in
+  /// `x-appwrite-id` header to allow the server to know that the partial upload
+  /// is for the existing file and not for a new one.
+  ///
+  /// If you're creating a new file using one of the Appwrite SDKs, all the
+  /// chunking logic will be managed by the SDK internally.
+  ///
+  ///
+  Future<models.File> createFile(
+      {required String bucketId,
+      required String fileId,
+      required InputFile file,
+      List<String>? permissions,
+      Function(UploadProgress)? onProgress}) async {
+    final String path =
+        '/storage/buckets/{bucketId}/files'.replaceAll('{bucketId}', bucketId);
+
+    final Map<String, dynamic> params = {
+      'fileId': fileId,
+      'file': file,
+      'permissions': permissions,
+    };
+
+    final Map<String, String> headers = {
+      'content-type': 'multipart/form-data',
+    };
+
+    String idParamName = '';
+    idParamName = 'fileId';
+    final paramName = 'file';
+    final res = await client.chunkedUpload(
+      path: path,
+      params: params,
+      paramName: paramName,
+      idParamName: idParamName,
+      headers: headers,
+      onProgress: onProgress,
+    );
+
+    return models.File.fromMap(res.data);
+  }
+
+  /// Get File
+  ///
+  /// Get a file by its unique ID. This endpoint response returns a JSON object
+  /// with the file metadata.
+  ///
+  Future<models.File> getFile(
+      {required String bucketId, required String fileId}) async {
+    final String path = '/storage/buckets/{bucketId}/files/{fileId}'
+        .replaceAll('{bucketId}', bucketId)
+        .replaceAll('{fileId}', fileId);
+
+    final Map<String, dynamic> params = {};
+
+    final Map<String, String> headers = {
+      'content-type': 'application/json',
+    };
+
+    final res = await client.call(HttpMethod.get,
+        path: path, params: params, headers: headers);
+
+    return models.File.fromMap(res.data);
+  }
+
+  /// Update File
+  ///
+  /// Update a file by its unique ID. Only users with write permissions have
+  /// access to update this resource.
+  ///
+  Future<models.File> updateFile(
+      {required String bucketId,
+      required String fileId,
+      List<String>? permissions}) async {
+    final String path = '/storage/buckets/{bucketId}/files/{fileId}'
+        .replaceAll('{bucketId}', bucketId)
+        .replaceAll('{fileId}', fileId);
+
+    final Map<String, dynamic> params = {
+      'permissions': permissions,
+    };
+
+    final Map<String, String> headers = {
+      'content-type': 'application/json',
+    };
+
+    final res = await client.call(HttpMethod.put,
+        path: path, params: params, headers: headers);
+
+    return models.File.fromMap(res.data);
+  }
+
+  /// Delete File
+  ///
+  /// Delete a file by its unique ID. Only users with write permissions have
+  /// access to delete this resource.
+  ///
+  Future deleteFile({required String bucketId, required String fileId}) async {
+    final String path = '/storage/buckets/{bucketId}/files/{fileId}'
+        .replaceAll('{bucketId}', bucketId)
+        .replaceAll('{fileId}', fileId);
+
+    final Map<String, dynamic> params = {};
+
+    final Map<String, String> headers = {
+      'content-type': 'application/json',
+    };
+
+    final res = await client.call(HttpMethod.delete,
+        path: path, params: params, headers: headers);
+
+    return res.data;
+  }
+
+  /// Get File for Download
+  ///
+  /// Get a file content by its unique ID. The endpoint response return with a
+  /// 'Content-Disposition: attachment' header that tells the browser to start
+  /// downloading the file to user downloads directory.
+  ///
+  Future<Uint8List> getFileDownload(
+      {required String bucketId, required String fileId}) async {
+    final String path = '/storage/buckets/{bucketId}/files/{fileId}/download'
+        .replaceAll('{bucketId}', bucketId)
+        .replaceAll('{fileId}', fileId);
+
+    final Map<String, dynamic> params = {
+      'project': client.config['project'],
+    };
+
+    final res = await client.call(HttpMethod.get,
+        path: path, params: params, responseType: ResponseType.bytes);
+    return res.data;
+  }
+
+  /// Get File Preview
+  ///
+  /// Get a file preview image. Currently, this method supports preview for image
+  /// files (jpg, png, and gif), other supported formats, like pdf, docs, slides,
+  /// and spreadsheets, will return the file icon image. You can also pass query
+  /// string arguments for cutting and resizing your preview image. Preview is
+  /// supported only for image files smaller than 10MB.
+  ///
+  Future<String> getFilePreview(
+      {required String bucketId,
+      required String fileId,
+      int? width,
+      int? height,
+      String? gravity,
+      int? quality,
+      int? borderWidth,
+      String? borderColor,
+      int? borderRadius,
+      double? opacity,
+      int? rotation,
+      String? background,
+      String? output}) async {
+    String path = '/storage/buckets/{bucketId}/files/{fileId}/preview'
+        .replaceAll('{bucketId}', bucketId)
+        .replaceAll('{fileId}', fileId);
+
+    final Map<String, dynamic> params = {
+      'width': width,
+      'height': height,
+      'gravity': gravity,
+      'quality': quality,
+      'borderWidth': borderWidth,
+      'borderColor': borderColor,
+      'borderRadius': borderRadius,
+      'opacity': opacity,
+      'rotation': rotation,
+      'background': background,
+      'output': output,
+      'project': client.config['project'],
+    };
+
+    params['jwt'] = await client.getJWT();
+
+    params.keys.forEach((key) {
+      if (params[key] is int || params[key] is double) {
+        params[key] = params[key].toString();
+      }
+    });
+    params.removeWhere((key, value) => value == null || value.isEmpty);
+
+    Uri endpoint = Uri.parse(client.endPoint);
+    Uri location = new Uri(
+      scheme: endpoint.scheme,
+      host: endpoint.host,
+      port: endpoint.port,
+      path: endpoint.path + path,
+      queryParameters: params,
+    );
+
+    return location.toString();
+
+    final res = await client.call(HttpMethod.get,
+        path: path, params: params, responseType: ResponseType.bytes);
+    return res.data;
+  }
+
+  /// Get File for View
+  ///
+  /// Get a file content by its unique ID. This endpoint is similar to the
+  /// download method but returns with no  'Content-Disposition: attachment'
+  /// header.
+  ///
+  Future<Uint8List> getFileView(
+      {required String bucketId, required String fileId}) async {
+    final String path = '/storage/buckets/{bucketId}/files/{fileId}/view'
+        .replaceAll('{bucketId}', bucketId)
+        .replaceAll('{fileId}', fileId);
+
+    final Map<String, dynamic> params = {
+      'project': client.config['project'],
+    };
+
+    final res = await client.call(HttpMethod.get,
+        path: path, params: params, responseType: ResponseType.bytes);
+    return res.data;
+  }
+}
diff --git a/lib/src/client.dart b/lib/src/client.dart
index 7b662f8e..dc50d948 100644
--- a/lib/src/client.dart
+++ b/lib/src/client.dart
@@ -6,7 +6,7 @@ import 'response.dart';
 import 'upload_progress.dart';
 
 abstract class Client {
-  static const int CHUNK_SIZE = 5*1024*1024;
+  static const int CHUNK_SIZE = 5 * 1024 * 1024;
   late Map<String, String> config;
   late String _endPoint;
   late String? _endPointRealtime;
@@ -15,8 +15,7 @@ abstract class Client {
   String? get endPointRealtime => _endPointRealtime;
 
   factory Client(
-          {String endPoint = 'https://HOSTNAME/v1',
-          bool selfSigned = false}) =>
+          {String endPoint = 'https://HOSTNAME/v1', bool selfSigned = false}) =>
       createClient(endPoint: endPoint, selfSigned: selfSigned);
 
   Future webAuth(Uri url, {String? callbackUrlScheme});
@@ -36,18 +35,22 @@ abstract class Client {
 
   Client setEndPointRealtime(String endPoint);
 
-    /// Your project ID
+  /// Your project ID
   Client setProject(value);
-    /// Your secret JSON Web Token
+
+  /// Your secret JSON Web Token
   Client setJWT(value);
   Client setLocale(value);
 
   Client addHeader(String key, String value);
 
-  Future<Response> call(HttpMethod method, {
+  Future<Response> call(
+    HttpMethod method, {
     String path = '',
     Map<String, String> headers = const {},
     Map<String, dynamic> params = const {},
     ResponseType? responseType,
   });
+
+  Future<String?> getJWT();
 }
diff --git a/lib/src/client_browser.dart b/lib/src/client_browser.dart
index 93192d40..9d6e6256 100644
--- a/lib/src/client_browser.dart
+++ b/lib/src/client_browser.dart
@@ -19,7 +19,7 @@ ClientBase createClient({
     ClientBrowser(endPoint: endPoint, selfSigned: selfSigned);
 
 class ClientBrowser extends ClientBase with ClientMixin {
-  static const int CHUNK_SIZE = 5*1024*1024;
+  static const int CHUNK_SIZE = 5 * 1024 * 1024;
   String _endPoint;
   Map<String, String>? _headers;
   @override
@@ -64,6 +64,7 @@ class ClientBrowser extends ClientBase with ClientMixin {
     addHeader('X-Appwrite-Project', value);
     return this;
   }
+
   /// Your secret JSON Web Token
   @override
   ClientBrowser setJWT(value) {
@@ -71,6 +72,7 @@ class ClientBrowser extends ClientBase with ClientMixin {
     addHeader('X-Appwrite-JWT', value);
     return this;
   }
+
   @override
   ClientBrowser setLocale(value) {
     config['locale'] = value;
@@ -131,7 +133,8 @@ class ClientBrowser extends ClientBase with ClientMixin {
 
     late Response res;
     if (size <= CHUNK_SIZE) {
-      params[paramName] = http.MultipartFile.fromBytes(paramName, file.bytes!, filename: file.filename);
+      params[paramName] = http.MultipartFile.fromBytes(paramName, file.bytes!,
+          filename: file.filename);
       return call(
         HttpMethod.post,
         path: path,
@@ -158,8 +161,8 @@ class ClientBrowser extends ClientBase with ClientMixin {
       var chunk;
       final end = min(offset + CHUNK_SIZE, size);
       chunk = file.bytes!.getRange(offset, end).toList();
-      params[paramName] =
-          http.MultipartFile.fromBytes(paramName, chunk, filename: file.filename);
+      params[paramName] = http.MultipartFile.fromBytes(paramName, chunk,
+          filename: file.filename);
       headers['content-range'] =
           'bytes $offset-${min<int>(((offset + CHUNK_SIZE) - 1), size)}/$size';
       res = await call(HttpMethod.post,
@@ -219,9 +222,15 @@ class ClientBrowser extends ClientBase with ClientMixin {
 
   @override
   Future webAuth(Uri url, {String? callbackUrlScheme}) {
-  return FlutterWebAuth2.authenticate(
+    return FlutterWebAuth2.authenticate(
       url: url.toString(),
       callbackUrlScheme: "appwrite-callback-" + config['project']!,
     );
   }
+
+  @override
+  Future<String?> getJWT() {
+    // On web cookie should work automatically
+    return Future.value(null);
+  }
 }
diff --git a/lib/src/client_io.dart b/lib/src/client_io.dart
index 06a8abf4..b34a5f93 100644
--- a/lib/src/client_io.dart
+++ b/lib/src/client_io.dart
@@ -1,5 +1,8 @@
 import 'dart:io';
 import 'dart:math';
+import 'package:appwrite/appwrite.dart'
+    show AppwriteException, Account, Response, InputFile, UploadProgress;
+import 'package:appwrite/src/jwt_decoder.dart';
 import 'package:cookie_jar/cookie_jar.dart';
 import 'package:device_info_plus/device_info_plus.dart';
 import 'package:http/http.dart' as http;
@@ -7,16 +10,13 @@ import 'package:http/io_client.dart';
 import 'package:package_info_plus/package_info_plus.dart';
 import 'package:path_provider/path_provider.dart';
 import 'package:flutter_web_auth_2/flutter_web_auth_2.dart';
+import 'package:shared_preferences/shared_preferences.dart';
 import 'client_mixin.dart';
 import 'client_base.dart';
 import 'cookie_manager.dart';
 import 'enums.dart';
-import 'exception.dart';
 import 'interceptor.dart';
-import 'response.dart';
 import 'package:flutter/foundation.dart';
-import 'input_file.dart';
-import 'upload_progress.dart';
 
 ClientBase createClient({
   required String endPoint,
@@ -28,7 +28,7 @@ ClientBase createClient({
     );
 
 class ClientIO extends ClientBase with ClientMixin {
-  static const int CHUNK_SIZE = 5*1024*1024;
+  static const int CHUNK_SIZE = 5 * 1024 * 1024;
   String _endPoint;
   Map<String, String>? _headers;
   @override
@@ -47,6 +47,7 @@ class ClientIO extends ClientBase with ClientMixin {
   CookieJar get cookieJar => _cookieJar;
   @override
   String? get endPointRealtime => _endPointRealtime;
+  SharedPreferences? _preferences;
 
   ClientIO({
     String endPoint = 'https://HOSTNAME/v1',
@@ -65,7 +66,7 @@ class ClientIO extends ClientBase with ClientMixin {
       'x-sdk-platform': 'client',
       'x-sdk-language': 'flutter',
       'x-sdk-version': '9.0.0',
-      'X-Appwrite-Response-Format' : '1.0.0',
+      'X-Appwrite-Response-Format': '1.0.0',
     };
 
     config = {};
@@ -86,26 +87,28 @@ class ClientIO extends ClientBase with ClientMixin {
     return dir;
   }
 
-     /// Your project ID
-    @override
-    ClientIO setProject(value) {
-        config['project'] = value;
-        addHeader('X-Appwrite-Project', value);
-        return this;
-    }
-     /// Your secret JSON Web Token
-    @override
-    ClientIO setJWT(value) {
-        config['jWT'] = value;
-        addHeader('X-Appwrite-JWT', value);
-        return this;
-    }
-    @override
-    ClientIO setLocale(value) {
-        config['locale'] = value;
-        addHeader('X-Appwrite-Locale', value);
-        return this;
-    }
+  /// Your project ID
+  @override
+  ClientIO setProject(value) {
+    config['project'] = value;
+    addHeader('X-Appwrite-Project', value);
+    return this;
+  }
+
+  /// Your secret JSON Web Token
+  @override
+  ClientIO setJWT(value) {
+    config['jWT'] = value;
+    addHeader('X-Appwrite-JWT', value);
+    return this;
+  }
+
+  @override
+  ClientIO setLocale(value) {
+    config['locale'] = value;
+    addHeader('X-Appwrite-Locale', value);
+    return this;
+  }
 
   @override
   ClientIO setSelfSigned({bool status = true}) {
@@ -138,7 +141,7 @@ class ClientIO extends ClientBase with ClientMixin {
   }
 
   Future init() async {
-    if(_initProgress) return;
+    if (_initProgress) return;
     _initProgress = true;
     final Directory cookieDir = await _getCookiePath();
     _cookieJar = PersistCookieJar(storage: FileStorage(cookieDir.path));
@@ -174,13 +177,12 @@ class ClientIO extends ClientBase with ClientMixin {
         final macinfo = await deviceInfoPlugin.macOsInfo;
         device = '(Macintosh; ${macinfo.model})';
       }
-      addHeader(
-          'user-agent', '${packageInfo.packageName}/${packageInfo.version} $device');
+      addHeader('user-agent',
+          '${packageInfo.packageName}/${packageInfo.version} $device');
     } catch (e) {
       debugPrint('Error getting device info: $e');
       device = Platform.operatingSystem;
-      addHeader(
-          'user-agent', '$device');
+      addHeader('user-agent', '$device');
     }
 
     _initialized = true;
@@ -283,14 +285,14 @@ class ClientIO extends ClientBase with ClientMixin {
     while (offset < size) {
       List<int> chunk = [];
       if (file.bytes != null) {
-        final end = min(offset + CHUNK_SIZE-1, size-1);
+        final end = min(offset + CHUNK_SIZE - 1, size - 1);
         chunk = file.bytes!.getRange(offset, end).toList();
       } else {
         raf!.setPositionSync(offset);
         chunk = raf.readSync(CHUNK_SIZE);
       }
-      params[paramName] =
-          http.MultipartFile.fromBytes(paramName, chunk, filename: file.filename);
+      params[paramName] = http.MultipartFile.fromBytes(paramName, chunk,
+          filename: file.filename);
       headers['content-range'] =
           'bytes $offset-${min<int>(((offset + CHUNK_SIZE) - 1), size)}/$size';
       res = await call(HttpMethod.post,
@@ -369,10 +371,19 @@ class ClientIO extends ClientBase with ClientMixin {
       res = await toResponse(streamedResponse);
       res = await _interceptResponse(res);
 
-      return prepareResponse(
+      final response = prepareResponse(
         res,
         responseType: responseType,
       );
+
+      if (method == HttpMethod.delete &&
+          path.contains('/account/sessions/current')) {
+        _resetJWT();
+      }
+      if ((method == HttpMethod.get && path == '/account') || (method == HttpMethod.post && path.contains('/account/sessions/'))) {
+        getJWT(true);
+      }
+      return response;
     } catch (e) {
       if (e is AppwriteException) {
         rethrow;
@@ -380,4 +391,42 @@ class ClientIO extends ClientBase with ClientMixin {
       throw AppwriteException(e.toString());
     }
   }
+
+  void _resetJWT() async {
+    if (_preferences != null) {
+      await _preferences!.remove('jwt');
+    }
+  }
+
+  @override
+  Future<String?> getJWT([bool fresh = false]) async {
+    final Account account = Account(this);
+    try {
+      if(!fresh) {
+
+        if (_preferences == null) {
+          _preferences = await SharedPreferences.getInstance();
+        }
+        final savedJwt = _preferences!.getString('jwt');
+        if (savedJwt != null) {
+          try {
+            if (!JwtDecoder.isExpired(savedJwt)) {
+              return savedJwt;
+            }
+          } catch (e) {
+            // Remove invalid token
+            await _preferences!.remove('jwt');
+          }
+        } else {
+          print('no saved jwt');
+        }
+      }
+
+      final jwt = (await account.createJWT()).jwt;
+      _preferences!.setString('jwt', jwt);
+      return jwt;
+    } catch (e) {
+      return null;
+    }
+  }
 }
diff --git a/lib/src/jwt_decoder.dart b/lib/src/jwt_decoder.dart
new file mode 100644
index 00000000..bb6a827c
--- /dev/null
+++ b/lib/src/jwt_decoder.dart
@@ -0,0 +1,29 @@
+library jwt_decoder;
+
+import 'dart:convert';
+
+class JwtDecoder {
+  static Map<String, dynamic> _decode(String token) {
+    final splitToken = token.split(".");
+    if (splitToken.length != 3) {
+      throw FormatException('Invalid Token');
+    }
+    try {
+      final payload =
+          utf8.decode(base64.decode(base64.normalize(splitToken[1])));
+      return jsonDecode(payload);
+    } catch (error) {
+      throw FormatException('Invalid Token');
+    }
+  }
+
+  static bool isExpired(String token) {
+    final decodedToken = _decode(token);
+    final expiration = decodedToken['exp'] as int?;
+    if (expiration == null) {
+      return false;
+    }
+    final expiresOn = DateTime.fromMillisecondsSinceEpoch(expiration * 1000);
+    return DateTime.now().isAfter(expiresOn);
+  }
+}