Fliplet.Security

Fliplet.Security

Persist a structured, per-app object to encrypted device-local storage via the fliplet-security package. The package exposes two globals:

  • Fliplet.Security — top-level namespace (currently a container for Storage).
  • Fliplet.Security.Storage — the storage API for reading, writing, resetting, and removing the encrypted bag.

Each app gets its own bag, keyed internally by appId, so values written from one app never leak to another running on the same device.

Not the same as fliplet-encryption. fliplet-security stores values on the device (a single encrypted bag per app, useful for tokens, flags, secrets). fliplet-encryption (Fliplet.DataSources.Encryption) transparently encrypts and decrypts selected columns of a Data Source as it goes to and from the server. If you need to protect data inside a Data Source row, use fliplet-encryption. If you need to keep something private on the device, use fliplet-security. See the When to use this section for a side-by-side.

When to use this

You need to… Use
Keep an OAuth token, refresh token, or API key private on the device fliplet-security (Fliplet.Security.Storage)
Persist a small structured object across app launches with a default shape fliplet-security
Protect specific Data Source columns (e.g. firstName, bio) end-to-end fliplet-encryption (Fliplet.DataSources.Encryption)
Encrypt rows on the server so the API never sees plaintext fliplet-encryption
Read/write the current login session Fliplet.Session
Persist arbitrary unencrypted device data Fliplet.Storage (in fliplet-core)

The two packages are complementary, not interchangeable. They’re often used together: fliplet-encryption to protect what’s stored remotely, fliplet-security to protect the encryption key itself on the device.

Install

Add the fliplet-security dependency to your screen or app resources. The package depends on fliplet-core and exposes Fliplet.Security and Fliplet.Security.Storage globally on load.

How it works

Fliplet.Security.Storage maintains a single in-memory object (the “secure bag”) for the current app. The bag is namespaced per app and persisted to encrypted device storage under the hood.

Each entry in the bag is created with a key and a dataStructure — the default shape used when the entry is first created and when it’s reset. Reading an entry returns its data; writing values means mutating that data object and calling update() to persist.

A typical lifecycle:

  1. Call Fliplet.Security.Storage.init() once on screen load to hydrate the bag.
  2. Call create(key, dataStructure) for each entry your screen owns — idempotent; returns the existing data if already present.
  3. Mutate the returned data object as the user interacts.
  4. Call update() to persist.
  5. Call reset(key) to revert one entry to its default shape, or resetAll() to wipe the whole bag (e.g. on logout).

Fliplet.Security.Storage.init()

(Returns Promise<Object>)

Hydrates the secure bag for the current app from device storage into memory. Must be called once before any other method on Fliplet.Security.Storage. Subsequent calls are no-ops and resolve with the cached bag.

Usage

Fliplet.Security.Storage.init().then(function (bag) {
  // bag is the full secure object for this app
  // typically you don't need to use it directly — call create() / get() instead
});

Vue 3 example

<script setup>
import { onMounted } from 'vue';

onMounted(async () => {
  await Fliplet.Security.Storage.init();
  // safe to call create / get / update from here on
});
</script>

Fliplet.Security.Storage.create(key, dataStructure)

(Returns Promise<Object>)

Creates a new named entry in the secure bag, or returns the existing data if one already exists for that key. The dataStructure argument is shallow-cloned and stored as both the entry’s initial data and its default shape (used later by reset(key)).

  • key (String) — Unique key for this entry inside the bag.
  • dataStructure (Object) — Default shape and initial values for the entry.

Usage

await Fliplet.Security.Storage.init();

const credentials = await Fliplet.Security.Storage.create('credentials', {
  accessToken: '',
  refreshToken: '',
  expiresAt: null
});

// credentials is the live data object — mutate it directly, then call update()
credentials.accessToken = 'eyJhbGciOi...';
credentials.expiresAt = Date.now() + 3600 * 1000;

await Fliplet.Security.Storage.update();

create() is idempotent. Calling it again with the same key returns the existing entry’s data rather than overwriting it. To wipe an entry back to its dataStructure, use reset(key).

Fliplet.Security.Storage.get(key)

(Returns Object | undefined — synchronous)

Reads the data for an existing entry. Returns undefined if no entry has been created for that key. Synchronousinit() must have already resolved.

  • key (String) — Key the entry was created under.

Usage

await Fliplet.Security.Storage.init();
await Fliplet.Security.Storage.create('credentials', {
  accessToken: '',
  refreshToken: ''
});

const credentials = Fliplet.Security.Storage.get('credentials');

if (credentials && credentials.accessToken) {
  // user has a stored token
}

Fliplet.Security.Storage.update()

(Returns Promise<Object>)

Persists the current in-memory state of the bag to device storage. Call this after mutating any entry’s data object.

Usage

const prefs = Fliplet.Security.Storage.get('preferences');

prefs.theme = 'dark';
prefs.notificationsEnabled = false;

await Fliplet.Security.Storage.update();

Mutations to data are not persisted automatically — update() is required.

Fliplet.Security.Storage.reset(key)

(Returns Promise<Object>)

Resets a single entry’s data back to the dataStructure that was passed when it was first created, then persists the bag. Other entries are untouched.

  • key (String) — Key of the entry to reset.

Usage

// User signs out — clear their credentials but keep their UI preferences
await Fliplet.Security.Storage.reset('credentials');

Fliplet.Security.Storage.resetAll()

(Returns Promise)

Removes the entire secure bag for the current app from device storage. Use this on full logout or “wipe app data” flows.

Usage

async function logoutAndWipe() {
  await Fliplet.Session.logout();
  await Fliplet.Security.Storage.resetAll();
  Fliplet.Navigate.screen('login');
}

After resetAll(), the in-memory bag in the current page session may still hold previously-created entries. To use the storage again, call init() followed by create() for each entry you need.

Full example

A screen that stores an OAuth token securely and reuses it across launches:

<template>
  <div>
    <button v-if="!isAuthenticated" @click="signIn">Sign in</button>
    <button v-else @click="signOut">Sign out</button>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

const isAuthenticated = ref(false);
let credentials = null;

onMounted(async () => {
  await Fliplet.Security.Storage.init();

  credentials = await Fliplet.Security.Storage.create('oauthCredentials', {
    accessToken: '',
    expiresAt: null
  });

  isAuthenticated.value = !!credentials.accessToken
    && credentials.expiresAt > Date.now();
});

async function signIn() {
  // ...obtain a token from your OAuth flow
  credentials.accessToken = 'eyJhbGciOi...';
  credentials.expiresAt = Date.now() + 3600 * 1000;

  await Fliplet.Security.Storage.update();
  isAuthenticated.value = true;
}

async function signOut() {
  await Fliplet.Security.Storage.reset('oauthCredentials');
  isAuthenticated.value = false;
}
</script>
  • Fliplet.DataSources.Encryption — column-level encryption for Data Sources.
  • Fliplet.Session — current user session, login/logout, passport details.
  • Fliplet.Storage (part of fliplet-core) — general-purpose unencrypted device storage.

Back to API documentation