Update docs for blind indices and secrets v3 endpoints

This commit is contained in:
Tuan Dang
2023-04-19 18:15:35 +03:00
parent ad5852fe3a
commit 9f944135b9
18 changed files with 1449 additions and 209 deletions

View File

@@ -1532,7 +1532,15 @@
"/api/v1/invite-org/signup": {
"post": {
"description": "",
"parameters": [],
"parameters": [
{
"name": "host",
"in": "header",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
@@ -2071,6 +2079,15 @@
"targetEnvironment": {
"example": "any"
},
"targetEnvironmentId": {
"example": "any"
},
"targetService": {
"example": "any"
},
"targetServiceId": {
"example": "any"
},
"owner": {
"example": "any"
},
@@ -2297,6 +2314,13 @@
"schema": {
"type": "string"
}
},
{
"name": "teamId",
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
@@ -2309,6 +2333,107 @@
}
}
},
"/api/v1/integration-auth/{integrationAuthId}/teams": {
"get": {
"description": "",
"parameters": [
{
"name": "integrationAuthId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/api/v1/integration-auth/{integrationAuthId}/vercel/branches": {
"get": {
"description": "",
"parameters": [
{
"name": "integrationAuthId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "appId",
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/api/v1/integration-auth/{integrationAuthId}/railway/environments": {
"get": {
"description": "",
"parameters": [
{
"name": "integrationAuthId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "appId",
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/api/v1/integration-auth/{integrationAuthId}/railway/services": {
"get": {
"description": "",
"parameters": [
{
"name": "integrationAuthId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "appId",
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/api/v2/signup/complete-account/signup": {
"post": {
"description": "",
@@ -2870,9 +2995,6 @@
}
}
}
},
"400": {
"description": "Bad Request"
}
},
"security": [
@@ -2882,6 +3004,26 @@
]
}
},
"/api/v2/organizations/{organizationId}/service-accounts": {
"get": {
"description": "",
"parameters": [
{
"name": "organizationId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/api/v2/workspace/{workspaceId}/environments": {
"post": {
"description": "",
@@ -4018,9 +4160,6 @@
"responses": {
"200": {
"description": "OK"
},
"400": {
"description": "Bad Request"
}
},
"requestBody": {
@@ -4073,6 +4212,138 @@
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/api/v2/service-accounts/me": {
"get": {
"description": "",
"parameters": [],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/api/v2/service-accounts/{serviceAccountId}": {
"get": {
"description": "",
"parameters": [
{
"name": "serviceAccountId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
},
"delete": {
"description": "",
"parameters": [
{
"name": "serviceAccountId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/api/v2/service-accounts/": {
"post": {
"description": "",
"parameters": [],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/api/v2/service-accounts/{serviceAccountId}/name": {
"patch": {
"description": "",
"parameters": [
{
"name": "serviceAccountId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"name": {
"example": "any"
}
}
}
}
}
}
}
},
"/api/v2/service-accounts/{serviceAccountId}/permissions/workspace": {
"get": {
"description": "",
"parameters": [
{
"name": "serviceAccountId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
},
"post": {
"description": "",
"parameters": [
{
"name": "serviceAccountId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
@@ -4080,6 +4351,90 @@
"400": {
"description": "Bad Request"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"environment": {
"example": "any"
},
"workspaceId": {
"example": "any"
},
"read": {
"example": "any"
},
"write": {
"example": "any"
},
"encryptedKey": {
"example": "any"
},
"nonce": {
"example": "any"
}
}
}
}
}
}
}
},
"/api/v2/service-accounts/{serviceAccountId}/permissions/workspace/{serviceAccountWorkspacePermissionId}": {
"delete": {
"description": "",
"parameters": [
{
"name": "serviceAccountId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "serviceAccountWorkspacePermissionId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/api/v2/service-accounts/{serviceAccountId}/keys": {
"get": {
"description": "",
"parameters": [
{
"name": "serviceAccountId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "workspaceId",
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
@@ -4149,6 +4504,297 @@
}
}
},
"/api/v3/secrets/": {
"get": {
"description": "",
"parameters": [
{
"name": "workspaceId",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "environment",
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/api/v3/secrets/{secretName}": {
"post": {
"description": "",
"parameters": [
{
"name": "secretName",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"workspaceId": {
"example": "any"
},
"environment": {
"example": "any"
},
"type": {
"example": "any"
},
"secretKeyCiphertext": {
"example": "any"
},
"secretKeyIV": {
"example": "any"
},
"secretKeyTag": {
"example": "any"
},
"secretValueCiphertext": {
"example": "any"
},
"secretValueIV": {
"example": "any"
},
"secretValueTag": {
"example": "any"
},
"secretCommentCiphertext": {
"example": "any"
},
"secretCommentIV": {
"example": "any"
},
"secretCommentTag": {
"example": "any"
}
}
}
}
}
}
},
"get": {
"description": "",
"parameters": [
{
"name": "secretName",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "workspaceId",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "environment",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "type",
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
},
"patch": {
"description": "",
"parameters": [
{
"name": "secretName",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"workspaceId": {
"example": "any"
},
"environment": {
"example": "any"
},
"type": {
"example": "any"
},
"secretValueCiphertext": {
"example": "any"
},
"secretValueIV": {
"example": "any"
},
"secretValueTag": {
"example": "any"
}
}
}
}
}
}
},
"delete": {
"description": "",
"parameters": [
{
"name": "secretName",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"workspaceId": {
"example": "any"
},
"environment": {
"example": "any"
},
"type": {
"example": "any"
}
}
}
}
}
}
}
},
"/api/v3/workspaces/{workspaceId}/secrets/blind-index-status": {
"get": {
"description": "",
"parameters": [
{
"name": "workspaceId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/api/v3/workspaces/{workspaceId}/secrets": {
"get": {
"description": "",
"parameters": [
{
"name": "workspaceId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/api/v3/workspaces/{workspaceId}/secrets/names": {
"post": {
"description": "",
"parameters": [
{
"name": "workspaceId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"secretsToUpdate": {
"example": "any"
}
}
}
}
}
}
}
},
"/api/status": {
"get": {
"description": "",

View File

@@ -220,7 +220,7 @@ const generateOpenAPISpec = async () => {
const outputJSONFile = '../spec.json';
const outputYAMLFile = '../docs/spec.yaml';
const endpointsFiles = ['../src/app.ts'];
const endpointsFiles = ['../src/index.ts'];
const spec = await swaggerAutogen(outputJSONFile, endpointsFiles, doc);
await fs.writeFile(outputYAMLFile, yaml.dump(spec.data));

View File

@@ -1,11 +1,11 @@
---
title: "Create"
openapi: "POST /api/v2/secrets/"
openapi: "POST /api/v3/secrets/{secretName}"
---
<Tip>
Using this route requires understanding Infisical's system and cryptography.
It may be helpful to read through the
[introduction](/api-reference/overview/introduction) and [guide for creating
secrets](/api-reference/overview/examples/create-secrets).
secrets](/api-reference/overview/examples/create-secret).
</Tip>

View File

@@ -1,4 +1,4 @@
---
title: "Delete"
openapi: "DELETE /api/v2/secrets/"
openapi: "DELETE /api/v3/secrets/{secretName}"
---

View File

@@ -0,0 +1,11 @@
---
title: "Retrieve"
openapi: "GET /api/v3/secrets/{secretName}"
---
<Tip>
Using this route requires understanding Infisical's system and cryptography.
It may be helpful to read through the
[introduction](/api-reference/overview/introduction) and [guide for retrieving
secrets](/api-reference/overview/examples/retrieve-secret).
</Tip>

View File

@@ -1,11 +1,11 @@
---
title: "Retrieve"
openapi: "GET /api/v2/secrets/"
title: "Retrieve All"
openapi: "GET /api/v3/secrets/"
---
<Tip>
Using this route requires understanding Infisical's system and cryptography.
It may be helpful to read through the
[introduction](/api-reference/overview/introduction) and [guide for retrieving
secrets](/api-reference/overview/examples/retrieve-secrets).
secrets](/api-reference/overview/examples/retrieve-secret).
</Tip>

View File

@@ -1,11 +1,11 @@
---
title: "Update"
openapi: "PATCH /api/v2/secrets/"
openapi: "PATCH /api/v3/secrets/{secretName}"
---
<Tip>
Using this route requires understanding Infisical's system and cryptography.
It may be helpful to read through the
[introduction](/api-reference/overview/introduction) and [guide for updating
secrets](/api-reference/overview/examples/update-secrets).
secrets](/api-reference/overview/examples/update-secret).
</Tip>

View File

@@ -0,0 +1,18 @@
---
title: "Blind Indices"
---
In April 2023, we added the capability for users to query for secrets by name to improve the user experience of Infisical. Previously, it was only possible to query by id of the secret or fetch all secrets belonging to a project and environment.
Blind indexing must be enabled for projects created prior to April 2023 to take effect. If your project can be blind indexed, then you'll see a section in your project settings appear as shown below:
![project enable blind indices](../../images/project-settings-blind-indices.png)
It works using virtually irreversible blind indices generated by applying `argon2id` to the name of each secret and a random 128-bit salt assigned to each project on the server. We continue to keep the values of secrets E2EE by default.
You can read more about it [here](/security/mechanics).
<Note>
As previously mentioned, all projects made after April 2023 are automatically blind indexed. If you created a project before this date, you have to enable it manually in your project settings.
</Note>

View File

@@ -1,21 +1,21 @@
---
title: "Create secrets"
title: "Create secret"
description: "How to add a secret using an Infisical Token scoped to a project and environment"
---
In this example, we demonstrate how to add secrets to a project and environment using an Infisical Token.
Prerequisites:
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
- Create an [Infisical Token](../../../getting-started/dashboard/token) for your project and environment.
- Set up and add envars to [Infisical Cloud](https://app.infisical.com).
- Create an [Infisical Token](../../../getting-started/dashboard/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(s) with the project key
4. [Send (encrypted) secret(s) to Infical](/api-reference/endpoints/secrets/create)
3. Encrypt your secret with the project key
4. [Send (encrypted) secret to Infisical](/api-reference/endpoints/secrets/create)
## Example
@@ -84,7 +84,7 @@ const createSecrets = async () => {
secret: serviceTokenSecret
});
// 3. Encrypt your secret(s) with the project key
// 3. Encrypt your secret with the project key
const {
ciphertext: secretKeyCiphertext,
iv: secretKeyIV,
@@ -111,27 +111,23 @@ const createSecrets = async () => {
text: secretComment,
secret: projectKey
});
const secret = {
type: secretType,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag
}
// 4. Send (encrypted) secret(s) to Infisical
// 4. Send (encrypted) secret to Infisical
await axios.post(
`${BASE_URL}/api/v2/secrets`,
`${BASE_URL}/api/v3/secrets/${secretKey}`,
{
workspaceId: serviceTokenData.workspace,
environment: serviceTokenData.environment,
secrets: [secret]
type: secretType,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag
},
{
headers: {
@@ -204,31 +200,27 @@ def create_secrets():
secret=service_token_secret,
)
# 3. Encrypt your secret(s) with the project key
# 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)
secret = {
"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"],
}
# 4. Send (encrypted) secret (s) to Infisical
# 4. Send (encrypted) secret to Infisical
requests.post(
f"{BASE_URL}/api/v2/secrets",
f"{BASE_URL}/api/v3/secrets/{secret_key}",
json={
"workspaceId": service_token_data["workspace"],
"environment": service_token_data["environment"],
"secrets": [secret],
"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}"},
)
@@ -238,10 +230,4 @@ create_secrets()
```
</Tab>
</Tabs>
<Info>
This example uses [TweetNaCl.js](https://tweetnacl.js.org/#/), a port of
TweetNacl/Nacl, to perform asymmeric decryption of the project key but there
are ports of NaCl available in every major language.
</Info>
</Tabs>

View File

@@ -0,0 +1,94 @@
---
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](../../../getting-started/dashboard/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>

View File

@@ -1,70 +0,0 @@
---
title: "Delete secrets"
---
In this example, we demonstrate how to delete secrets
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](../../../getting-started/dashboard/token) for your project and environment.
- Grasp a basic understanding of the system and its underlying cryptography [here](/api-reference/overview/introduction).
## 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 secretId = 'id_of_secret_to_delete';
// 6. Send ID(s) of secret(s) to delete to the Infisical API
await axios.delete(
`${BASE_URL}/api/v2/secrets`,
{
secretIds: [secretId],
},
{
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_id = "id_of_secret_to_delete"
# Send ID(s) of secret(s) to delete to the Infisical API
requests.delete(
f"{BASE_URL}/api/v2/secrets",
json={"secretIds": [secret_id]},
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>

View File

@@ -0,0 +1,180 @@
---
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](../../../getting-started/dashboard/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>

View File

@@ -1,14 +1,14 @@
---
title: "Retrieve secrets"
description: "How to get all secrets using an Infisical Token scoped to a project and environment"
---
In this example, we demonstrate how to retrieve secrets from a project and environment using an Infisical Token.
Prerequisites:
- Set up and add envars to [Infisical Cloud](https://app.infisical.com).
- Create an [Infisical Token](../../../getting-started/dashboard/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
@@ -58,7 +58,7 @@ const getSecrets = async () => {
// 2. Get secrets for your project and environment
const { data } = await axios.get(
`${BASE_URL}/api/v2/secrets?${new URLSearchParams({
`${BASE_URL}/api/v3/secrets?${new URLSearchParams({
environment: serviceTokenData.environment,
workspaceId: serviceTokenData.workspace
})}`,
@@ -143,7 +143,7 @@ def get_secrets():
# 2. Get secrets for your project and environment
data = requests.get(
f"{BASE_URL}/api/v2/secrets",
f"{BASE_URL}/api/v3/secrets",
params={
"environment": service_token_data["environment"],
"workspaceId": service_token_data["workspace"],
@@ -192,10 +192,4 @@ get_secrets()
```
</Tab>
</Tabs>
<Info>
This example uses [TweetNaCl.js](https://tweetnacl.js.org/#/), a port of
TweetNacl/Nacl, to perform asymmeric decryption of the project key but there
are ports of NaCl available in every major language.
</Info>
</Tabs>

View File

@@ -1,21 +1,21 @@
---
title: "Update secrets"
title: "Update secret"
description: "How to update a secret using an Infisical Token scoped to a project and environment"
---
In this example, we demonstrate how to update secrets using an Infisical Token.
Prerequisites:
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
- Create an [Infisical Token](../../../getting-started/dashboard/token) for your project and environment.
- Set up and add envars to [Infisical Cloud](https://app.infisical.com).
- Create an [Infisical Token](../../../getting-started/dashboard/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(s) with the project key
4. [Send (encrypted) updated secret(s) to Infical](/api-reference/endpoints/secrets/update)
3. Encrypt your updated secret with the project key
4. [Send (encrypted) updated secret to Infical](/api-reference/endpoints/secrets/update)
## Example
@@ -60,7 +60,7 @@ const updateSecrets = async () => {
const serviceToken = 'your_service_token';
const serviceTokenSecret = serviceToken.substring(serviceToken.lastIndexOf('.') + 1);
const secretId = 'id_of_secret_to_update';
const secretType = 'shared' // 'shared' or 'personal'
const secretKey = 'some_key';
const secretValue = 'updated_value';
const secretComment = 'updated_comment';
@@ -83,7 +83,7 @@ const updateSecrets = async () => {
secret: serviceTokenSecret
});
// 3. Encrypt your updated secret(s) with the project key
// 3. Encrypt your updated secret with the project key
const {
ciphertext: secretKeyCiphertext,
iv: secretKeyIV,
@@ -110,27 +110,20 @@ const updateSecrets = async () => {
text: secretComment,
secret: projectKey
});
const secret = {
id: secretId,
workspace: serviceTokenData.workspace,
environment: serviceTokenData.environment,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag
}
// 4. Send (encrypted) updated secret(s) to Infisical
// 4. Send (encrypted) updated secret to Infisical
await axios.patch(
`${BASE_URL}/api/v2/secrets`,
`${BASE_URL}/api/v3/secrets/${secretKey}`,
{
secrets: [secret]
workspaceId: serviceTokenData.workspace,
environment: serviceTokenData.environment,
type: secretType,
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag
},
{
headers: {
@@ -184,7 +177,7 @@ def update_secret():
service_token = "your_service_token"
service_token_secret = service_token[service_token.rindex(".") + 1 :]
secret_id = "id_of_secret_to_update"
secret_type = "shared" # "shared" or "personal"
secret_key = "some_key"
secret_value = "updated_value"
secret_comment = "updated_comment"
@@ -203,30 +196,28 @@ def update_secret():
secret=service_token_secret,
)
# 3. Encrypt your updated secret(s) with the project key
# 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)
secret = {
"id": secret_id,
"workspace": service_token_data["workspace"],
"environment": service_token_data["environment"],
"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"],
}
# 4. Send (encrypted) updated secret(s) to Infisical
# 4. Send (encrypted) updated secret to Infisical
requests.patch(
f"{BASE_URL}/api/v2/secrets",
json={"secrets": [secret]},
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}"},
)
@@ -235,10 +226,4 @@ update_secret()
```
</Tab>
</Tabs>
<Info>
This example uses [TweetNaCl.js](https://tweetnacl.js.org/#/), a port of
TweetNacl/Nacl, to perform asymmeric decryption of the project key but there
are ports of NaCl available in every major language.
</Info>
</Tabs>

View File

@@ -14,9 +14,13 @@ With the Public API, users can create, read, update, and delete secrets, as well
If you decide to make your own requests using the API reference instead, be prepared for a steeper learning curve and more manual work.
</Warning>
<Warning>
In April 2023, we added the capability for users to query for secrets by name to improve the user experience of Infisical. If your project was created prior to April 2023, please read and follow the section on [blind indices](./blind-indices) and how to enable them for better usage of Infisical.
</Warning>
## Concepts
Using Infisical's API to manage secrets requires a basic understanding of the system and its underlying cryptography detailed [here](/security/overview).
Using Infisical's API to manage secrets requires a basic understanding of the system and its underlying cryptography detailed [here](/security/overview). A few key points:
- Each user has a public/private key pair that is stored with the platform; private keys are encrypted locally by protected keys that are encrypted by keys derived from Argon2id applied to the user's password before being sent off to the server during the account signup process.
- Each (encrypted) secret belongs to a project and environment.

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 KiB

View File

@@ -205,13 +205,15 @@
"pages": [
"api-reference/overview/introduction",
"api-reference/overview/authentication",
"api-reference/overview/blind-indices",
{
"group": "Examples",
"pages": [
"api-reference/overview/examples/create-secrets",
"api-reference/overview/examples/retrieve-secrets",
"api-reference/overview/examples/update-secrets",
"api-reference/overview/examples/delete-secrets"
"api-reference/overview/examples/create-secret",
"api-reference/overview/examples/retrieve-secret",
"api-reference/overview/examples/update-secret",
"api-reference/overview/examples/delete-secret"
]
}
]
@@ -250,8 +252,9 @@
{
"group": "Secrets",
"pages": [
"api-reference/endpoints/secrets/create",
"api-reference/endpoints/secrets/read",
"api-reference/endpoints/secrets/create",
"api-reference/endpoints/secrets/read-one",
"api-reference/endpoints/secrets/update",
"api-reference/endpoints/secrets/delete",
"api-reference/endpoints/secrets/versions",

View File

@@ -940,7 +940,11 @@ paths:
/api/v1/invite-org/signup:
post:
description: ''
parameters: []
parameters:
- name: host
in: header
schema:
type: string
responses:
'200':
description: OK
@@ -1277,6 +1281,12 @@ paths:
example: any
targetEnvironment:
example: any
targetEnvironmentId:
example: any
targetService:
example: any
targetServiceId:
example: any
owner:
example: any
path:
@@ -1415,11 +1425,75 @@ paths:
required: true
schema:
type: string
- name: teamId
in: query
schema:
type: string
responses:
'200':
description: OK
'400':
description: Bad Request
/api/v1/integration-auth/{integrationAuthId}/teams:
get:
description: ''
parameters:
- name: integrationAuthId
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
/api/v1/integration-auth/{integrationAuthId}/vercel/branches:
get:
description: ''
parameters:
- name: integrationAuthId
in: path
required: true
schema:
type: string
- name: appId
in: query
schema:
type: string
responses:
'200':
description: OK
/api/v1/integration-auth/{integrationAuthId}/railway/environments:
get:
description: ''
parameters:
- name: integrationAuthId
in: path
required: true
schema:
type: string
- name: appId
in: query
schema:
type: string
responses:
'200':
description: OK
/api/v1/integration-auth/{integrationAuthId}/railway/services:
get:
description: ''
parameters:
- name: integrationAuthId
in: path
required: true
schema:
type: string
- name: appId
in: query
schema:
type: string
responses:
'200':
description: OK
/api/v2/signup/complete-account/signup:
post:
description: ''
@@ -1771,10 +1845,20 @@ paths:
items:
$ref: '#/components/schemas/Project'
description: Projects of organization
'400':
description: Bad Request
security:
- apiKeyAuth: []
/api/v2/organizations/{organizationId}/service-accounts:
get:
description: ''
parameters:
- name: organizationId
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
/api/v2/workspace/{workspaceId}/environments:
post:
description: ''
@@ -2467,8 +2551,6 @@ paths:
responses:
'200':
description: OK
'400':
description: Bad Request
requestBody:
content:
application/json:
@@ -2503,8 +2585,139 @@ paths:
responses:
'200':
description: OK
/api/v2/service-accounts/me:
get:
description: ''
parameters: []
responses:
'200':
description: OK
/api/v2/service-accounts/{serviceAccountId}:
get:
description: ''
parameters:
- name: serviceAccountId
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
delete:
description: ''
parameters:
- name: serviceAccountId
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
/api/v2/service-accounts/:
post:
description: ''
parameters: []
responses:
'200':
description: OK
/api/v2/service-accounts/{serviceAccountId}/name:
patch:
description: ''
parameters:
- name: serviceAccountId
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
example: any
/api/v2/service-accounts/{serviceAccountId}/permissions/workspace:
get:
description: ''
parameters:
- name: serviceAccountId
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
post:
description: ''
parameters:
- name: serviceAccountId
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
'400':
description: Bad Request
requestBody:
content:
application/json:
schema:
type: object
properties:
environment:
example: any
workspaceId:
example: any
read:
example: any
write:
example: any
encryptedKey:
example: any
nonce:
example: any
/api/v2/service-accounts/{serviceAccountId}/permissions/workspace/{serviceAccountWorkspacePermissionId}:
delete:
description: ''
parameters:
- name: serviceAccountId
in: path
required: true
schema:
type: string
- name: serviceAccountWorkspacePermissionId
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
/api/v2/service-accounts/{serviceAccountId}/keys:
get:
description: ''
parameters:
- name: serviceAccountId
in: path
required: true
schema:
type: string
- name: workspaceId
in: query
schema:
type: string
responses:
'200':
description: OK
/api/v2/api-key/:
get:
description: ''
@@ -2546,6 +2759,182 @@ paths:
description: OK
'400':
description: Bad Request
/api/v3/secrets/:
get:
description: ''
parameters:
- name: workspaceId
in: query
schema:
type: string
- name: environment
in: query
schema:
type: string
responses:
'200':
description: OK
/api/v3/secrets/{secretName}:
post:
description: ''
parameters:
- name: secretName
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
requestBody:
content:
application/json:
schema:
type: object
properties:
workspaceId:
example: any
environment:
example: any
type:
example: any
secretKeyCiphertext:
example: any
secretKeyIV:
example: any
secretKeyTag:
example: any
secretValueCiphertext:
example: any
secretValueIV:
example: any
secretValueTag:
example: any
secretCommentCiphertext:
example: any
secretCommentIV:
example: any
secretCommentTag:
example: any
get:
description: ''
parameters:
- name: secretName
in: path
required: true
schema:
type: string
- name: workspaceId
in: query
schema:
type: string
- name: environment
in: query
schema:
type: string
- name: type
in: query
schema:
type: string
responses:
'200':
description: OK
patch:
description: ''
parameters:
- name: secretName
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
requestBody:
content:
application/json:
schema:
type: object
properties:
workspaceId:
example: any
environment:
example: any
type:
example: any
secretValueCiphertext:
example: any
secretValueIV:
example: any
secretValueTag:
example: any
delete:
description: ''
parameters:
- name: secretName
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
requestBody:
content:
application/json:
schema:
type: object
properties:
workspaceId:
example: any
environment:
example: any
type:
example: any
/api/v3/workspaces/{workspaceId}/secrets/blind-index-status:
get:
description: ''
parameters:
- name: workspaceId
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
/api/v3/workspaces/{workspaceId}/secrets:
get:
description: ''
parameters:
- name: workspaceId
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
/api/v3/workspaces/{workspaceId}/secrets/names:
post:
description: ''
parameters:
- name: workspaceId
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
requestBody:
content:
application/json:
schema:
type: object
properties:
secretsToUpdate:
example: any
/api/status:
get:
description: ''