casual-simulation / casualos

Casual Open Simulation for the Web
https://ab1.bot
MIT License
48 stars 8 forks source link

Add the ability to securely store and retrieve API keys #446

Open KallynGowdy opened 3 months ago

KallynGowdy commented 3 months ago

Currently, the only way to query a protected API in CasualOS is to store the API key inside the inst data. Ideally, users will be able to query any API they desire, using an API key that is securely stored and managed by CasualOS. We call these cloud keys records.

Cloud key records are pretty similar data records. They have an address, markers, and a value. In this case, the value of the key is always a string. Additionally, cloud keys are able to be assigned a list of HTTP origins that they can be used for. They are encrypted by the key secret which is wrapped by the service secret.

The key secret is a secret that is unique to the key and is stored in the database. This secret is used to ensure that keys can only be decrypted by users who have access to the key. This secret is stored in an encrypted form encrypted by the service secret.

The service secret is a secret that is unique to the deployment of CasualOS and is ideally stored in a key management service. This secret is used to ensure that key secrets can only be decrypted by the CasualOS service itself.

If someone has access to the database, then they should not have access to the service secret.

In addition, cloud keys have a default marker of private, which means that the key is only able to be accessed by members of the studio or the owner of the record by default. For now. cloud keys cannot have the publicRead or publicWrite markers.

Cloud keys also keep a audit log to help track who has accessed the key and what operations have been performed on it.

Here is a list of operations that can be performed for cloud keys:

Finally, CasualOS should support proxying HTTP requests so that cloud keys can be injected into them.

Example of proxying a web request with a cloud key:

await web.proxy({
    url: 'https://example.com',
    method: 'GET',
    headers: {
        Authorization: secretString`Bearer ${os.injectCloudKey(recordName, keyName)}`
    }
});