Skip to content
Merged
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
63 changes: 38 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,26 @@
# Context

Modules typically handle two types of data that benefit from persistent secure storage and management: module settings and user settings and secrets.
This module introduces the concept of `Contexts`, which store data locally on the machine where the module runs. It allows module developers to
This module introduces the concept of `Contexts`, which enable persistent and secure data storage for PowerShell modules. It allows module developers to
separate user and module data from the module code, enabling users to resume their work without needing to reconfigure the module or log in again,
provided the service supports session refresh mechanisms (e.g., refresh tokens).

The module uses NaCl-based encryption, provided by the `libsodium` library, to encrypt and decrypt `Context` data. The module that delivers this
functionality is called [`Sodium`](https://github.com/someuser/Sodium) and is a dependency of this module. The
[`Sodium`](https://github.com/someuser/Sodium) module is automatically installed when you install this module.
functionality is called [`Sodium`](https://github.com/PSModule/Sodium) and is a dependency of this module. The
[`Sodium`](https://github.com/PSModule/Sodium) module is automatically installed when you install this module.

## What is a `Context`?

A `Context` is a way to securely persist user and module state on disk while ensuring data remains encrypted at rest. It stores structured data that
can be represented in JSON format, including regular values, arrays, objects and `SecureString`. It can hold multiple secrets (such as passwords or
API tokens) alongside general data like configuration settings, session metadata, or user preferences. `SecureStrings` are specially handled to
maintain their security when stored and retrieved.
The concept of `Context` is widely used to represent a collection of data that is relevant to a specific use-case. In this module,
a `Context` is a way to securely persist user and module data and offers a set of functions to manage this across modules that implement it.
Data that is stored in a `Context` can include user-specific settings, secrets, and module configuration data.
A `Context` is identified by a unique ID, which is typically a string that represents the module and user of a module (e.g., `GitHub/john_doe`), but
this is just an example. Any data that can be represented in JSON format can be stored in a `Context`.

When saving a `Context`, data is first structured as plain-text JSON, then encrypted and stored on disk. `SecureStrings` are marked with a special
prefix before encryption, ensuring they can be safely restored as secure strings when the `Context` is loaded back into memory.
### 1. Storing data (object or dictionary) to disk using `Set-Context`

When imported, the encrypted data is decrypted, converted back into its original structured format, and held in memory, ensuring both usability and
security.

<details>
<summary> 1. Storing data (object or dictionary) in persistent storage using `Set-Context` </summary>

Typically, the first input to a `Context` is an object (though it can also be a hashtable or any other type that converts to JSON).
To store data to disk, use the `Set-Context` function. The function needs an ID and the data object.
The object can be anything that can be converted and represented in JSON format.

```pwsh
Set-Context -ID 'john_doe' -Context ([PSCustomObject]@{
Expand All @@ -36,22 +31,40 @@ Set-Context -ID 'john_doe' -Context ([PSCustomObject]@{
TwoFactorMethods = @('TOTP', 'SMS')
})
```
</details>

<details>
<summary> 2. How the data is ultimately stored – as processed JSON </summary>
### 2. The object is converted to JSON and prepared for encryption

This is how the object above is stored, shown here in an uncompressed format for readability. Notice that the `ID` property gets added.
The object that is passed into `Set-Context` is first analyzed. If the object contains any `SecureString` values, they are converted to plain-text and
prefixed with `[SECURESTRING]`. This indicates that these values should be restored back to `SecureString`. The whole object is then converted to a
JSON string.

```json
{
"ID": "john_doe",
"Username": "john_doe",
"AuthToken": "[SECURESTRING]ghp_12345ABCDE67890FGHIJ",
"LoginTime": "2024-11-21T21:16:56.2518249+01:00"
"LoginTime": "2024-11-21T21:16:56.2518249+01:00",
"IsTwoFactorAuth": true,
"TwoFactorMethods": ["TOTP", "SMS"]
}
```

### 3. Storing the context object to disk

When the data is primed for storage, it is finally encrypted using the `Sodium` module and saved to disk. The file is stored in the user's home
directory `$HOME\.contextvault\<context_id>.json`, where `<context_id>` is a generated GUID, providing a unique name for the file.
The encrypted JSON representation of the data is added to metadata object that holds other info such as the ID of the `Context` and the path to the
file where it is stored.

```json
{
"ID": "PSModule.GitHub/github.com/john_doe",
"Path": "C:\\Users\\JohnDoe\\.contextvault\\d2edaa6e-95a1-41a0-b6ef-0ecc5d116030.json",
"Context": "0kGmtbQiEtih7 --< encrypted context data >-- ceqbMiBilUvEzO1Lk"
}
```
</details>

The metadata can be accessed using the `Get-ContextInfo` function.

## Installation

Expand All @@ -71,13 +84,13 @@ Let's take a closer look at how to store these types of data using the module.
A module developer can create additional `Contexts` for settings that share the same lifecycle, such as those associated with a module extension.

For example, if we have a module called `GitHub` that needs to store some settings, the module developer could initialize a `Context` called `GitHub`
as part of the loading section in the module code. The module configuration is stored in `ContextVault` under the ID `GitHub`.
as part of the loading section in the module code. The module configuration is accessed using the ID `GitHub`.

### User Configuration

To store user data, a module developer can create a `Context` that serves as a "namespace" for user-specific configurations.

Imagine a user named `BobMarley` logs into the `GitHub` module. The following would exist in `ContextVault`:
Imagine a user named `BobMarley` logs into the `GitHub` module. The following logical structure would be created:

- `GitHub`: Contains module configuration, like default user, host, and client ID.
- `GitHub/BobMarley`: Contains user configuration, secrets, and default values for API calls.
Expand Down Expand Up @@ -147,4 +160,4 @@ If you code, we'd love your contributions! Please read the [Contribution Guideli

## Links

- Sodium [GitHub](https://github.com/someuser/Sodium) | [PSGallery](https://www.powershellgallery.com/packages/Sodium)
- Sodium [GitHub](https://github.com/PSModule/Sodium) | [PSGallery](https://www.powershellgallery.com/packages/Sodium)
Loading