Skip to content

Commit

Permalink
Merge pull request #824 from rjs580/web-session-storage
Browse files Browse the repository at this point in the history
feat: Support sessionStorage option for web
  • Loading branch information
juliansteenbakker authored Jan 4, 2025
2 parents e804133 + 5f8d7d9 commit 38518cc
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 14 deletions.
3 changes: 3 additions & 0 deletions flutter_secure_storage/lib/options/web_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class WebOptions extends Options {
this.publicKey = 'FlutterSecureStorage',
this.wrapKey = '',
this.wrapKeyIv = '',
this.useSessionStorage = false,
});

static const WebOptions defaultOptions = WebOptions();
Expand All @@ -15,12 +16,14 @@ class WebOptions extends Options {
final String publicKey;
final String wrapKey;
final String wrapKeyIv;
final bool useSessionStorage;

@override
Map<String, String> toMap() => <String, String>{
'dbName': dbName,
'publicKey': publicKey,
'wrapKey': wrapKey,
'wrapKeyIv': wrapKeyIv,
'useSessionStorage': useSessionStorage.toString(),
};
}
37 changes: 23 additions & 14 deletions flutter_secure_storage_web/lib/flutter_secure_storage_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,27 @@ class FlutterSecureStorageWeb extends FlutterSecureStoragePlatform {
static const _publicKey = 'publicKey';
static const _wrapKey = 'wrapKey';
static const _wrapKeyIv = 'wrapKeyIv';
static const _useSessionStorage = 'useSessionStorage';

/// Registrar for FlutterSecureStorageWeb
static void registerWith(Registrar registrar) {
FlutterSecureStoragePlatform.instance = FlutterSecureStorageWeb();
}

web.Storage _getStorage(Map<String, String> options) {
return options[_useSessionStorage] == 'true'
? web.window.sessionStorage
: web.window.localStorage;
}

/// Returns true if the storage contains the given [key].
@override
Future<bool> containsKey({
required String key,
required Map<String, String> options,
}) =>
Future.value(
web.window.localStorage.has("${options[_publicKey]!}.$key"),
_getStorage(options).has("${options[_publicKey]!}.$key"),
);

/// Deletes associated value for the given [key].
Expand All @@ -39,18 +46,19 @@ class FlutterSecureStorageWeb extends FlutterSecureStoragePlatform {
required String key,
required Map<String, String> options,
}) async {
web.window.localStorage.removeItem("${options[_publicKey]!}.$key");
_getStorage(options).removeItem("${options[_publicKey]!}.$key");
}

/// Deletes all keys with associated values.
@override
Future<void> deleteAll({
required Map<String, String> options,
}) async {
final storage = _getStorage(options);
final publicKey = options[_publicKey]!;
final keys = [publicKey];
for (int j = 0; j < web.window.localStorage.length; j++) {
final key = web.window.localStorage.key(j) ?? "";
for (int j = 0; j < storage.length; j++) {
final key = storage.key(j) ?? "";
if (!key.startsWith('$publicKey.')) {
continue;
}
Expand All @@ -59,7 +67,7 @@ class FlutterSecureStorageWeb extends FlutterSecureStoragePlatform {
}

for (final key in keys) {
web.window.localStorage.removeItem(key);
storage.removeItem(key);
}
}

Expand All @@ -71,7 +79,7 @@ class FlutterSecureStorageWeb extends FlutterSecureStoragePlatform {
required String key,
required Map<String, String> options,
}) async {
final value = web.window.localStorage["${options[_publicKey]!}.$key"];
final value = _getStorage(options)["${options[_publicKey]!}.$key"];

return _decryptValue(value, options);
}
Expand All @@ -81,16 +89,16 @@ class FlutterSecureStorageWeb extends FlutterSecureStoragePlatform {
Future<Map<String, String>> readAll({
required Map<String, String> options,
}) async {
final storage = _getStorage(options);
final map = <String, String>{};
final prefix = "${options[_publicKey]!}.";
for (int j = 0; j < web.window.localStorage.length; j++) {
final key = web.window.localStorage.key(j) ?? "";
for (int j = 0; j < storage.length; j++) {
final key = storage.key(j) ?? "";
if (!key.startsWith(prefix)) {
continue;
}

final value =
await _decryptValue(web.window.localStorage.getItem(key), options);
final value = await _decryptValue(storage.getItem(key), options);

if (value == null) {
continue;
Expand All @@ -110,12 +118,13 @@ class FlutterSecureStorageWeb extends FlutterSecureStoragePlatform {
js_interop.JSAny algorithm,
Map<String, String> options,
) async {
final storage = _getStorage(options);
late web.CryptoKey encryptionKey;
final key = options[_publicKey]!;
final useWrapKey = options[_wrapKey]?.isNotEmpty ?? false;

if (web.window.localStorage.has(key)) {
final jwk = base64Decode(web.window.localStorage[key]!);
if (storage.has(key)) {
final jwk = base64Decode(storage[key]!);

if (useWrapKey) {
final unwrappingKey = await _getWrapKey(options);
Expand Down Expand Up @@ -165,7 +174,7 @@ class FlutterSecureStorageWeb extends FlutterSecureStoragePlatform {
.toDart;
}

web.window.localStorage[key] = base64Encode(
storage[key] = base64Encode(
(jsonWebKey! as js_interop.JSArrayBuffer).toDart.asUint8List(),
);
}
Expand Down Expand Up @@ -223,7 +232,7 @@ class FlutterSecureStorageWeb extends FlutterSecureStoragePlatform {
final encoded =
"${base64Encode(iv)}.${base64Encode(encryptedContent.toDart.asUint8List())}";

web.window.localStorage["${options[_publicKey]!}.$key"] = encoded;
_getStorage(options)["${options[_publicKey]!}.$key"] = encoded;
}

Future<String?> _decryptValue(
Expand Down

0 comments on commit 38518cc

Please sign in to comment.