Update MI docs for client id/secret

This commit is contained in:
Tuan Dang
2023-12-04 23:04:04 +07:00
parent 8b1eaad7b5
commit 28b617fd89
18 changed files with 52 additions and 71 deletions
@@ -531,6 +531,8 @@ export const updateMachineIdentity = async (req: Request, res: Response) => {
if (!machineIdentity) throw ResourceNotFoundError({ if (!machineIdentity) throw ResourceNotFoundError({
message: `Machine identity with id ${machineId} not found` message: `Machine identity with id ${machineId} not found`
}); });
// TODO: validate existing role (if it is currently admin then cant demote it)
const { permission } = await getUserOrgPermissions(req.user._id, machineIdentity.organization.toString()); const { permission } = await getUserOrgPermissions(req.user._id, machineIdentity.organization.toString());
@@ -19,11 +19,11 @@ fetch secrets back from the `/` path of the `development` environment in some pr
Here's a few pointers to get you acquainted with MIs: Here's a few pointers to get you acquainted with MIs:
- When you create a MI, you get issued a refresh token that can be exchanged for an access token to authenticate with the Infisical API. - Each MI has a **Client ID** for which you can generate one or more **Client Secret(s)**. Together, a **Client ID** and **Client Secret** can be exchanged for an access token to authenticate with the Infisical API.
- MIs support IP allowlisting; this means you can restrict the usage of a MI access token to a specific IP or CIDR range. - MIs support IP allowlisting; this means you can restrict the usage of a MI access token to a specific IP or CIDR range.
- MIs rely on the role-based permission system to provision access to resources like secrets. - MIs rely on the role-based permission system to provision access to resources like secrets.
- MIs support expiration, so, if specified, the refresh token of the MI will automatically be defunct after a period of time. - MIs support expiration, so, if specified, the client secret of the MI will automatically be defunct after a period of time.
- MIs tracks most recent usage of their refresh and access tokens; they also keeps track of each token's usage count. - MIs tracks most recent usage of their client secrets and access tokens; they also keep track of each token's usage count.
- MIs are editable. - MIs are editable.
## Using machine identities ## Using machine identities
@@ -32,7 +32,7 @@ In the following steps, we explore how to create and use MIs for your applicatio
<Steps> <Steps>
<Step title="Creating a MI"> <Step title="Creating a MI">
To create a machine identity, head to your Organization Settings > Access Control > Machine Identities and press **Create MI**. To create a machine identity, head to your Organization Settings > Access Control > Machine Identities and press **Create identity**.
![machine identities organization](../../images/platform/machine-identity/machine-identity-org.png) ![machine identities organization](../../images/platform/machine-identity/machine-identity-org.png)
@@ -42,23 +42,36 @@ In the following steps, we explore how to create and use MIs for your applicatio
- Name (required): A friendly name for the MI - Name (required): A friendly name for the MI
- Role (required): A role from the **Organization Roles** tab to permit the MI to access certain resources. - Role (required): A role from the **Organization Roles** tab to permit the MI to access certain resources.
- Refresh Token Expires In: The number of days from now to deactivate the MI refresh token
- Trusted IPs: The IPs or CIDR ranges that the refresh and access tokens can be used from. By default, each token is given the `0.0.0.0/0` entry representing all possible IPv4 addresses.
- Access Token TTL: The time-to-live for each acccess token in seconds. - Access Token TTL: The time-to-live for each acccess token in seconds.
- Refresh Token Rotation: Whether or not to return a new refresh token when exchanging an existing refresh token; if enabled, the existing refresh token is invalidated upon the refresh operation. - Client Secret Trusted IPs: The IPs or CIDR ranges that the **Client Secret** can be used from together with the **Client ID** to get back an access token. By default, **Client Secrets** are given the `0.0.0.0/0` entry representing all possible IPv4 addresses.
- Access Token Trusted IPs: The IPs or CIDR ranges that access tokens can be used from. By default, each token is given the `0.0.0.0/0` entry representing all possible IPv4 addresses.
<Warning> <Warning>
Restricting token usage to specific trusted IPs is a paid feature. Restricting **Client Secret** and access token usage to specific trusted IPs is a paid feature.
If you’re using Infisical Cloud, then it is available under the Pro Tier. If you’re self-hosting Infisical, then you should contact [email protected] to purchase an enterprise license to use it. If you’re using Infisical Cloud, then it is available under the Pro Tier. If you’re self-hosting Infisical, then you should contact [email protected] to purchase an enterprise license to use it.
</Warning> </Warning>
Once you've created the MI, you'll be issued a refresh token for it; copy the token and keep it handy. </Step>
<Step title="Creating a Client Secret">
In order to use the MI, you'll need the non-sensitive **Client ID**
of the MI and a **Client Secret** for it; you can think of these credentials akin to a username
and password used to authenticate with the Infisical API. With that, press on the key icon on the MI to generate a **Client Secret**
for it.
![machine identities client secret create](../../images/platform/machine-identity/machine-identity-org-client-secret.png)
![machine identities client secret create](../../images/platform/machine-identity/machine-identity-org-client-secret-create-1.png)
![machine identities client secret create](../../images/platform/machine-identity/machine-identity-org-client-secret-create-2.png)
Feel free to input any (optional) details for the **Client Secret** configuration:
- Description: A description for the **Client Secret**.
- TTL: The time-to-live for the **Client Secret**. By default, the TTL will be set to 0 which implies that the **Client Secret** will never expire.
</Step> </Step>
<Step title="Adding a MI to a project"> <Step title="Adding a MI to a project">
If you intend the MI access project-level resources such as secrets within a specific project, you should add it to that project. To enable the MI to access project-level resources such as secrets within a specific project, you should add it to that project.
To do this, head over to the project you want to add the MI to and go to Project Settings > Access Control > Machine Identities and press **Add MI**. To do this, head over to the project you want to add the MI to and go to Project Settings > Access Control > Machine Identities and press **Add identity**.
Next, select the MI you want to add to the project and the role you want to assign it. Next, select the MI you want to add to the project and the role you want to assign it.
@@ -67,22 +80,23 @@ In the following steps, we explore how to create and use MIs for your applicatio
![machine identities project create](../../images/platform/machine-identity/machine-identity-project-create.png) ![machine identities project create](../../images/platform/machine-identity/machine-identity-project-create.png)
</Step> </Step>
<Step title="Accessing the Infisical API with the MI"> <Step title="Accessing the Infisical API with the MI">
To access the Infisical API as the MI, you should first exchange the MI refresh token from **Step 1** for an access token To access the Infisical API as the MI, you should first perform a login operation
by making a request to the `/api/v3/machines/me/token` endpoint. that is to exchange the **Client ID** and **Client Secret** of the MI for an access token
by making a request to the `/api/v3/machines/login` endpoint.
#### Sample request #### Sample request
``` ```
curl --location --request POST 'http://localhost:8080/api/v3/machines/me/token' \ curl --location --request POST 'https://app.infisical.com/api/v3/machines/login' \
--header 'Content-Type: application/x-www-form-urlencoded' \ --header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'refreshToken=<token>' --data-urlencode 'clientSecret=...' \
--data-urlencode 'clientId=...'
``` ```
#### Sample response #### Sample response
``` ```
{ {
"refreshToken": "...",
"accessToken": "...", "accessToken": "...",
"expiresIn": 7200, "expiresIn": 7200,
"tokenType": "Bearer" "tokenType": "Bearer"
@@ -92,11 +106,11 @@ In the following steps, we explore how to create and use MIs for your applicatio
Next, you can use the access token to authenticate with the [Infisical API](/api-reference/overview/introduction) Next, you can use the access token to authenticate with the [Infisical API](/api-reference/overview/introduction)
<Note> <Note>
Each MI access token has a time-to-live (TLL) which you can infer from the response of the refresh token exchange; Each MI access token has a time-to-live (TLL) which you can infer from the response of the login operation;
the default TTL is `7200` seconds which can be adjusted in the **Advanced** settings of the MI. the default TTL is `7200` seconds which can be adjusted in the **Advanced** settings of the MI.
If a MI access token expires, it can no longer authenticate with the Infisical API. In this case, If a MI access token expires, it can no longer authenticate with the Infisical API. In this case,
a new access token should be obtained from the refresh token exchange. a new access token should be obtained from the aforementioned login operation.
</Note> </Note>
</Step> </Step>
</Steps> </Steps>
@@ -113,10 +127,10 @@ In the following steps, we explore how to create and use MIs for your applicatio
<Accordion title="Why is the Infisical API rejecting my machine identity credentials?"> <Accordion title="Why is the Infisical API rejecting my machine identity credentials?">
There are a few reasons for why this might happen: There are a few reasons for why this might happen:
- The refresh/access token has expired. - The client secret or access token has expired.
- The MI is insufficently permissioned to interact with the resources you wish to access. - The MI is insufficently permissioned to interact with the resources you wish to access.
- You are attempting to access a `/raw` secrets endpoint that requires your project to disable E2EE. - You are attempting to access a `/raw` secrets endpoint that requires your project to disable E2EE.
- The refresh/access token is being used from an untrusted IP. - The client secret/access token is being used from an untrusted IP.
</Accordion> </Accordion>
<Accordion title="Can you provide examples for using glob patterns?"> <Accordion title="Can you provide examples for using glob patterns?">
1. `/**`: This pattern matches all folders at any depth in the directory structure. For example, it would match folders like `/folder1/`, `/folder1/subfolder/`, and so on. 1. `/**`: This pattern matches all folders at any depth in the directory structure. For example, it would match folders like `/folder1/`, `/folder1/subfolder/`, and so on.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

@@ -27,7 +27,7 @@ export const MembersPage = withPermission(
<Tab value={TabSections.Member}>People</Tab> <Tab value={TabSections.Member}>People</Tab>
<Tab value={TabSections.MachineIdentities}> <Tab value={TabSections.MachineIdentities}>
<div className="flex items-center"> <div className="flex items-center">
<p>App Clients</p> <p>Machine Identities</p>
<div className="ml-2 rounded-md text-yellow text-sm inline-block bg-yellow/20 px-1.5 pb-[0.03rem] pt-[0.04rem] opacity-80 hover:opacity-100 cursor-default"> <div className="ml-2 rounded-md text-yellow text-sm inline-block bg-yellow/20 px-1.5 pb-[0.03rem] pt-[0.04rem] opacity-80 hover:opacity-100 cursor-default">
New New
</div> </div>
@@ -263,7 +263,7 @@ export const AddMachineIdentityModal = ({
reset(); reset();
}} }}
> >
<ModalContent title={`${popUp?.machineIdentity?.data ? "Update" : "Create"} App Client`}> <ModalContent title={`${popUp?.machineIdentity?.data ? "Update" : "Create"} Machine Identity`}>
<form onSubmit={handleSubmit(onFormSubmit)}> <form onSubmit={handleSubmit(onFormSubmit)}>
<Tabs defaultValue={TabSections.General}> <Tabs defaultValue={TabSections.General}>
<TabList> <TabList>
@@ -270,7 +270,7 @@ export const CreateClientSecretModal = ({
{!isLoading && data && data?.length === 0 && ( {!isLoading && data && data?.length === 0 && (
<Tr> <Tr>
<Td colSpan={4}> <Td colSpan={4}>
<EmptyState title="No client secrets have been created for this app client yet" icon={faKey} /> <EmptyState title="No client secrets have been created for this machine identity yet" icon={faKey} />
</Td> </Td>
</Tr> </Tr>
)} )}
@@ -51,7 +51,7 @@ export const MachineIdentitySection = withPermission(
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"> <div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="flex justify-between mb-8"> <div className="flex justify-between mb-8">
<p className="text-xl font-semibold text-mineshaft-100"> <p className="text-xl font-semibold text-mineshaft-100">
App Clients Machine Identities
</p> </p>
<OrgPermissionCan <OrgPermissionCan
I={OrgPermissionActions.Create} I={OrgPermissionActions.Create}
@@ -65,7 +65,7 @@ export const MachineIdentitySection = withPermission(
onClick={() => handlePopUpOpen("machineIdentity")} onClick={() => handlePopUpOpen("machineIdentity")}
isDisabled={!isAllowed} isDisabled={!isAllowed}
> >
Create client Create identity
</Button> </Button>
)} )}
</OrgPermissionCan> </OrgPermissionCan>
@@ -151,29 +151,9 @@ export const MachineIdentityTable = ({
customRole customRole
}) => { }) => {
return ( return (
<Tr className="h-10" key={`st-v3-${_id}`}> <Tr className="h-10" key={`machine-identity-${_id}`}>
<Td>{name}</Td> <Td>{name}</Td>
<Td>{clientId}</Td> <Td>{clientId}</Td>
{/* <Td>
<OrgPermissionCan
I={OrgPermissionActions.Edit}
a={OrgPermissionSubjects.MachineIdentity}
>
{(isAllowed) => (
<Switch
id={`enable-service-token-${_id}`}
onCheckedChange={(value) => handleToggleStatus({
serviceTokenDataId: _id,
isActive: value
})}
isChecked={isActive}
isDisabled={!isAllowed}
>
<p className="w-12 mr-4">{isActive ? "Active" : "Inactive"}</p>
</Switch>
)}
</OrgPermissionCan>
</Td> */}
<Td> <Td>
<OrgPermissionCan <OrgPermissionCan
I={OrgPermissionActions.Edit} I={OrgPermissionActions.Edit}
@@ -206,24 +186,8 @@ export const MachineIdentityTable = ({
}} }}
</OrgPermissionCan> </OrgPermissionCan>
</Td> </Td>
{/* <Td> <Td>
{trustedIps.map(({ <div className="flex justify-end items-center">
_id: trustedIpId,
ipAddress,
prefix
}) => {
return (
<p key={`service-token-${_id}-}-trusted-ip-${trustedIpId}`}>
{`${ipAddress}${prefix !== undefined ? `/${prefix}` : ""}`}
</p>
);
})}
</Td> */}
{/* <Td>{accessTokenTTL}</Td> */}
{/* <Td>{format(new Date(createdAt), "yyyy-MM-dd")}</Td> */}
{/* <Td>{expiresAt ? format(new Date(expiresAt), "yyyy-MM-dd") : "-"}</Td> */}
<Td className="flex justify-end">
<Tooltip content="Manage client secrets"> <Tooltip content="Manage client secrets">
<IconButton <IconButton
onClick={async () => { onClick={async () => {
@@ -292,6 +256,7 @@ export const MachineIdentityTable = ({
</IconButton> </IconButton>
)} )}
</OrgPermissionCan> </OrgPermissionCan>
</div>
</Td> </Td>
</Tr> </Tr>
); );
@@ -299,7 +264,7 @@ export const MachineIdentityTable = ({
{!isLoading && data && data?.length === 0 && ( {!isLoading && data && data?.length === 0 && (
<Tr> <Tr>
<Td colSpan={7}> <Td colSpan={7}>
<EmptyState title="No app clients have been created in this organization" icon={faServer} /> <EmptyState title="No machine identities have been created in this organization" icon={faServer} />
</Td> </Td>
</Tr> </Tr>
)} )}
@@ -32,7 +32,7 @@ export const MembersPage = withProjectPermission(
<Tab value={TabSections.Member}>People</Tab> <Tab value={TabSections.Member}>People</Tab>
<Tab value={TabSections.MachineIdentities}> <Tab value={TabSections.MachineIdentities}>
<div className="flex items-center"> <div className="flex items-center">
<p>App Clients</p> <p>Machine Identities</p>
<div className="ml-2 rounded-md text-yellow text-sm inline-block bg-yellow/20 px-1.5 pb-[0.03rem] pt-[0.04rem] opacity-80 hover:opacity-100 cursor-default"> <div className="ml-2 rounded-md text-yellow text-sm inline-block bg-yellow/20 px-1.5 pb-[0.03rem] pt-[0.04rem] opacity-80 hover:opacity-100 cursor-default">
New New
</div> </div>
@@ -120,7 +120,7 @@ export const AddMachineIdentityModal = ({
reset(); reset();
}} }}
> >
<ModalContent title="Add App Client to Project"> <ModalContent title="Add Machine Identity to Project">
{filteredMachineMembershipOrgs.length ? ( {filteredMachineMembershipOrgs.length ? (
<form onSubmit={handleSubmit(onFormSubmit)}> <form onSubmit={handleSubmit(onFormSubmit)}>
<Controller <Controller
@@ -129,7 +129,7 @@ export const AddMachineIdentityModal = ({
defaultValue={filteredMachineMembershipOrgs?.[0]?._id} defaultValue={filteredMachineMembershipOrgs?.[0]?._id}
render={({ field: { onChange, ...field }, fieldState: { error } }) => ( render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl <FormControl
label="App Client" label="Machine Identity"
errorText={error?.message} errorText={error?.message}
isError={Boolean(error)} isError={Boolean(error)}
> >
@@ -62,7 +62,7 @@ export const MachineIdentitySection = withProjectPermission(
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"> <div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="flex justify-between mb-8"> <div className="flex justify-between mb-8">
<p className="text-xl font-semibold text-mineshaft-100"> <p className="text-xl font-semibold text-mineshaft-100">
App Clients Machine Identities
</p> </p>
<ProjectPermissionCan <ProjectPermissionCan
I={ProjectPermissionActions.Create} I={ProjectPermissionActions.Create}
@@ -76,7 +76,7 @@ export const MachineIdentitySection = withProjectPermission(
onClick={() => handlePopUpOpen("machineIdentity")} onClick={() => handlePopUpOpen("machineIdentity")}
isDisabled={!isAllowed} isDisabled={!isAllowed}
> >
Add client Add identity
</Button> </Button>
)} )}
</ProjectPermissionCan> </ProjectPermissionCan>
@@ -188,7 +188,7 @@ export const MachineIdentityTable = ({
{!isLoading && data && data?.length === 0 && ( {!isLoading && data && data?.length === 0 && (
<Tr> <Tr>
<Td colSpan={7}> <Td colSpan={7}>
<EmptyState title="No app clients have been added to this project" icon={faServer} /> <EmptyState title="No machine identities have been added to this project" icon={faServer} />
</Td> </Td>
</Tr> </Tr>
)} )}