mirror of
https://github.com/Infisical/infisical.git
synced 2025-04-10 07:25:40 +00:00
Compare commits
7 Commits
infisical/
...
infisical/
Author | SHA1 | Date | |
---|---|---|---|
3d70333f9c | |||
f4404f66b8 | |||
9a62496d5c | |||
e24c1f38e0 | |||
3ca9b7d6bf | |||
37d2d580f4 | |||
41dd2fda8a |
@ -36,8 +36,8 @@ export const emailPasswordReset = async (req: Request, res: Response) => {
|
||||
if (!user || !user?.publicKey) {
|
||||
// case: user has already completed account
|
||||
|
||||
return res.status(403).send({
|
||||
message: "If an account exists with this email, a password reset link has been sent"
|
||||
return res.status(200).send({
|
||||
message:"If an account exists with this email, a password reset link has been sent"
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,6 @@ import {
|
||||
router.get(
|
||||
"/raw",
|
||||
query("workspaceId").exists().isString().trim(),
|
||||
query("workspaceId").exists().isString().trim(),
|
||||
query("environment").exists().isString().trim(),
|
||||
query("secretPath").default("/").isString().trim(),
|
||||
validateRequest,
|
||||
|
@ -3,32 +3,29 @@ title: "Authentication"
|
||||
description: "How to authenticate with the Infisical Public API"
|
||||
---
|
||||
|
||||
## Essentials
|
||||
The Public API accepts multiple modes of authentication being via [Infisical Token](/documentation/platform/token) or API Key.
|
||||
|
||||
The Public API accepts multiple modes of authentication being via API Key or [Infisical Token](/documentation/platform/token).
|
||||
|
||||
- API Key: Provides full access to all endpoints representing the user without ability to encrypt/decrypt secrets in **E2EE** mode.
|
||||
- [Infisical Token](/documentation/platform/token): Provides short-lived, scoped CRUD access to the secrets of a specific project and environment.
|
||||
- API Key: Provides full access to all endpoints representing the user without ability to encrypt/decrypt secrets for **E2EE** endpoints.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="API Key">
|
||||
The API key mode uses an API key to authenticate with the API.
|
||||
<Tabs>
|
||||
<Tab title="Infisical Token">
|
||||
The Infisical Token mode uses an Infisical Token to authenticate with the API.
|
||||
|
||||
To authenticate requests with Infisical using the API Key, you must include an API key in the `X-API-KEY` header of HTTP requests made to the platform.
|
||||
To authenticate requests with Infisical using the Infisical Token, you must include your Infisical Token in the `Authorization` header of HTTP requests made to the platform with the value `Bearer <infisical_token>`.
|
||||
|
||||
You can obtain an API key in User Settings > API Keys
|
||||
You can obtain an Infisical Token in Project Settings > Service Tokens.
|
||||
|
||||

|
||||

|
||||
</Accordion>
|
||||
<Accordion title="Infisical Token">
|
||||

|
||||
</Tab>
|
||||
<Tab title="API Key">
|
||||
The API key mode uses an API key to authenticate with the API.
|
||||
|
||||
The Infisical Token mode uses an Infisical Token to authenticate with the API.
|
||||
To authenticate requests with Infisical using the API Key, you must include an API key in the `X-API-KEY` header of HTTP requests made to the platform.
|
||||
|
||||
To authenticate requests with Infisical using the Infisical Token, you must include your Infisical Token in the `Authorization` header of HTTP requests made to the platform with the value `Bearer <infisical_token>`.
|
||||
You can obtain an API key in User Settings > API Keys
|
||||
|
||||
You can obtain an Infisical Token in Project Settings > Service Tokens.
|
||||
|
||||

|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||

|
||||

|
||||
</Tab>
|
||||
</Tabs>
|
@ -1,92 +0,0 @@
|
||||
---
|
||||
title: "ES Mode"
|
||||
---
|
||||
|
||||
Encrypted Standard (ES) mode is the easiest way to use Infisical's API. With it, you can make HTTP calls to Infisical
|
||||
to read/write secrets in plaintext.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com).
|
||||
- Create an [Infisical Token](/documentation/platform/token) for your project and environment with write access enabled.
|
||||
- [Ensure that your project is blind-indexed](../blind-indices).
|
||||
|
||||
Below, we showcase how to execute common CRUD operations to manage secrets in **ES** mode:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Retrieve secrets">
|
||||
<Tabs>
|
||||
<Tab title="cURL">
|
||||
```bash
|
||||
curl --location --request GET 'http://localhost:8080/api/v3/secrets/raw?environment=dev&workspaceId=xxx' \
|
||||
--header 'Authorization: Bearer st.xxx'
|
||||
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</Accordion>
|
||||
<Accordion title="Create secret">
|
||||
<Tabs>
|
||||
<Tab title="cURL">
|
||||
```bash
|
||||
curl --location --request POST 'http://localhost:8080/api/v3/secrets/raw/SECRET_NAME' \
|
||||
--header 'Authorization: Bearer st.xxx' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"workspaceId": "xxx",
|
||||
"environment": "dev",
|
||||
"type": "shared",
|
||||
"secretValue": "SECRET_VALUE",
|
||||
"secretPath": "/"
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</Accordion>
|
||||
<Accordion title="Retrieve secret">
|
||||
<Tabs>
|
||||
<Tab title="cURL">
|
||||
```bash
|
||||
curl --location --request GET 'http://localhost:8080/api/v3/secrets/raw/SECRET_NAME?workspaceId=xxx&environment=dev&secretPath=/' \
|
||||
--header 'Authorization: Bearer st.xxx'
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</Accordion>
|
||||
<Accordion title="Update secret">
|
||||
<Tabs>
|
||||
<Tab title="cURL">
|
||||
```bash
|
||||
curl --location --request PATCH 'http://localhost:8080/api/v3/secrets/raw/SECRET_NAME' \
|
||||
--header 'Authorization: Bearer st.xxx' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"workspaceId": "xxx",
|
||||
"environment": "dev",
|
||||
"type": "shared",
|
||||
"secretValue": "SECRET_VALUE",
|
||||
"secretPath": "/"
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</Accordion>
|
||||
<Accordion title="Delete secret">
|
||||
<Tabs>
|
||||
<Tab title="cURL">
|
||||
```bash
|
||||
curl --location --request DELETE 'http://localhost:8080/api/v3/secrets/raw/SECRET_NAME' \
|
||||
--header 'Authorization: Bearer st.xxx' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"workspaceId": "xxx",
|
||||
"environment": "dev",
|
||||
"type": "shared",
|
||||
"secretValue": "SECRET_VALUE",
|
||||
"secretPath": "/"
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
@ -1,57 +0,0 @@
|
||||
---
|
||||
title: "Preface"
|
||||
---
|
||||
|
||||
Each project in Infisical can be used either in **End-to-End Encrypted (E2EE)** mode or **Encrypted Standard (ES)** mode which dictates how it can be interacted with via the Infisical API.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Encrypted Standard (ES)"
|
||||
href="/api-reference/overview/encryption-modes/es-mode"
|
||||
icon="shield-halved"
|
||||
color="#3c8639"
|
||||
>
|
||||
Secret operations without client-side encryption/decryption
|
||||
</Card>
|
||||
<Card href="/api-reference/overview/encryption-modes/e2ee-mode" title="End-to-End Encrypted (E2EE)" icon="shield" color="#3775a9">
|
||||
Secret operations with client-side encryption/decryption
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
By default, all projects are initialized in **E2EE** mode which means the server is not able to decrypt any values because all secret encryption/decryption operations occur on the client-side. However, this has limitations around functionality and ease-of-use:
|
||||
|
||||
- You cannot make HTTP calls to Infisical to read/write secrets in plaintext.
|
||||
- You cannot leverage non-E2EE features like native integrations and in-platform automations like dynamic secrets and secret rotation.
|
||||
|
||||
For this reason, Infisical also provides the **ES** mode of operation to unlock the above limitations by enabling the server to decrypt your values. You can optionally switch a project to using **ES** mode
|
||||
in your Project Settings.
|
||||
|
||||
<Note>
|
||||
Make no mistake, the limitations of **E2EE** mode do not prevent you from syncing secrets from Infisical to platforms like GitLab. They just imply
|
||||
that you have to do things the "E2EE-way" such as by embedding the Infisical CLI into your GitLab CI/CD pipelines to fetch and decrypt
|
||||
secrets on the client-side.
|
||||
</Note>
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Is E2EE mode or ES mode right for me?">
|
||||
We recommend starting with **E2EE** mode and switching to **ES** mode when:
|
||||
|
||||
- Your team needs more power out of non-E2EE features available in **ES** mode such as secret rotation, dynamic secrets, etc.
|
||||
- Your team wants an easier way to read/write secrets with Infisical.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="How can I switch from E2EE mode to ES mode?">
|
||||
By default, all projects in Infisical are initialized to **E2EE** mode and can be switched to **ES** mode in the Project Settings by disabling end-to-end encryption.
|
||||
</Accordion>
|
||||
<Accordion title="Is ES mode secure if it's not E2EE?">
|
||||
**ES** mode is secure and in fact what most vendors in the secret management industry are doing at the moment. In this mode, secrets are encrypted at rest by
|
||||
a series of keys, secured ultimately by a top-level `ROOT_ENCRYPTION_KEY` located on the server.
|
||||
|
||||
If you're concerned about Infisical Cloud's ability to read your secrets if using **ES** mode in Infisical Cloud, then you may wish to
|
||||
use Infisical Cloud in **E2EE** mode or self-host Infisical on your own infrastructure and then use **ES** mode; this of course which means setting up firewalls and securing the instance yourself.
|
||||
|
||||
As an organization, we prohibit reading any customer secrets without explicit permission; access to the `ROOT_ENCRYPTION_KEY` is restricted to one individual in the organization.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
@ -1,233 +0,0 @@
|
||||
---
|
||||
title: "Create secret"
|
||||
description: "How to add a secret using an Infisical Token scoped to a project and environment"
|
||||
---
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com).
|
||||
- Create an [Infisical Token](/documentation/platform/token) for your project and environment with write access enabled.
|
||||
- Grasp a basic understanding of the system and its underlying cryptography [here](/api-reference/overview/introduction).
|
||||
- [Ensure that your project is blind-indexed](../blind-indices).
|
||||
|
||||
## Flow
|
||||
|
||||
1. [Get your Infisical Token data](/api-reference/endpoints/service-tokens/get) including a (encrypted) project key.
|
||||
2. Decrypt the (encrypted) project key with the key from your Infisical Token.
|
||||
3. Encrypt your secret with the project key
|
||||
4. [Send (encrypted) secret to Infisical](/api-reference/endpoints/secrets/create)
|
||||
|
||||
## Example
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Javascript">
|
||||
```js
|
||||
const crypto = require('crypto');
|
||||
const axios = require('axios');
|
||||
const nacl = require('tweetnacl');
|
||||
|
||||
const BASE_URL = 'https://app.infisical.com';
|
||||
const ALGORITHM = 'aes-256-gcm';
|
||||
const BLOCK_SIZE_BYTES = 16;
|
||||
|
||||
const encrypt = ({ text, secret }) => {
|
||||
const iv = crypto.randomBytes(BLOCK_SIZE_BYTES);
|
||||
const cipher = crypto.createCipheriv(ALGORITHM, secret, iv);
|
||||
|
||||
let ciphertext = cipher.update(text, 'utf8', 'base64');
|
||||
ciphertext += cipher.final('base64');
|
||||
return {
|
||||
ciphertext,
|
||||
iv: iv.toString('base64'),
|
||||
tag: cipher.getAuthTag().toString('base64')
|
||||
};
|
||||
}
|
||||
|
||||
const decrypt = ({ ciphertext, iv, tag, secret}) => {
|
||||
const decipher = crypto.createDecipheriv(
|
||||
ALGORITHM,
|
||||
secret,
|
||||
Buffer.from(iv, 'base64')
|
||||
);
|
||||
decipher.setAuthTag(Buffer.from(tag, 'base64'));
|
||||
|
||||
let cleartext = decipher.update(ciphertext, 'base64', 'utf8');
|
||||
cleartext += decipher.final('utf8');
|
||||
|
||||
return cleartext;
|
||||
}
|
||||
|
||||
const createSecrets = async () => {
|
||||
const serviceToken = '';
|
||||
const serviceTokenSecret = serviceToken.substring(serviceToken.lastIndexOf('.') + 1);
|
||||
|
||||
const secretType = 'shared'; // 'shared' or 'personal'
|
||||
const secretKey = 'some_key';
|
||||
const secretValue = 'some_value';
|
||||
const secretComment = 'some_comment';
|
||||
|
||||
// 1. Get your Infisical Token data
|
||||
const { data: serviceTokenData } = await axios.get(
|
||||
`${BASE_URL}/api/v2/service-token`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 2. Decrypt the (encrypted) project key with the key from your Infisical Token
|
||||
const projectKey = decrypt({
|
||||
ciphertext: serviceTokenData.encryptedKey,
|
||||
iv: serviceTokenData.iv,
|
||||
tag: serviceTokenData.tag,
|
||||
secret: serviceTokenSecret
|
||||
});
|
||||
|
||||
// 3. Encrypt your secret with the project key
|
||||
const {
|
||||
ciphertext: secretKeyCiphertext,
|
||||
iv: secretKeyIV,
|
||||
tag: secretKeyTag
|
||||
} = encrypt({
|
||||
text: secretKey,
|
||||
secret: projectKey
|
||||
});
|
||||
|
||||
const {
|
||||
ciphertext: secretValueCiphertext,
|
||||
iv: secretValueIV,
|
||||
tag: secretValueTag
|
||||
} = encrypt({
|
||||
text: secretValue,
|
||||
secret: projectKey
|
||||
});
|
||||
|
||||
const {
|
||||
ciphertext: secretCommentCiphertext,
|
||||
iv: secretCommentIV,
|
||||
tag: secretCommentTag
|
||||
} = encrypt({
|
||||
text: secretComment,
|
||||
secret: projectKey
|
||||
});
|
||||
|
||||
// 4. Send (encrypted) secret to Infisical
|
||||
await axios.post(
|
||||
`${BASE_URL}/api/v3/secrets/${secretKey}`,
|
||||
{
|
||||
workspaceId: serviceTokenData.workspace,
|
||||
environment: serviceTokenData.environment,
|
||||
type: secretType,
|
||||
secretKeyCiphertext,
|
||||
secretKeyIV,
|
||||
secretKeyTag,
|
||||
secretValueCiphertext,
|
||||
secretValueIV,
|
||||
secretValueTag,
|
||||
secretCommentCiphertext,
|
||||
secretCommentIV,
|
||||
secretCommentTag
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
createSecrets();
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Python">
|
||||
```Python
|
||||
import base64
|
||||
import requests
|
||||
from Cryptodome.Cipher import AES
|
||||
from Cryptodome.Random import get_random_bytes
|
||||
|
||||
|
||||
BASE_URL = "https://app.infisical.com"
|
||||
BLOCK_SIZE_BYTES = 16
|
||||
|
||||
|
||||
def encrypt(text, secret):
|
||||
iv = get_random_bytes(BLOCK_SIZE_BYTES)
|
||||
secret = bytes(secret, "utf-8")
|
||||
cipher = AES.new(secret, AES.MODE_GCM, iv)
|
||||
ciphertext, tag = cipher.encrypt_and_digest(text.encode("utf-8"))
|
||||
return {
|
||||
"ciphertext": base64.standard_b64encode(ciphertext).decode("utf-8"),
|
||||
"tag": base64.standard_b64encode(tag).decode("utf-8"),
|
||||
"iv": base64.standard_b64encode(iv).decode("utf-8"),
|
||||
}
|
||||
|
||||
|
||||
def decrypt(ciphertext, iv, tag, secret):
|
||||
secret = bytes(secret, "utf-8")
|
||||
iv = base64.standard_b64decode(iv)
|
||||
tag = base64.standard_b64decode(tag)
|
||||
ciphertext = base64.standard_b64decode(ciphertext)
|
||||
|
||||
cipher = AES.new(secret, AES.MODE_GCM, iv)
|
||||
cipher.update(tag)
|
||||
cleartext = cipher.decrypt(ciphertext).decode("utf-8")
|
||||
return cleartext
|
||||
|
||||
|
||||
def create_secrets():
|
||||
service_token = "your_service_token"
|
||||
service_token_secret = service_token[service_token.rindex(".") + 1 :]
|
||||
|
||||
secret_type = "shared" # "shared or "personal"
|
||||
secret_key = "some_key"
|
||||
secret_value = "some_value"
|
||||
secret_comment = "some_comment"
|
||||
|
||||
# 1. Get your Infisical Token data
|
||||
service_token_data = requests.get(
|
||||
f"{BASE_URL}/api/v2/service-token",
|
||||
headers={"Authorization": f"Bearer {service_token}"},
|
||||
).json()
|
||||
|
||||
# 2. Decrypt the (encrypted) project key with the key from your Infisical Token
|
||||
project_key = decrypt(
|
||||
ciphertext=service_token_data["encryptedKey"],
|
||||
iv=service_token_data["iv"],
|
||||
tag=service_token_data["tag"],
|
||||
secret=service_token_secret,
|
||||
)
|
||||
|
||||
# 3. Encrypt your secret with the project key
|
||||
encrypted_key_data = encrypt(text=secret_key, secret=project_key)
|
||||
encrypted_value_data = encrypt(text=secret_value, secret=project_key)
|
||||
encrypted_comment_data = encrypt(text=secret_comment, secret=project_key)
|
||||
|
||||
# 4. Send (encrypted) secret to Infisical
|
||||
requests.post(
|
||||
f"{BASE_URL}/api/v3/secrets/{secret_key}",
|
||||
json={
|
||||
"workspaceId": service_token_data["workspace"],
|
||||
"environment": service_token_data["environment"],
|
||||
"type": secret_type,
|
||||
"secretKeyCiphertext": encrypted_key_data["ciphertext"],
|
||||
"secretKeyIV": encrypted_key_data["iv"],
|
||||
"secretKeyTag": encrypted_key_data["tag"],
|
||||
"secretValueCiphertext": encrypted_value_data["ciphertext"],
|
||||
"secretValueIV": encrypted_value_data["iv"],
|
||||
"secretValueTag": encrypted_value_data["tag"],
|
||||
"secretCommentCiphertext": encrypted_comment_data["ciphertext"],
|
||||
"secretCommentIV": encrypted_comment_data["iv"],
|
||||
"secretCommentTag": encrypted_comment_data["tag"]
|
||||
},
|
||||
headers={"Authorization": f"Bearer {service_token}"},
|
||||
)
|
||||
|
||||
|
||||
create_secrets()
|
||||
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
@ -1,94 +0,0 @@
|
||||
---
|
||||
title: "Delete secret"
|
||||
description: "How to delete a secret using an Infisical Token scoped to a project and environment"
|
||||
---
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com).
|
||||
- Create either an [API Key](/api-reference/overview/authentication) or [Infisical Token](/documentation/platform/token) for your project and environment with write access enabled.
|
||||
- Grasp a basic understanding of the system and its underlying cryptography [here](/api-reference/overview/introduction).
|
||||
- [Ensure that your project is blind-indexed](../blind-indices).
|
||||
|
||||
## Example
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Javascript">
|
||||
```js
|
||||
const axios = require('axios');
|
||||
const BASE_URL = 'https://app.infisical.com';
|
||||
|
||||
const deleteSecrets = async () => {
|
||||
const serviceToken = 'your_service_token';
|
||||
const secretType = 'shared' // 'shared' or 'personal'
|
||||
const secretKey = 'some_key'
|
||||
|
||||
// 1. Get your Infisical Token data
|
||||
const { data: serviceTokenData } = await axios.get(
|
||||
`${BASE_URL}/api/v2/service-token`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 2. Delete secret from Infisical
|
||||
await axios.delete(
|
||||
`${BASE_URL}/api/v3/secrets/${secretKey}`,
|
||||
{
|
||||
workspaceId: serviceTokenData.workspace,
|
||||
environment: serviceTokenData.environment,
|
||||
type: secretType
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
deleteSecrets();
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Python">
|
||||
```Python
|
||||
import requests
|
||||
|
||||
BASE_URL = "https://app.infisical.com"
|
||||
|
||||
|
||||
def delete_secrets():
|
||||
service_token = "<your_service_token>"
|
||||
secret_type = "shared" # "shared" or "personal"
|
||||
secret_key = "some_key"
|
||||
|
||||
# 1. Get your Infisical Token data
|
||||
service_token_data = requests.get(
|
||||
f"{BASE_URL}/api/v2/service-token",
|
||||
headers={"Authorization": f"Bearer {service_token}"},
|
||||
).json()
|
||||
|
||||
# 2. Delete secret from Infisical
|
||||
requests.delete(
|
||||
f"{BASE_URL}/api/v2/secrets/{secret_key}",
|
||||
json={
|
||||
"workspaceId": service_token_data["workspace"],
|
||||
"environment": service_token_data["environment"],
|
||||
"type": secret_type
|
||||
},
|
||||
headers={"Authorization": f"Bearer {service_token}"},
|
||||
)
|
||||
|
||||
|
||||
delete_secrets()
|
||||
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
<Info>
|
||||
If using an `API_KEY` to authenticate with the Infisical API, then you should include it in the `X_API_KEY` header.
|
||||
</Info>
|
||||
|
176
docs/api-reference/overview/examples/e2ee-disabled.mdx
Normal file
176
docs/api-reference/overview/examples/e2ee-disabled.mdx
Normal file
@ -0,0 +1,176 @@
|
||||
---
|
||||
title: "E2EE Disabled"
|
||||
---
|
||||
|
||||
Using Infisical's API to read/write secrets with E2EE disabled allows you to create, update, and retrieve secrets
|
||||
in plaintext. Effectively, this means each such secret operation only requires 1 HTTP call.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Retrieve secrets">
|
||||
Retrieve all secrets for an Infisical project and environment.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="cURL">
|
||||
```bash
|
||||
curl --location --request GET 'https://app.infisical.com/api/v3/secrets/raw?environment=environment&workspaceId=workspaceId' \
|
||||
--header 'Authorization: Bearer serviceToken'
|
||||
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<ParamField query="workspaceId" type="string" required>
|
||||
The ID of the workspace
|
||||
</ParamField>
|
||||
<ParamField query="environment" type="string" required>
|
||||
The environment slug
|
||||
</ParamField>
|
||||
<ParamField query="secretPath" type="string" default="/" optional>
|
||||
Path to secrets in workspace
|
||||
</ParamField>
|
||||
</Accordion>
|
||||
<Accordion title="Create secret">
|
||||
Create a secret in Infisical.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="cURL">
|
||||
```bash
|
||||
curl --location --request POST 'https://app.infisical.com/api/v3/secrets/raw/secretName' \
|
||||
--header 'Authorization: Bearer serviceToken' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"workspaceId": "workspaceId",
|
||||
"environment": "environment",
|
||||
"type": "shared",
|
||||
"secretValue": "secretValue",
|
||||
"secretPath": "/"
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<ParamField path="secretName" type="string" required>
|
||||
Name of secret to create
|
||||
</ParamField>
|
||||
<ParamField body="workspaceId" type="string" required>
|
||||
The ID of the workspace
|
||||
</ParamField>
|
||||
<ParamField body="environment" type="string" required>
|
||||
The environment slug
|
||||
</ParamField>
|
||||
<ParamField body="secretValue" type="string" required>
|
||||
Value of secret
|
||||
</ParamField>
|
||||
<ParamField body="secretComment" type="string" optional>
|
||||
Comment of secret
|
||||
</ParamField>
|
||||
<ParamField body="secretPath" type="string" default="/" optional>
|
||||
Path to secret in workspace
|
||||
</ParamField>
|
||||
<ParamField query="type" type="string" optional default="shared">
|
||||
The type of the secret. Valid options are “shared” or “personal”
|
||||
</ParamField>
|
||||
</Accordion>
|
||||
<Accordion title="Retrieve secret">
|
||||
Retrieve a secret from Infisical.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="cURL">
|
||||
```bash
|
||||
curl --location --request GET 'https://app.infisical.com/api/v3/secrets/raw/secretName?workspaceId=workspaceId&environment=environment' \
|
||||
--header 'Authorization: Bearer serviceToken'
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<ParamField path="secretName" type="string" required>
|
||||
Name of secret to retrieve
|
||||
</ParamField>
|
||||
<ParamField query="workspaceId" type="string" required>
|
||||
The ID of the workspace
|
||||
</ParamField>
|
||||
<ParamField query="environment" type="string" required>
|
||||
The environment slug
|
||||
</ParamField>
|
||||
<ParamField query="secretPath" type="string" default="/" optional>
|
||||
Path to secrets in workspace
|
||||
</ParamField>
|
||||
<ParamField query="type" type="string" optional default="personal">
|
||||
The type of the secret. Valid options are “shared” or “personal”
|
||||
</ParamField>
|
||||
</Accordion>
|
||||
<Accordion title="Update secret">
|
||||
Update an existing secret in Infisical.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="cURL">
|
||||
```bash
|
||||
curl --location --request PATCH 'https://app.infisical.com/api/v3/secrets/raw/secretName' \
|
||||
--header 'Authorization: Bearer serviceToken' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"workspaceId": "workspaceId",
|
||||
"environment": "environment",
|
||||
"type": "shared",
|
||||
"secretValue": "secretValue",
|
||||
"secretPath": "/"
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<ParamField path="secretName" type="string" required>
|
||||
Name of secret to update
|
||||
</ParamField>
|
||||
<ParamField body="workspaceId" type="string" required>
|
||||
The ID of the workspace
|
||||
</ParamField>
|
||||
<ParamField body="environment" type="string" required>
|
||||
The environment slug
|
||||
</ParamField>
|
||||
<ParamField body="secretValue" type="string" required>
|
||||
Value of secret
|
||||
</ParamField>
|
||||
<ParamField body="secretPath" type="string" default="/" optional>
|
||||
Path to secret in workspace.
|
||||
</ParamField>
|
||||
<ParamField query="type" type="string" optional default="shared">
|
||||
The type of the secret. Valid options are “shared” or “personal”
|
||||
</ParamField>
|
||||
</Accordion>
|
||||
<Accordion title="Delete secret">
|
||||
Delete a secret in Infisical.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="cURL">
|
||||
```bash
|
||||
curl --location --request DELETE 'https://app.infisical.com/api/v3/secrets/raw/secretName' \
|
||||
--header 'Authorization: Bearer serviceToken' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"workspaceId": "workspaceId",
|
||||
"environment": "environment",
|
||||
"type": "shared",
|
||||
"secretPath": "/"
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<ParamField path="secretName" type="string" required>
|
||||
Name of secret to update
|
||||
</ParamField>
|
||||
<ParamField body="workspaceId" type="string" required>
|
||||
The ID of the workspace
|
||||
</ParamField>
|
||||
<ParamField body="environment" type="string" required>
|
||||
The environment slug
|
||||
</ParamField>
|
||||
<ParamField body="secretPath" type="string" default="/" optional>
|
||||
Path to secret in workspace.
|
||||
</ParamField>
|
||||
<ParamField query="type" type="string" optional default="personal">
|
||||
The type of the secret. Valid options are “shared” or “personal”
|
||||
</ParamField>
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
@ -1,23 +1,16 @@
|
||||
---
|
||||
title: "E2EE Mode"
|
||||
title: "E2EE Enabled"
|
||||
---
|
||||
|
||||
End-to-End Encrypted (E2EE) mode is the default way to use Infisical's API. With it, you must perform client-side encryption/decryption
|
||||
when reading/writing secrets via HTTP call to Infisical.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com).
|
||||
- Create an [Infisical Token](/documentation/platform/token) for your project and environment with write access enabled.
|
||||
- Grasp a basic understanding of the system and its underlying cryptography [here](/api-reference/overview/introduction).
|
||||
- [Ensure that your project is blind-indexed](../blind-indices).
|
||||
|
||||
Below, we showcase how to execute common CRUD operations to manage secrets in **E2EE** mode:
|
||||
Using Infisical's API to read/write secrets with E2EE enabled allows you to create, update, and retrieve secrets
|
||||
but requires you to perform client-side encryption/decryption operations. For this reason, we recommend using one of the available
|
||||
SDKs instead.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Retrieve secrets">
|
||||
<Tabs>
|
||||
<Tab title="Javascript">
|
||||
Retrieve all secrets for an Infisical project and environment.
|
||||
```js
|
||||
const crypto = require('crypto');
|
||||
const axios = require('axios');
|
||||
@ -194,6 +187,7 @@ get_secrets()
|
||||
<Accordion title="Create secret">
|
||||
<Tabs>
|
||||
<Tab title="Javascript">
|
||||
Create a secret in Infisical.
|
||||
```js
|
||||
const crypto = require('crypto');
|
||||
const axios = require('axios');
|
||||
@ -408,6 +402,7 @@ create_secrets()
|
||||
<Accordion title="Retrieve secret">
|
||||
<Tabs>
|
||||
<Tab title="Javascript">
|
||||
Retrieve a secret from Infisical.
|
||||
```js
|
||||
const crypto = require('crypto');
|
||||
const axios = require('axios');
|
||||
@ -569,6 +564,7 @@ get_secret()
|
||||
<Accordion title="Update secret">
|
||||
<Tabs>
|
||||
<Tab title="Javascript">
|
||||
Update an existing secret in Infisical.
|
||||
```js
|
||||
const crypto = require('crypto');
|
||||
const axios = require('axios');
|
||||
@ -779,6 +775,7 @@ update_secret()
|
||||
<Accordion title="Delete secret">
|
||||
<Tabs>
|
||||
<Tab title="Javascript">
|
||||
Delete a secret in Infisical.
|
||||
```js
|
||||
const axios = require('axios');
|
||||
const BASE_URL = 'https://app.infisical.com';
|
54
docs/api-reference/overview/examples/note.mdx
Normal file
54
docs/api-reference/overview/examples/note.mdx
Normal file
@ -0,0 +1,54 @@
|
||||
---
|
||||
title: "Note on E2EE"
|
||||
---
|
||||
|
||||
Each project in Infisical can have **End-to-End Encryption (E2EE)** enabled or disabled.
|
||||
|
||||
By default, all projects have **E2EE** enabled which means the server is not able to decrypt any values because all secret encryption/decryption operations occur on the client-side; this can be (optionally) disabled. However, this has limitations around functionality and ease-of-use:
|
||||
|
||||
- You cannot make HTTP calls to Infisical to read/write secrets in plaintext.
|
||||
- You cannot leverage non-E2EE features like native integrations and in-platform automations like dynamic secrets and secret rotation.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="E2EE Disabled"
|
||||
href="/api-reference/overview/examples/e2ee-disabled"
|
||||
icon="shield-halved"
|
||||
color="#3c8639"
|
||||
>
|
||||
Example read/write secrets without client-side encryption/decryption
|
||||
</Card>
|
||||
<Card
|
||||
href="/api-reference/overview/examples/e2ee-enabled"
|
||||
title="E2EE Enabled"
|
||||
icon="shield"
|
||||
color="#3775a9"
|
||||
>
|
||||
Example read/write secrets with client-side encryption/decryption
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Should I have E2EE enabled or disabled?">
|
||||
We recommend starting with having **E2EE** enabled and disabling it if:
|
||||
|
||||
- You're self-hosting Infisical, so having your instance of Infisical be able to read your secrets isn't an issue.
|
||||
- You want an easier way to read/write secrets with Infisical.
|
||||
- You need more power out of non-E2EE features such as secret rotation, dynamic secrets, etc.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="How can I enable/disable E2EE?">
|
||||
You can enable/disable E2EE for your project in Infisical in the Project Settings.
|
||||
</Accordion>
|
||||
<Accordion title="Is disabling E2EE secure?">
|
||||
It is secure and in fact how most vendors in our industry are able to offer features like secret rotation. In this mode, secrets are encrypted at rest by
|
||||
a series of keys, secured ultimately by a top-level `ROOT_ENCRYPTION_KEY` located on the server.
|
||||
|
||||
If you're concerned about Infisical Cloud's ability to read your secrets, then you may wish to
|
||||
use it with **E2EE** enabled or self-host Infisical on your own infrastructure and disable E2EE there.
|
||||
|
||||
As an organization, we do not read any customer secrets without explicit permission; access to the `ROOT_ENCRYPTION_KEY` is restricted to one individual in the organization.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
@ -1,180 +0,0 @@
|
||||
---
|
||||
title: "Retrieve secret"
|
||||
description: "How to get a secret using an Infisical Token scoped to a project and environment"
|
||||
---
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com).
|
||||
- Create an [Infisical Token](/documentation/platform/token) for your project and environment.
|
||||
- Grasp a basic understanding of the system and its underlying cryptography [here](/api-reference/overview/introduction).
|
||||
- [Ensure that your project is blind-indexed](../blind-indices).
|
||||
|
||||
## Flow
|
||||
|
||||
1. [Get your Infisical Token data](/api-reference/endpoints/service-tokens/get) including a (encrypted) project key.
|
||||
2. [Get the secret from your project and environment](/api-reference/endpoints/secrets/read-one).
|
||||
3. Decrypt the (encrypted) project key with the key from your Infisical Token.
|
||||
4. Decrypt the (encrypted) secret
|
||||
|
||||
## Example
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Javascript">
|
||||
```js
|
||||
const crypto = require('crypto');
|
||||
const axios = require('axios');
|
||||
|
||||
const BASE_URL = 'https://app.infisical.com';
|
||||
const ALGORITHM = 'aes-256-gcm';
|
||||
|
||||
const decrypt = ({ ciphertext, iv, tag, secret}) => {
|
||||
const decipher = crypto.createDecipheriv(
|
||||
ALGORITHM,
|
||||
secret,
|
||||
Buffer.from(iv, 'base64')
|
||||
);
|
||||
decipher.setAuthTag(Buffer.from(tag, 'base64'));
|
||||
|
||||
let cleartext = decipher.update(ciphertext, 'base64', 'utf8');
|
||||
cleartext += decipher.final('utf8');
|
||||
|
||||
return cleartext;
|
||||
}
|
||||
|
||||
const getSecret = async () => {
|
||||
const serviceToken = 'your_service_token';
|
||||
const serviceTokenSecret = serviceToken.substring(serviceToken.lastIndexOf('.') + 1);
|
||||
|
||||
const secretType = 'shared' // 'shared' or 'personal'
|
||||
const secretKey = 'some_key';
|
||||
|
||||
// 1. Get your Infisical Token data
|
||||
const { data: serviceTokenData } = await axios.get(
|
||||
`${BASE_URL}/api/v2/service-token`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 2. Get the secret from your project and environment
|
||||
const { data } = await axios.get(
|
||||
`${BASE_URL}/api/v3/secrets/${secretKey}?${new URLSearchParams({
|
||||
environment: serviceTokenData.environment,
|
||||
workspaceId: serviceTokenData.workspace,
|
||||
type: secretType // optional, defaults to 'shared'
|
||||
})}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const encryptedSecret = data.secret;
|
||||
|
||||
// 3. Decrypt the (encrypted) project key with the key from your Infisical Token
|
||||
const projectKey = decrypt({
|
||||
ciphertext: serviceTokenData.encryptedKey,
|
||||
iv: serviceTokenData.iv,
|
||||
tag: serviceTokenData.tag,
|
||||
secret: serviceTokenSecret
|
||||
});
|
||||
|
||||
// 4. Decrypt the (encrypted) secret value
|
||||
|
||||
const secretValue = decrypt({
|
||||
ciphertext: encryptedSecret.secretValueCiphertext,
|
||||
iv: encryptedSecret.secretValueIV,
|
||||
tag: encryptedSecret.secretValueTag,
|
||||
secret: projectKey
|
||||
});
|
||||
|
||||
console.log('secret: ', ({
|
||||
secretKey,
|
||||
secretValue
|
||||
}));
|
||||
}
|
||||
|
||||
getSecret();
|
||||
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Python">
|
||||
```Python
|
||||
import requests
|
||||
import base64
|
||||
from Cryptodome.Cipher import AES
|
||||
|
||||
|
||||
BASE_URL = "http://app.infisical.com"
|
||||
|
||||
|
||||
def decrypt(ciphertext, iv, tag, secret):
|
||||
secret = bytes(secret, "utf-8")
|
||||
iv = base64.standard_b64decode(iv)
|
||||
tag = base64.standard_b64decode(tag)
|
||||
ciphertext = base64.standard_b64decode(ciphertext)
|
||||
|
||||
cipher = AES.new(secret, AES.MODE_GCM, iv)
|
||||
cipher.update(tag)
|
||||
cleartext = cipher.decrypt(ciphertext).decode("utf-8")
|
||||
return cleartext
|
||||
|
||||
|
||||
def get_secret():
|
||||
service_token = "your_service_token"
|
||||
service_token_secret = service_token[service_token.rindex(".") + 1 :]
|
||||
|
||||
secret_type = "shared" # "shared" or "personal"
|
||||
secret_key = "some_key"
|
||||
|
||||
# 1. Get your Infisical Token data
|
||||
service_token_data = requests.get(
|
||||
f"{BASE_URL}/api/v2/service-token",
|
||||
headers={"Authorization": f"Bearer {service_token}"},
|
||||
).json()
|
||||
|
||||
# 2. Get secret from your project and environment
|
||||
data = requests.get(
|
||||
f"{BASE_URL}/api/v3/secrets/{secret_key}",
|
||||
params={
|
||||
"environment": service_token_data["environment"],
|
||||
"workspaceId": service_token_data["workspace"],
|
||||
"type": secret_type # optional, defaults to "shared"
|
||||
},
|
||||
headers={"Authorization": f"Bearer {service_token}"},
|
||||
).json()
|
||||
|
||||
encrypted_secret = data["secret"]
|
||||
|
||||
# 3. Decrypt the (encrypted) project key with the key from your Infisical Token
|
||||
project_key = decrypt(
|
||||
ciphertext=service_token_data["encryptedKey"],
|
||||
iv=service_token_data["iv"],
|
||||
tag=service_token_data["tag"],
|
||||
secret=service_token_secret,
|
||||
)
|
||||
|
||||
# 4. Decrypt the (encrypted) secret value
|
||||
secret_value = decrypt(
|
||||
ciphertext=encrypted_secret["secretValueCiphertext"],
|
||||
iv=encrypted_secret["secretValueIV"],
|
||||
tag=encrypted_secret["secretValueTag"],
|
||||
secret=project_key,
|
||||
)
|
||||
|
||||
print("secret: ", {
|
||||
"secret_key": secret_key,
|
||||
"secret_value": secret_value
|
||||
})
|
||||
|
||||
|
||||
get_secret()
|
||||
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
@ -1,195 +0,0 @@
|
||||
---
|
||||
title: "Retrieve secrets"
|
||||
description: "How to get all secrets using an Infisical Token scoped to a project and environment"
|
||||
---
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com).
|
||||
- Create an [Infisical Token](/documentation/platform/token) for your project and environment.
|
||||
- Grasp a basic understanding of the system and its underlying cryptography [here](/api-reference/overview/introduction).
|
||||
- [Ensure that your project is blind-indexed](../blind-indices).
|
||||
|
||||
## Flow
|
||||
|
||||
1. [Get your Infisical Token data](/api-reference/endpoints/service-tokens/get) including a (encrypted) project key.
|
||||
2. [Get secrets for your project and environment](/api-reference/endpoints/secrets/read).
|
||||
3. Decrypt the (encrypted) project key with the key from your Infisical Token.
|
||||
4. Decrypt the (encrypted) secrets
|
||||
|
||||
## Example
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Javascript">
|
||||
```js
|
||||
const crypto = require('crypto');
|
||||
const axios = require('axios');
|
||||
|
||||
const BASE_URL = 'https://app.infisical.com';
|
||||
const ALGORITHM = 'aes-256-gcm';
|
||||
|
||||
const decrypt = ({ ciphertext, iv, tag, secret}) => {
|
||||
const decipher = crypto.createDecipheriv(
|
||||
ALGORITHM,
|
||||
secret,
|
||||
Buffer.from(iv, 'base64')
|
||||
);
|
||||
decipher.setAuthTag(Buffer.from(tag, 'base64'));
|
||||
|
||||
let cleartext = decipher.update(ciphertext, 'base64', 'utf8');
|
||||
cleartext += decipher.final('utf8');
|
||||
|
||||
return cleartext;
|
||||
}
|
||||
|
||||
const getSecrets = async () => {
|
||||
const serviceToken = 'your_service_token';
|
||||
const serviceTokenSecret = serviceToken.substring(serviceToken.lastIndexOf('.') + 1);
|
||||
|
||||
// 1. Get your Infisical Token data
|
||||
const { data: serviceTokenData } = await axios.get(
|
||||
`${BASE_URL}/api/v2/service-token`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 2. Get secrets for your project and environment
|
||||
const { data } = await axios.get(
|
||||
`${BASE_URL}/api/v3/secrets?${new URLSearchParams({
|
||||
environment: serviceTokenData.environment,
|
||||
workspaceId: serviceTokenData.workspace
|
||||
})}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const encryptedSecrets = data.secrets;
|
||||
|
||||
// 3. Decrypt the (encrypted) project key with the key from your Infisical Token
|
||||
const projectKey = decrypt({
|
||||
ciphertext: serviceTokenData.encryptedKey,
|
||||
iv: serviceTokenData.iv,
|
||||
tag: serviceTokenData.tag,
|
||||
secret: serviceTokenSecret
|
||||
});
|
||||
|
||||
// 4. Decrypt the (encrypted) secrets
|
||||
const secrets = encryptedSecrets.map((secret) => {
|
||||
const secretKey = decrypt({
|
||||
ciphertext: secret.secretKeyCiphertext,
|
||||
iv: secret.secretKeyIV,
|
||||
tag: secret.secretKeyTag,
|
||||
secret: projectKey
|
||||
});
|
||||
|
||||
const secretValue = decrypt({
|
||||
ciphertext: secret.secretValueCiphertext,
|
||||
iv: secret.secretValueIV,
|
||||
tag: secret.secretValueTag,
|
||||
secret: projectKey
|
||||
});
|
||||
|
||||
return ({
|
||||
secretKey,
|
||||
secretValue
|
||||
});
|
||||
});
|
||||
|
||||
console.log('secrets: ', secrets);
|
||||
}
|
||||
|
||||
getSecrets();
|
||||
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Python">
|
||||
```Python
|
||||
import requests
|
||||
import base64
|
||||
from Cryptodome.Cipher import AES
|
||||
|
||||
|
||||
BASE_URL = "http://app.infisical.com"
|
||||
|
||||
|
||||
def decrypt(ciphertext, iv, tag, secret):
|
||||
secret = bytes(secret, "utf-8")
|
||||
iv = base64.standard_b64decode(iv)
|
||||
tag = base64.standard_b64decode(tag)
|
||||
ciphertext = base64.standard_b64decode(ciphertext)
|
||||
|
||||
cipher = AES.new(secret, AES.MODE_GCM, iv)
|
||||
cipher.update(tag)
|
||||
cleartext = cipher.decrypt(ciphertext).decode("utf-8")
|
||||
return cleartext
|
||||
|
||||
|
||||
def get_secrets():
|
||||
service_token = "your_service_token"
|
||||
service_token_secret = service_token[service_token.rindex(".") + 1 :]
|
||||
|
||||
# 1. Get your Infisical Token data
|
||||
service_token_data = requests.get(
|
||||
f"{BASE_URL}/api/v2/service-token",
|
||||
headers={"Authorization": f"Bearer {service_token}"},
|
||||
).json()
|
||||
|
||||
# 2. Get secrets for your project and environment
|
||||
data = requests.get(
|
||||
f"{BASE_URL}/api/v3/secrets",
|
||||
params={
|
||||
"environment": service_token_data["environment"],
|
||||
"workspaceId": service_token_data["workspace"],
|
||||
},
|
||||
headers={"Authorization": f"Bearer {service_token}"},
|
||||
).json()
|
||||
|
||||
encrypted_secrets = data["secrets"]
|
||||
|
||||
# 3. Decrypt the (encrypted) project key with the key from your Infisical Token
|
||||
project_key = decrypt(
|
||||
ciphertext=service_token_data["encryptedKey"],
|
||||
iv=service_token_data["iv"],
|
||||
tag=service_token_data["tag"],
|
||||
secret=service_token_secret,
|
||||
)
|
||||
|
||||
# 4. Decrypt the (encrypted) secrets
|
||||
secrets = []
|
||||
for secret in encrypted_secrets:
|
||||
secret_key = decrypt(
|
||||
ciphertext=secret["secretKeyCiphertext"],
|
||||
iv=secret["secretKeyIV"],
|
||||
tag=secret["secretKeyTag"],
|
||||
secret=project_key,
|
||||
)
|
||||
|
||||
secret_value = decrypt(
|
||||
ciphertext=secret["secretValueCiphertext"],
|
||||
iv=secret["secretValueIV"],
|
||||
tag=secret["secretValueTag"],
|
||||
secret=project_key,
|
||||
)
|
||||
|
||||
secrets.append(
|
||||
{
|
||||
"secret_key": secret_key,
|
||||
"secret_value": secret_value,
|
||||
}
|
||||
)
|
||||
|
||||
print("secrets:", secrets)
|
||||
|
||||
|
||||
get_secrets()
|
||||
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
@ -1,229 +0,0 @@
|
||||
---
|
||||
title: "Update secret"
|
||||
description: "How to update a secret using an Infisical Token scoped to a project and environment"
|
||||
---
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com).
|
||||
- Create an [Infisical Token](/documentation/platform/token) for your project and environment with write access enabled.
|
||||
- Grasp a basic understanding of the system and its underlying cryptography [here](/api-reference/overview/introduction).
|
||||
- [Ensure that your project is blind-indexed](../blind-indices).
|
||||
|
||||
## Flow
|
||||
|
||||
1. [Get your Infisical Token data](/api-reference/endpoints/service-tokens/get) including a (encrypted) project key.
|
||||
2. Decrypt the (encrypted) project key with the key from your Infisical Token.
|
||||
3. Encrypt your updated secret with the project key
|
||||
4. [Send (encrypted) updated secret to Infical](/api-reference/endpoints/secrets/update)
|
||||
|
||||
## Example
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Javascript">
|
||||
```js
|
||||
const crypto = require('crypto');
|
||||
const axios = require('axios');
|
||||
|
||||
const BASE_URL = 'https://app.infisical.com';
|
||||
const ALGORITHM = 'aes-256-gcm';
|
||||
const BLOCK_SIZE_BYTES = 16;
|
||||
|
||||
const encrypt = ({ text, secret }) => {
|
||||
const iv = crypto.randomBytes(BLOCK_SIZE_BYTES);
|
||||
const cipher = crypto.createCipheriv(ALGORITHM, secret, iv);
|
||||
|
||||
let ciphertext = cipher.update(text, 'utf8', 'base64');
|
||||
ciphertext += cipher.final('base64');
|
||||
return {
|
||||
ciphertext,
|
||||
iv: iv.toString('base64'),
|
||||
tag: cipher.getAuthTag().toString('base64')
|
||||
};
|
||||
}
|
||||
|
||||
const decrypt = ({ ciphertext, iv, tag, secret}) => {
|
||||
const decipher = crypto.createDecipheriv(
|
||||
ALGORITHM,
|
||||
secret,
|
||||
Buffer.from(iv, 'base64')
|
||||
);
|
||||
decipher.setAuthTag(Buffer.from(tag, 'base64'));
|
||||
|
||||
let cleartext = decipher.update(ciphertext, 'base64', 'utf8');
|
||||
cleartext += decipher.final('utf8');
|
||||
|
||||
return cleartext;
|
||||
}
|
||||
|
||||
const updateSecrets = async () => {
|
||||
const serviceToken = 'your_service_token';
|
||||
const serviceTokenSecret = serviceToken.substring(serviceToken.lastIndexOf('.') + 1);
|
||||
|
||||
const secretType = 'shared' // 'shared' or 'personal'
|
||||
const secretKey = 'some_key';
|
||||
const secretValue = 'updated_value';
|
||||
const secretComment = 'updated_comment';
|
||||
|
||||
// 1. Get your Infisical Token data
|
||||
const { data: serviceTokenData } = await axios.get(
|
||||
`${BASE_URL}/api/v2/service-token`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 2. Decrypt the (encrypted) project key with the key from your Infisical Token
|
||||
const projectKey = decrypt({
|
||||
ciphertext: serviceTokenData.encryptedKey,
|
||||
iv: serviceTokenData.iv,
|
||||
tag: serviceTokenData.tag,
|
||||
secret: serviceTokenSecret
|
||||
});
|
||||
|
||||
// 3. Encrypt your updated secret with the project key
|
||||
const {
|
||||
ciphertext: secretKeyCiphertext,
|
||||
iv: secretKeyIV,
|
||||
tag: secretKeyTag
|
||||
} = encrypt({
|
||||
text: secretKey,
|
||||
secret: projectKey
|
||||
});
|
||||
|
||||
const {
|
||||
ciphertext: secretValueCiphertext,
|
||||
iv: secretValueIV,
|
||||
tag: secretValueTag
|
||||
} = encrypt({
|
||||
text: secretValue,
|
||||
secret: projectKey
|
||||
});
|
||||
|
||||
const {
|
||||
ciphertext: secretCommentCiphertext,
|
||||
iv: secretCommentIV,
|
||||
tag: secretCommentTag
|
||||
} = encrypt({
|
||||
text: secretComment,
|
||||
secret: projectKey
|
||||
});
|
||||
|
||||
// 4. Send (encrypted) updated secret to Infisical
|
||||
await axios.patch(
|
||||
`${BASE_URL}/api/v3/secrets/${secretKey}`,
|
||||
{
|
||||
workspaceId: serviceTokenData.workspace,
|
||||
environment: serviceTokenData.environment,
|
||||
type: secretType,
|
||||
secretValueCiphertext,
|
||||
secretValueIV,
|
||||
secretValueTag,
|
||||
secretCommentCiphertext,
|
||||
secretCommentIV,
|
||||
secretCommentTag
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
updateSecrets();
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Python">
|
||||
```Python
|
||||
import base64
|
||||
import requests
|
||||
from Cryptodome.Cipher import AES
|
||||
from Cryptodome.Random import get_random_bytes
|
||||
|
||||
|
||||
BASE_URL = "https://app.infisical.com"
|
||||
BLOCK_SIZE_BYTES = 16
|
||||
|
||||
|
||||
def encrypt(text, secret):
|
||||
iv = get_random_bytes(BLOCK_SIZE_BYTES)
|
||||
secret = bytes(secret, "utf-8")
|
||||
cipher = AES.new(secret, AES.MODE_GCM, iv)
|
||||
ciphertext, tag = cipher.encrypt_and_digest(text.encode("utf-8"))
|
||||
return {
|
||||
"ciphertext": base64.standard_b64encode(ciphertext).decode("utf-8"),
|
||||
"tag": base64.standard_b64encode(tag).decode("utf-8"),
|
||||
"iv": base64.standard_b64encode(iv).decode("utf-8"),
|
||||
}
|
||||
|
||||
|
||||
def decrypt(ciphertext, iv, tag, secret):
|
||||
secret = bytes(secret, "utf-8")
|
||||
iv = base64.standard_b64decode(iv)
|
||||
tag = base64.standard_b64decode(tag)
|
||||
ciphertext = base64.standard_b64decode(ciphertext)
|
||||
|
||||
cipher = AES.new(secret, AES.MODE_GCM, iv)
|
||||
cipher.update(tag)
|
||||
cleartext = cipher.decrypt(ciphertext).decode("utf-8")
|
||||
return cleartext
|
||||
|
||||
|
||||
def update_secret():
|
||||
service_token = "your_service_token"
|
||||
service_token_secret = service_token[service_token.rindex(".") + 1 :]
|
||||
|
||||
secret_type = "shared" # "shared" or "personal"
|
||||
secret_key = "some_key"
|
||||
secret_value = "updated_value"
|
||||
secret_comment = "updated_comment"
|
||||
|
||||
# 1. Get your Infisical Token data
|
||||
service_token_data = requests.get(
|
||||
f"{BASE_URL}/api/v2/service-token",
|
||||
headers={"Authorization": f"Bearer {service_token}"},
|
||||
).json()
|
||||
|
||||
# 2. Decrypt the (encrypted) project key with the key from your Infisical Token
|
||||
project_key = decrypt(
|
||||
ciphertext=service_token_data["encryptedKey"],
|
||||
iv=service_token_data["iv"],
|
||||
tag=service_token_data["tag"],
|
||||
secret=service_token_secret,
|
||||
)
|
||||
|
||||
# 3. Encrypt your updated secret with the project key
|
||||
encrypted_key_data = encrypt(text=secret_key, secret=project_key)
|
||||
encrypted_value_data = encrypt(text=secret_value, secret=project_key)
|
||||
encrypted_comment_data = encrypt(text=secret_comment, secret=project_key)
|
||||
|
||||
# 4. Send (encrypted) updated secret to Infisical
|
||||
requests.patch(
|
||||
f"{BASE_URL}/api/v3/secrets/{secret_key}",
|
||||
json={
|
||||
"workspaceId": service_token_data["workspace"],
|
||||
"environment": service_token_data["environment"],
|
||||
"type": secret_type,
|
||||
"secretKeyCiphertext": encrypted_key_data["ciphertext"],
|
||||
"secretKeyIV": encrypted_key_data["iv"],
|
||||
"secretKeyTag": encrypted_key_data["tag"],
|
||||
"secretValueCiphertext": encrypted_value_data["ciphertext"],
|
||||
"secretValueIV": encrypted_value_data["iv"],
|
||||
"secretValueTag": encrypted_value_data["tag"],
|
||||
"secretCommentCiphertext": encrypted_comment_data["ciphertext"],
|
||||
"secretCommentIV": encrypted_comment_data["iv"],
|
||||
"secretCommentTag": encrypted_comment_data["tag"]
|
||||
},
|
||||
headers={"Authorization": f"Bearer {service_token}"},
|
||||
)
|
||||
|
||||
|
||||
update_secret()
|
||||
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
59
docs/documentation/getting-started/api.mdx
Normal file
59
docs/documentation/getting-started/api.mdx
Normal file
@ -0,0 +1,59 @@
|
||||
---
|
||||
title: "REST API"
|
||||
---
|
||||
|
||||
Infisical's Public (REST) API is the most flexible, platform-agnostic way to read/write secrets for your application.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Have a project with secrets ready in [Infisical Cloud](https://app.infisical.com).
|
||||
- Create an [Infisical Token](/documentation/platform/token) scoped to an environment in your project in Infisical.
|
||||
|
||||
To keep it simple, we're going to fetch secrets from the API with **End-to-End Encryption (E2EE)** disabled.
|
||||
|
||||
<Note>
|
||||
It's possible to use the API with **E2EE** enabled but this means learning about how encryption works with Infisical and performing client-side encryption/decryption operations yourself.
|
||||
yourself.
|
||||
|
||||
If **E2EE** is a must for your team, we recommend either using one of the [Infisical SDKs](/documentation/getting-started/sdks) or checking out the [examples for E2EE](/api-reference/overview/examples/e2ee-disabled).
|
||||
</Note>
|
||||
|
||||
## Configuration
|
||||
|
||||
Head to your Project Settings, where you created your service token, and un-check the **E2EE** setting.
|
||||
|
||||
## Retrieve Secret
|
||||
|
||||
Retrieve a secret from the project and environment in Infisical scoped to your service token by making a HTTP request with the following format/details:
|
||||
|
||||
```bash
|
||||
curl --location --request GET 'https://app.infisical.com/api/v3/secrets/raw/secretName?workspaceId=workspaceId&environment=environment' \
|
||||
--header 'Authorization: Bearer serviceToken'
|
||||
```
|
||||
|
||||
<ParamField path="secretName" type="string" required>
|
||||
Name of secret to retrieve
|
||||
</ParamField>
|
||||
<ParamField query="workspaceId" type="string" required>
|
||||
The ID of the workspace
|
||||
</ParamField>
|
||||
<ParamField query="environment" type="string" required>
|
||||
The environment slug
|
||||
</ParamField>
|
||||
<ParamField query="secretPath" type="string" default="/" optional>
|
||||
Path to secrets in workspace
|
||||
</ParamField>
|
||||
<ParamField query="type" type="string" optional default="personal">
|
||||
The type of the secret. Valid options are “shared” or “personal”
|
||||
</ParamField>
|
||||
|
||||
Depending on your application requirements, you may wish to use Infisical's API in different ways such as by retaining **E2EE**
|
||||
or fetching multiple secrets at once instead of one at a time.
|
||||
|
||||
Whatever the case, we recommend glossing over the [API Examples](/api-reference/overview/examples/note)
|
||||
to gain a deeper understanding of how you to best leverage the Infisical API for your use-case.
|
||||
|
||||
See also:
|
||||
|
||||
- Explore the [API Examples](/api-reference/overview/examples/note)
|
||||
- [API Reference](/api-reference/overview/introduction)
|
@ -42,6 +42,14 @@ Start syncing environment variables with [Infisical Cloud](https://app.infisical
|
||||
>
|
||||
Fetch and save secrets as native Kubernetes secrets
|
||||
</Card>
|
||||
<Card
|
||||
href="/documentation/getting-started/api"
|
||||
title="REST API"
|
||||
icon="cloud"
|
||||
color="#3775a9"
|
||||
>
|
||||
Fetch secrets via HTTP request
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Resources
|
||||
|
@ -92,7 +92,8 @@
|
||||
"documentation/getting-started/sdks",
|
||||
"documentation/getting-started/cli",
|
||||
"documentation/getting-started/docker",
|
||||
"documentation/getting-started/kubernetes"
|
||||
"documentation/getting-started/kubernetes",
|
||||
"documentation/getting-started/api"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -250,9 +251,9 @@
|
||||
{
|
||||
"group": "Examples",
|
||||
"pages": [
|
||||
"api-reference/overview/encryption-modes/overview",
|
||||
"api-reference/overview/encryption-modes/es-mode",
|
||||
"api-reference/overview/encryption-modes/e2ee-mode"
|
||||
"api-reference/overview/examples/note",
|
||||
"api-reference/overview/examples/e2ee-disabled",
|
||||
"api-reference/overview/examples/e2ee-enabled"
|
||||
]
|
||||
},
|
||||
"api-reference/overview/blind-indices"
|
||||
|
64
frontend/src/components/utilities/intercom/intercom.ts
Normal file
64
frontend/src/components/utilities/intercom/intercom.ts
Normal file
@ -0,0 +1,64 @@
|
||||
/* eslint-disable prefer-template */
|
||||
/* eslint-disable prefer-rest-params */
|
||||
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
||||
/* eslint-disable no-nested-ternary */
|
||||
/* eslint-disable no-unexpected-multiline */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable vars-on-top */
|
||||
/* eslint-disable no-var */
|
||||
/* eslint-disable func-names */
|
||||
// @ts-nocheck
|
||||
|
||||
import { INTERCOM_ID as APP_ID } from '@app/components/utilities/config';
|
||||
|
||||
// Loads Intercom with the snippet
|
||||
// This must be run before boot, it initializes window.Intercom
|
||||
|
||||
// prettier-ignore
|
||||
export const load = () => {
|
||||
(function(){
|
||||
var w=window;
|
||||
var ic=w.Intercom;
|
||||
|
||||
if(typeof ic==="function"){
|
||||
ic('reattach_activator');
|
||||
ic('update',w.intercomSettings);
|
||||
} else {
|
||||
var d=document;
|
||||
var i=function() {
|
||||
i.c(arguments);
|
||||
};
|
||||
i.q=[];
|
||||
i.c=function(args) {
|
||||
i.q.push(args);
|
||||
};
|
||||
w.Intercom=i;
|
||||
var l=function() {
|
||||
var s=d.createElement('script');
|
||||
s.type='text/javascript';
|
||||
s.async=true;
|
||||
s.src='https://widget.intercom.io/widget/' + APP_ID;
|
||||
var x=d.getElementsByTagName('script')[0];
|
||||
x.parentNode.insertBefore(s, x);
|
||||
};
|
||||
if (document.readyState==='complete') {
|
||||
l();
|
||||
} else if (w.attachEvent) {
|
||||
w.attachEvent('onload',l);
|
||||
} else {
|
||||
w.addEventListener('load',l,false);
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
// Initializes Intercom
|
||||
export const boot = (options = {}) => {
|
||||
window &&
|
||||
window.Intercom &&
|
||||
window.Intercom("boot", { app_id: APP_ID, ...options });
|
||||
};
|
||||
|
||||
export const update = () => {
|
||||
window && window.Intercom && window.Intercom("update");
|
||||
};
|
@ -0,0 +1,37 @@
|
||||
import { useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import {
|
||||
boot as bootIntercom,
|
||||
load as loadIntercom,
|
||||
update as updateIntercom,
|
||||
} from "./intercom";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export const IntercomProvider = ({ children }: { children: any }) => {
|
||||
const router = useRouter();
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
loadIntercom();
|
||||
bootIntercom();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const handleRouteChange = (url: string) => {
|
||||
if (typeof window !== "undefined") {
|
||||
updateIntercom();
|
||||
}
|
||||
};
|
||||
|
||||
router.events.on("routeChangeStart", handleRouteChange);
|
||||
|
||||
// If the component is unmounted, unsubscribe
|
||||
// from the event with the `off` method:
|
||||
return () => {
|
||||
router.events.off("routeChangeStart", handleRouteChange);
|
||||
};
|
||||
}, [router.events]);
|
||||
|
||||
return children;
|
||||
};
|
@ -21,7 +21,6 @@ import * as yup from 'yup';
|
||||
import { useNotificationContext } from '@app/components/context/Notifications/NotificationProvider';
|
||||
import onboardingCheck from '@app/components/utilities/checks/OnboardingCheck';
|
||||
import { tempLocalStorage } from '@app/components/utilities/checks/tempLocalStorage';
|
||||
import { INTERCOM_ID } from '@app/components/utilities/config';
|
||||
import { encryptAssymmetric } from '@app/components/utilities/cryptography/crypto';
|
||||
import {
|
||||
Button,
|
||||
@ -90,46 +89,6 @@ export const AppLayout = ({ children }: LayoutProps) => {
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
// Intercom code snippet
|
||||
(function() {
|
||||
var w=window;var ic=w.Intercom;
|
||||
if(typeof ic==="function") {
|
||||
ic('reattach_activator');
|
||||
ic('update',w.intercomSettings);
|
||||
} else {
|
||||
var d=document;
|
||||
var i=function() {
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
i.c(arguments);
|
||||
};
|
||||
i.q=[];
|
||||
i.c=function(args) {
|
||||
i.q.push(args);
|
||||
};
|
||||
w.Intercom=i;
|
||||
var l=function() {
|
||||
var s=d.createElement('script');
|
||||
s.type='text/javascript';
|
||||
s.async=true;
|
||||
s.src=`https://widget.intercom.io/widget/${INTERCOM_ID}`;
|
||||
var x=d.getElementsByTagName('script')[0];
|
||||
x.parentNode.insertBefore(s,x);};
|
||||
if(w.attachEvent) {
|
||||
w.attachEvent('onload',l);
|
||||
} else {
|
||||
w.addEventListener('load',l,false);
|
||||
}
|
||||
}
|
||||
}
|
||||
)();
|
||||
|
||||
window.Intercom('boot', {
|
||||
app_id: {INTERCOM_ID} || "undefined",
|
||||
email: user?.email || 'undefined'
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const handleRouteChange = () => {
|
||||
(window).Intercom('update');
|
||||
|
@ -11,6 +11,7 @@ import { config } from '@fortawesome/fontawesome-svg-core';
|
||||
import { QueryClientProvider } from '@tanstack/react-query';
|
||||
|
||||
import NotificationProvider from '@app/components/context/Notifications/NotificationProvider';
|
||||
import { IntercomProvider } from '@app/components/utilities/intercom/intercomProvider';
|
||||
import Telemetry from '@app/components/utilities/telemetry/Telemetry';
|
||||
import { TooltipProvider } from '@app/components/v2';
|
||||
import { publicPaths } from '@app/const';
|
||||
@ -81,9 +82,11 @@ const App = ({ Component, pageProps, ...appProps }: NextAppProp): JSX.Element =>
|
||||
<SubscriptionProvider>
|
||||
<UserProvider>
|
||||
<NotificationProvider>
|
||||
<AppLayout>
|
||||
<Component {...pageProps} />
|
||||
</AppLayout>
|
||||
<IntercomProvider>
|
||||
<AppLayout>
|
||||
<Component {...pageProps} />
|
||||
</AppLayout>
|
||||
</IntercomProvider>
|
||||
</NotificationProvider>
|
||||
</UserProvider>
|
||||
</SubscriptionProvider>
|
||||
|
Reference in New Issue
Block a user