diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 00000000..01d8eea7 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/astro.config.mjs b/astro.config.mjs index 12ebe42d..327b71bf 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -724,7 +724,16 @@ export default defineConfig({ { label: 'Single Sign-On', items: [ - { autogenerate: { directory: '/aws/enterprise/sso' } }, + { slug: 'aws/enterprise/sso' }, + { slug: 'aws/enterprise/sso/azure-ad' }, + { + label: 'SCIM', + items: [ + { slug: 'aws/enterprise/sso/scim' }, + { slug: 'aws/enterprise/sso/scim/okta' }, + { slug: 'aws/enterprise/sso/scim/entra' }, + ], + }, ], }, { diff --git a/public/images/aws/SCIM_entra_add_members_search.jpg b/public/images/aws/SCIM_entra_add_members_search.jpg new file mode 100644 index 00000000..f38be31f Binary files /dev/null and b/public/images/aws/SCIM_entra_add_members_search.jpg differ diff --git a/public/images/aws/SCIM_entra_connectivity.png b/public/images/aws/SCIM_entra_connectivity.png new file mode 100644 index 00000000..4ac00fe0 Binary files /dev/null and b/public/images/aws/SCIM_entra_connectivity.png differ diff --git a/public/images/aws/SCIM_entra_create_group_member_popup.png b/public/images/aws/SCIM_entra_create_group_member_popup.png new file mode 100644 index 00000000..9c9c3545 Binary files /dev/null and b/public/images/aws/SCIM_entra_create_group_member_popup.png differ diff --git a/public/images/aws/SCIM_entra_create_new_user.png b/public/images/aws/SCIM_entra_create_new_user.png new file mode 100644 index 00000000..5680205c Binary files /dev/null and b/public/images/aws/SCIM_entra_create_new_user.png differ diff --git a/public/images/aws/SCIM_entra_created_group_member.jpg b/public/images/aws/SCIM_entra_created_group_member.jpg new file mode 100644 index 00000000..afbbca09 Binary files /dev/null and b/public/images/aws/SCIM_entra_created_group_member.jpg differ diff --git a/public/images/aws/SCIM_entra_new_group.png b/public/images/aws/SCIM_entra_new_group.png new file mode 100644 index 00000000..81c30089 Binary files /dev/null and b/public/images/aws/SCIM_entra_new_group.png differ diff --git a/src/content/docs/aws/enterprise/sso/scim.mdx b/src/content/docs/aws/enterprise/sso/scim.mdx deleted file mode 100644 index 87308b66..00000000 --- a/src/content/docs/aws/enterprise/sso/scim.mdx +++ /dev/null @@ -1,232 +0,0 @@ ---- -title: SCIM User Provisioning -description: Automating user and license provisioning in LocalStack using SCIM (System for Cross-domain Identity Management). -template: doc -tags: ['Enterprise'] ---- - -SCIM (System for Cross-domain Identity Management) allows you to automate user provisioning, deprovisioning, and license assignment in LocalStack through your identity provider (IdP). LocalStack's SCIM implementation follows the SCIM v2.0 specification and has been developed and tested with the Okta SCIM client. - -SCIM is a sub-feature of SSO and requires an active SSO configuration with at least one Identity Provider already set up. See the [Single Sign-On](/aws/enterprise/sso/) documentation before proceeding. - -All integration details — including the SCIM Base Connector URL, Bearer Auth Token, and group names per subscription — are available in the LocalStack web app under **Settings → [Single Sign-On](https://app.localstack.cloud/settings/sso)**. - -## Prerequisites - -- An active Enterprise subscription with the SCIM feature enabled -- A configured SSO Identity Provider (OIDC or SAML) -- Admin access to your organization in the LocalStack web app - -## Enabling SCIM - -In the LocalStack web app, navigate to **Settings → Single Sign-On**. For each configured Identity Provider, you will see a **SCIM User Provisioning** toggle. Enable it for the IdP you want to use for SCIM provisioning. - -:::note -Only one Identity Provider can have SCIM active at a time. -::: - -Once enabled, click **View SCIM Configuration** to access the SCIM Base Connector URL and Bearer Auth Token needed to configure your IdP. - -## Setup and Configuration - -The settings contain the **SCIM API Base Connector URL** and the **Bearer Auth Token** as shown in the image below. You can copy these values to configure your SCIM client. - -![SCIM connection details](/images/aws/SCIM-configuration.jpg) - -SCIM clients authenticate using a long-lived bearer token. The token starts with `scim-` and is displayed (masked) in the SCIM configuration panel. Use the copy icon to copy it to your clipboard. - -You can regenerate the token at any time using the refresh icon. Regenerating the token immediately invalidates the previous one — update your IdP configuration with the new token to avoid interruptions. - -## Configuring SCIM with Okta - -Use the following steps to configure SCIM Base Connector URL and Bearer Auth Token: - -1. **Select your application** — Go to **Applications → Applications** and select the application you want to enable SCIM provisioning for. -2. **Navigate to Provisioning settings** — In the application settings, go to the **Provisioning** tab and click **Integration** or **Edit** (wording may vary). -3. **Enter the SCIM connection details:** - - **SCIM connector base URL:** Paste the SCIM Base Connector URL from the LocalStack SCIM configuration panel. - - **Authentication Mode:** Select **HTTP Header**. - - **Bearer Token:** Paste the SCIM bearer token from the LocalStack SCIM configuration panel. -4. **Test the connection** — Click **Test Connector Configuration** to confirm Okta can connect successfully. -5. **Enable provisioning features** (optional) — Once the connection succeeds, enable the desired provisioning actions (Create Users, Update User Attributes, Deactivate Users) under the **To App** settings tab. There is no need to enable Sync Password, as SSO does not require a password. -6. **Save** — Save and apply the integration settings. - -:::note -The exact menu names may vary depending on Okta's UI version and app type, but the key settings are always the SCIM Base Connector URL and Bearer Auth Token under the provisioning or integration section. -::: - -### Provisioning Individual Users - -LocalStack supports full provisioning and deprovisioning of individual user accounts via SCIM. - -:::note -For security reasons, SCIM can only provision user accounts for users who do not already exist in the LocalStack web app. If a user was originally created via SCIM and later removed from your workspace, you must invite them again through the LocalStack [**Users & Licenses**](https://app.localstack.cloud/settings/members). The user will receive an email invitation and must explicitly accept it to rejoin the workspace. -::: - -1. In the Okta Admin Console, go to your application and click the **Assignments** tab. -2. Select **Assign → Assign to People**. -3. Search for and select the users you want to provision, then click **Assign** and **Done**. -4. Okta will automatically send a SCIM request to LocalStack to create the user account. The user will be visible in LocalStack and their account details will sync from Okta. - -:::tip -Legacy users (existing LocalStack accounts) can also be assigned to the Okta application, provided their email address matches the one they used to register with the LocalStack web app. -::: - -### Updating User Accounts - -Changes to user attributes (first name, last name, email) in Okta are automatically pushed to LocalStack via SCIM while the integration is active. - -### Deprovisioning Users - -1. In Okta, go to your application's **Assignments** tab. -2. Find the user you want to remove and click **Remove** next to their name. -3. Confirm the action. - -Okta will send a SCIM deprovisioning request and the user will be removed from LocalStack. - -### Provisioning Groups of Users - -Groups in Okta can be used to provision multiple users to LocalStack at once. - -#### Assigning a Group - -1. In the Okta Admin Console, go to your application and click the **Assignments** tab. -2. Select **Assign → Assign to Groups**. -3. Search for and select the groups you want to provision, then click **Assign** and **Done**. - -Okta will send a SCIM request to LocalStack to create a user account for each member of the group. Changes to a group's membership in Okta are automatically pushed to LocalStack via SCIM. - -#### Deprovisioning a Group - -1. In Okta, return to your application's **Assignments** tab. -2. Find the group and click **Remove** next to its name. -3. Confirm the action. - -Okta will send a SCIM request to remove the group's users from LocalStack. Users who were provisioned solely through this group assignment will also be deprovisioned. - -:::tip -Any changes in Okta (user/group attribute changes, group memberships, etc.) are automatically synchronized with LocalStack as long as the SCIM integration is active. -::: - -#### Migrating an Existing OpenID Connect or SAML Application - -If you have an existing OIDC or SAML app in Okta that already has SSO users assigned, follow these steps to add SCIM provisioning: - -1. On the **General** tab of your Okta application, set **Provisioning** to **SCIM**. - ![Provisioning to SCIM for SAML application](/images/aws/SCIM-SAML-provisioning.jpg) -2. Go to the **Provisioning** tab and click **Edit** to configure the SCIM connection: - - **SCIM connector base URL:** Paste the URL from LocalStack. - - **Unique identifier field for users:** Enter `userName` (the Okta default). - - **Supported provisioning actions:** Enable all available options. - - ![Adding the LocalStack settings for SAML application](/images/aws/SCIM-SAML-provisioning-2.jpg) - -3. Select **HTTP Header** as the Authentication Mode and paste the Bearer token from the LocalStack SCIM configuration panel. Click **Save**. - ![Adding the Bearer token for SAML application](/images/aws/SCIM-SAML-provisioning-3.jpg) -4. After a successful connection test, go to the **To App** tab, click **Edit**, and enable **Create Users**, **Update User Attributes**, and **Deactivate Users**. Save your changes. - ![Testing the connection for SAML application](/images/aws/SCIM-SAML-provisioning-4.jpg) -5. Click the **Assignments** tab. Okta will show error messages for users who were assigned before provisioning was enabled. Click **Provision User** and confirm the action to sync all existing users. If the task fails, you can retry it under **Dashboard → Tasks**. - ![Errors when provisioning users for SAML application](/images/aws/SCIM-SAML-provisioning-5.jpg) -6. After syncing completes, refresh the page — the error messages should be gone and all users will be fully managed via Okta SCIM. - -## License Assignment - -Licenses are assigned to users by pushing specifically named SCIM groups that correspond to your LocalStack subscriptions. - -### Group Name Format - -License group names follow this format: - -```text -{PLAN}-{EMULATOR}-{SUBSCRIPTION_ID} -``` - -For example: `Enterprise Plan-AWS-sub_1RqpMYGCs0LNOzY9UszOGJkL` - -The exact group name for each subscription is displayed in the SCIM configuration panel in the LocalStack web app. Use the subscription dropdown to select the plan you want to manage, and the correct group name will be shown for you to copy. - -:::tip -Legacy users can be added to a license assignment group in Okta, provided their email address matches their LocalStack registration email, they have been assigned to the Okta application, and the group name matches the correct subscription. -::: - -:::caution -Each user can only be a member of one license group (subscription) per organization. Assigning a user to multiple license groups will result in an error and provisioning will fail for that user. -::: - -#### Creating and Pushing a License Group in Okta - -1. Create a new Okta group named exactly as shown in the LocalStack SCIM configuration panel. -2. Add users to the group (users must already be assigned to the LocalStack SCIM application). -3. In your application, go to the **Push Groups** tab. -4. Push the group to LocalStack via SCIM. -5. Once synced, LocalStack will recognize the group and assign the corresponding license to all members. - -:::danger[License revocation risk] - -The Okta group's membership is the source of truth for license assignments on this subscription. Any change to this group in Okta (adding users, removing users, or syncing it) will reconcile the subscription's licenses to match the group exactly. Users who are licensed on this subscription but not in the Okta group will have their licenses revoked, regardless of how the license was originally assigned (manually or via SCIM). - -This means: - -- If you sync an **empty group**, every license on this subscription will be revoked. -- If you sync a **partial group** (for example, 2 users in Okta but 5 currently licensed), the 3 users not in the group will lose their licenses. - -If you are enabling SCIM on a subscription that already has licensed users, follow the [Migrating Users with Existing Licenses](#migrating-users-with-existing-licenses) steps below **before** any sync occurs. Once SCIM is enabled, manage license assignments exclusively through Okta. - -::: - -### Migrating Users with Existing Licenses - -If your organization already has users with assigned licenses and you want to manage them through SCIM: - -1. Create a license group in Okta with the correct name. -2. Add it to the application via the **Push Groups** tab. -3. Add the existing licensed users to that group through the application. Once added, they will be automatically synced (Push Status becomes **Active**) and managed through SCIM going forward. - -## Web App Roles and Permissions - -LocalStack supports configuring default roles and permissions that are applied when a user is provisioned via SCIM. These can for example be used to grant users access CI credentials or to make them workspace admins. - -Granting users permissions or assigning them to groups (e.g. 'Member', 'Admin') is not supported via SCIM but the settings in the LocalStack web app allow you to set presets that are applied when a user is provisioned via SCIM. These settings are inherited from the SSO settings. - -![SCIM user role and permission settings](/images/aws/SCIM-permissions.jpg) - -## Limitations - -- **One license group per user:** Each user can be assigned to only one license group (subscription) per organization. -- **One SCIM provider at a time:** Only one Identity Provider can have SCIM enabled at a time. -- **Provisioning is one-way:** SCIM sync goes from your IdP to LocalStack only. There is no synchronization from LocalStack back to your IdP. -- **LocalStack UI does not block manual edits:** The LocalStack web app does not prevent you from manually editing SCIM-provisioned users or their license assignments. It is strongly recommended to manage SCIM-provisioned users exclusively through your IdP to avoid inconsistencies. -- **Re-provisioning removed users requires re-invitation:** If a user was provisioned via SCIM and later removed, they cannot be re-provisioned via SCIM directly. They must be re-invited through the LocalStack **Users & Licenses** page and accept the invitation before being reassigned. - -## API Reference - -LocalStack's SCIM API is available at `/scim/v2` and implements the SCIM v2.0 specification (RFC 7644). - -### User Endpoints (`/scim/v2/Users`) - -| Method | Endpoint | Description | -| ------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `POST` | `/scim/v2/Users` | Create a SCIM user, or idempotently return an existing member when the email matches. Enforces global email uniqueness and `userName` uniqueness per org and IdP. | -| `GET` | `/scim/v2/Users` | List active SCIM-provisioned users. Supports `filter=userName eq "..."`, `startIndex`, and `count` for pagination. | -| `GET` | `/scim/v2/Users/{id}` | Retrieve a SCIM user only if they are SCIM-provisioned and active in the org; returns `404` otherwise. | -| `PATCH` | `/scim/v2/Users/{id}` | RFC 7644 PatchOp for selected fields (`name`, `emails`) and deactivation via `active:false`. Reactivation via SCIM is not supported. Patching `userName` or `externalId` is not supported. | -| `PUT` | `/scim/v2/Users/{id}` | Full replace of mutable fields (name, email) with support for deactivation via `active:false`. Reactivation via SCIM is ignored. | - -### Group Endpoints (`/scim/v2/Groups`) - -| Method | Endpoint | Description | -| -------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `POST` | `/scim/v2/Groups` | Bind an existing subscription as a SCIM group via `displayName` (format: `{PLAN}-{EMULATOR}-{subscription_id}`). Optionally assign members. Validates membership and enforces one-group-per-user per org. Returns `201` on success; `409` for insufficient seats or conflicts. | -| `GET` | `/scim/v2/Groups` | List groups (subscriptions) with their SCIM members. Supports `filter=displayName eq "..."`, `startIndex`, and `count` (max 1000). | -| `GET` | `/scim/v2/Groups/{id}` | Retrieve a group by its subscription ID with SCIM members. Returns `404` if not found. | -| `PATCH` | `/scim/v2/Groups/{id}` | RFC 7644 PatchOp (`add`, `remove`, `replace`) for members. Supports capacity checks and rollback on partial failures. | -| `PUT` | `/scim/v2/Groups/{id}` | Full replace of group membership. Omitting or passing an empty `members` array clears all members. Supports rollback on errors. | -| `DELETE` | `/scim/v2/Groups/{id}` | Delete the group binding and unassign SCIM members. Non-SCIM assignments are unaffected. Returns `204` on success. | - -### Metadata Endpoints - -| Method | Endpoint | Description | -| ------ | -------------------------------- | ----------------------------------------------------------------- | -| `GET` | `/scim/v2/ResourceTypes` | List all supported SCIM resource types (`User`, `Group`). | -| `GET` | `/scim/v2/Schemas` | List supported SCIM schemas for user and group resources. | -| `GET` | `/scim/v2/ServiceProviderConfig` | Return service provider configuration and supported capabilities. | diff --git a/src/content/docs/aws/enterprise/sso/scim/entra.md b/src/content/docs/aws/enterprise/sso/scim/entra.md new file mode 100644 index 00000000..6851cabb --- /dev/null +++ b/src/content/docs/aws/enterprise/sso/scim/entra.md @@ -0,0 +1,137 @@ +--- +title: SCIM with Entra ID +description: Configuring Microsoft Entra ID as the SCIM client for LocalStack user provisioning. +template: doc +tags: ['Enterprise'] +sidebar: + order: 3 +--- + +This page covers configuring **Microsoft Entra ID** as your SCIM client to provision users and groups into LocalStack. Before starting, make sure you've completed the steps in the [SCIM overview](/aws/enterprise/sso/scim/) to enable SCIM and obtain the **SCIM Base Connector URL** and **Bearer Auth Token** from the LocalStack web app. + +## Configuring SCIM with Microsoft Entra ID + +Use the following steps to configure SCIM provisioning from a Microsoft Entra ID Enterprise Application. + +1. **Select or create your Enterprise Application** — In the [Microsoft Entra admin center](https://entra.microsoft.com), go to **Identity → Applications → Enterprise applications** and select the application you want to enable SCIM provisioning for. If you don't have one yet, create a new non-gallery application. +2. **Navigate to Provisioning** — In the application's side menu, open **Manage → Provisioning**. On first setup, click **Get started** and set the **Provisioning Mode** to **Automatic**. +3. **Enter the SCIM connection details** under the **Connectivity** section (or **Admin Credentials** in the legacy view): + - **Authentication method:** Select **Bearer authentication**. + - **Tenant URL:** Paste the SCIM Base Connector URL from the LocalStack SCIM configuration panel. + - **Secret Token:** Paste the SCIM bearer token from the LocalStack SCIM configuration panel. + + ![Entra ID SCIM connectivity configuration](/images/aws/SCIM_entra_connectivity.png) + +4. **Test the connection and save** — Click **Test connection** to confirm Entra can reach LocalStack, then save the settings. +5. **(Recommended) Set scope** — Under **Provisioning → Settings → Scope**, select **Sync only assigned users and groups** to limit provisioning to users and groups you explicitly assign to the application. +6. **Start provisioning** — Return to the Provisioning overview and click **Start provisioning**. Entra will sync user and group changes to LocalStack every ~40 minutes; for an immediate sync of a specific user, use **Provision on Demand** from the Provisioning blade. + +:::caution +Do **NOT** enable the `aadOptscim062020` feature flag on the Entra provisioning configuration. This flag changes Entra's outbound `PATCH /Groups` semantics in a way that can cause destructive single-user member replacements. The default behavior (flag off) is what LocalStack expects. +::: + +### User Management + +#### Provisioning Individual Users + +LocalStack supports full provisioning and deprovisioning of individual user accounts via SCIM. + +:::note +For security reasons, SCIM can only provision user accounts for users who do not already exist in the LocalStack web app. If a user was originally created via SCIM and later removed from your workspace, you must invite them again through the LocalStack [**Users & Licenses**](https://app.localstack.cloud/settings/members). The user will receive an email invitation and must explicitly accept it to rejoin the workspace. +::: + +1. **Create the user in Entra** (if not already present) — In **Microsoft Entra ID → Users**, click **+ New user → Create new user** and fill in the basic details (User principal name, Display name, etc). + ![Creating a new user in Entra ID](/images/aws/SCIM_entra_create_new_user.png) + +2. **Assign the user to the LocalStack application** — Open your Enterprise Application and go to **Manage → Users and groups**. Click **+ Add user/group**, search for the user, select them, and click **Select**. + ![Selecting users to assign to the application](/images/aws/SCIM_entra_add_members_search.jpg) + +3. **Wait for sync** — On the next provisioning cycle (or via **Provision on Demand**), Entra will send a SCIM request to LocalStack to create the user account. + +:::tip +Legacy users (existing LocalStack accounts) can also be assigned to the Entra application, provided their email address matches the one they used to register with the LocalStack web app. +::: + +#### Updating User Accounts + +Changes to user attributes (first name, last name, email) in Entra are automatically pushed to LocalStack via SCIM while the integration is active. + +#### Deprovisioning Users + +1. In Entra, open the LocalStack Enterprise Application and go to **Manage → Users and groups**. +2. Find the user you want to remove and click **Remove**. +3. Confirm the action. + +Entra will send a SCIM deprovisioning request and the user will be removed from LocalStack on the next sync cycle. Disabling the user in the Entra directory itself (`accountEnabled = false`) has the same effect. + +#### Provisioning Groups of Users + +Groups in Microsoft Entra ID can be used to provision multiple users to LocalStack at once. To enable group provisioning, ensure the **Provision Microsoft Entra ID Groups** mapping is enabled in **Provisioning → Mappings**. + +##### Assigning a Group + +1. **Create a security group** — In **Microsoft Entra ID → Groups → All groups**, click **+ New group**. Choose **Security** as the group type, set the **Membership type** to **Assigned**, give the group a name, and (optionally) a description. + ![Creating a new security group in Entra ID](/images/aws/SCIM_entra_new_group.png) +2. **Add members to the group** — In the same form (or after creation, via the group's **Members** tab), select the users you want to provision. + ![Adding members to a group in Entra ID](/images/aws/SCIM_entra_create_group_member_popup.png) +3. **Assign the group to the application** — Open your Enterprise Application, go to **Manage → Users and groups**, click **+ Add user/group**, select the group, and confirm. +4. **Wait for sync** — Entra will send SCIM requests to LocalStack to provision each member on the next sync cycle. + +Changes to a group's membership in Entra are automatically pushed to LocalStack via SCIM on subsequent sync cycles. + +##### Deprovisioning a Group + +1. In Entra, open the LocalStack Enterprise Application and go to **Manage → Users and groups**. +2. Find the group and click **Remove**. +3. Confirm the action. + +Entra will send SCIM requests to remove the group's users from LocalStack. Users who were provisioned solely through this group assignment will also be deprovisioned. + +:::tip +Any changes in Entra (user/group attribute changes, group memberships, etc.) are automatically synchronized with LocalStack on subsequent sync cycles as long as the SCIM integration is active. +::: + +### Role Management + +LocalStack workspace roles (**admin** and **member**) are assigned to users by syncing SCIM groups whose name identifies the target role. The role groups themselves do not need to exist in LocalStack before the sync — they are synthetic SCIM groups keyed off the `displayName`. + +:::caution +Each user can only be in **one** role group at a time. Attempting to add a user who is already in the admin role group to the member role group (or vice versa) returns a `409` conflict. Remove the user from the previous role group first, then add to the new one. +::: + +#### Group Name Convention + +Role groups are matched by `displayName` using a case-insensitive substring check: + +- Any group whose name contains `admin` → admin role group +- Any group whose name contains `member` → member role group + +All of the following are valid names for the admin role group: + +- `LocalStack-Admin` +- `LocalStack-Admins-Prod` +- `XD5-XD5-LA1000_AuthLocalstackAdmin` + +The first time you sync a role group from Entra, LocalStack persists that `displayName` so subsequent GET responses to your IdP reflect the name you sent. You can also rename the group later via SCIM and LocalStack will track the rename. + +#### Creating a Role Group in Microsoft Entra ID + +1. In **Microsoft Entra ID → Groups → All groups**, click **+ New group**. Create a **Security** group with **Membership type: Assigned** whose name contains either `Admin` (for the admin role) or `Member` (for the member role). + ![Creating a role group in Entra ID](/images/aws/SCIM_entra_role_group.png) +2. Add users to the group (users must already be assigned to the LocalStack Enterprise Application). +3. Assign the group to the LocalStack Enterprise Application via **Manage → Users and groups**. +4. Confirm that **Provision Microsoft Entra ID Groups** is enabled under **Provisioning → Mappings**. +5. On the next provisioning cycle (or via **Provision on Demand**), Entra will sync the group to LocalStack and assign the corresponding role to all members. + +#### Moving a User Between Roles + +To change a user's role from member to admin (or vice versa): + +1. Remove the user from their current role group in Entra. +2. Add them to the target role group. + +Perform these operations as a single atomic action where possible. Adding a user to the new role group while they are still in the old one will return a `409` conflict. + +#### Last-Admin Protection + +LocalStack will reject any SCIM request that would leave the workspace without an admin. If you attempt to remove the only admin from the admin role group, the request fails with `409 Cannot remove the last workspace admin`. Assign another admin in LocalStack first, then retry the removal. diff --git a/src/content/docs/aws/enterprise/sso/scim/index.md b/src/content/docs/aws/enterprise/sso/scim/index.md new file mode 100644 index 00000000..0676d0e2 --- /dev/null +++ b/src/content/docs/aws/enterprise/sso/scim/index.md @@ -0,0 +1,99 @@ +--- +title: SCIM +description: Automating user and license provisioning in LocalStack using SCIM (System for Cross-domain Identity Management). +template: doc +tags: ['Enterprise'] +sidebar: + label: Overview +--- + +SCIM (System for Cross-domain Identity Management) allows you to automate user provisioning, deprovisioning, and license assignment in LocalStack through your identity provider (IdP). LocalStack's SCIM implementation follows the SCIM v2.0 specification and has been developed and tested with both the **Okta** and **Microsoft Entra ID** SCIM clients. + +SCIM is a sub-feature of SSO and requires an active SSO configuration with at least one Identity Provider already set up. See the [Single Sign-On](/aws/enterprise/sso/) documentation before proceeding. + +All integration details — including the SCIM Base Connector URL, Bearer Auth Token, and group names per subscription — are available in the LocalStack web app under **Settings → [Single Sign-On](https://app.localstack.cloud/settings/sso)**. + +For IdP-specific setup instructions, see: + +- [SCIM with Okta](/aws/enterprise/sso/scim/okta/) +- [SCIM with Microsoft Entra ID](/aws/enterprise/sso/scim/entra/) + +## Prerequisites + +- An active Enterprise subscription with the SCIM feature enabled +- A configured SSO Identity Provider (OIDC or SAML) +- Admin access to your organization in the LocalStack web app + +## Enabling SCIM + +In the LocalStack web app, navigate to **Settings → Single Sign-On**. For each configured Identity Provider, you will see a **SCIM User Provisioning** toggle. Enable it for the IdP you want to use for SCIM provisioning. + +:::note +Only one Identity Provider can have SCIM active at a time. +::: + +Once enabled, click **View SCIM Configuration** to access the SCIM Base Connector URL and Bearer Auth Token needed to configure your IdP. + +## Setup and Configuration + +The settings contain the **SCIM API Base Connector URL** and the **Bearer Auth Token** as shown in the image below. You can copy these values to configure your SCIM client. + +![SCIM connection details](/images/aws/SCIM-configuration.jpg) + +SCIM clients authenticate using a long-lived bearer token. The token starts with `scim-` and is displayed (masked) in the SCIM configuration panel. Use the copy icon to copy it to your clipboard. + +You can regenerate the token at any time using the refresh icon. Regenerating the token immediately invalidates the previous one — update your IdP configuration with the new token to avoid interruptions. + +Once you have the Base Connector URL and Bearer Token, continue with the IdP-specific setup: + +- [SCIM with Okta](/aws/enterprise/sso/scim/okta/) +- [SCIM with Microsoft Entra ID](/aws/enterprise/sso/scim/entra/) + +## Web App Roles and Permissions + +LocalStack supports configuring default roles and permissions that are applied when a user is provisioned via SCIM. These can for example be used to grant users access CI credentials or to make them workspace admins. + +Granting users permissions or assigning them to groups (e.g. 'Member', 'Admin') is not supported via SCIM but the settings in the LocalStack web app allow you to set presets that are applied when a user is provisioned via SCIM. These settings are inherited from the SSO settings. + +![SCIM user role and permission settings](/images/aws/SCIM-permissions.jpg) + +## Limitations + +- **One license group per user:** Each user can be assigned to only one license group (subscription) per organization. +- **One SCIM provider at a time:** Only one Identity Provider can have SCIM enabled at a time. +- **Provisioning is one-way:** SCIM sync goes from your IdP to LocalStack only. There is no synchronization from LocalStack back to your IdP. +- **LocalStack UI does not block manual edits:** The LocalStack web app does not prevent you from manually editing SCIM-provisioned users or their license assignments. It is strongly recommended to manage SCIM-provisioned users exclusively through your IdP to avoid inconsistencies. +- **Re-provisioning removed users requires re-invitation:** If a user was provisioned via SCIM and later removed, they cannot be re-provisioned via SCIM directly. They must be re-invited through the LocalStack **Users & Licenses** page and accept the invitation before being reassigned. + +## API Reference + +LocalStack's SCIM API is available at `/scim/v2` and implements the SCIM v2.0 specification (RFC 7644). + +### User Endpoints (`/scim/v2/Users`) + +| Method | Endpoint | Description | +| ------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `POST` | `/scim/v2/Users` | Create a SCIM user, or idempotently return an existing member when the email matches. Enforces global email uniqueness and `userName` uniqueness per org and IdP. | +| `GET` | `/scim/v2/Users` | List active SCIM-provisioned users. Supports `filter=userName eq "..."`, `startIndex`, and `count` for pagination. | +| `GET` | `/scim/v2/Users/{id}` | Retrieve a SCIM user only if they are SCIM-provisioned and active in the org; returns `404` otherwise. | +| `PATCH` | `/scim/v2/Users/{id}` | RFC 7644 PatchOp for selected fields (`name`, `emails`) and deactivation via `active:false`. Reactivation via SCIM is not supported. Patching `userName` or `externalId` is not supported. | +| `PUT` | `/scim/v2/Users/{id}` | Full replace of mutable fields (name, email) with support for deactivation via `active:false`. Reactivation via SCIM is ignored. | + +### Group Endpoints (`/scim/v2/Groups`) + +| Method | Endpoint | Description | +| -------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `POST` | `/scim/v2/Groups` | Bind an existing subscription as a SCIM group via `displayName` (format: `{PLAN}-{EMULATOR}-{subscription_id}`). Optionally assign members. Validates membership and enforces one-group-per-user per org. Returns `201` on success; `409` for insufficient seats or conflicts. | +| `GET` | `/scim/v2/Groups` | List groups (subscriptions) with their SCIM members. Supports `filter=displayName eq "..."`, `startIndex`, and `count` (max 1000). | +| `GET` | `/scim/v2/Groups/{id}` | Retrieve a group by its subscription ID with SCIM members. Returns `404` if not found. | +| `PATCH` | `/scim/v2/Groups/{id}` | RFC 7644 PatchOp (`add`, `remove`, `replace`) for members. Supports capacity checks and rollback on partial failures. | +| `PUT` | `/scim/v2/Groups/{id}` | Full replace of group membership. Omitting or passing an empty `members` array clears all members. Supports rollback on errors. | +| `DELETE` | `/scim/v2/Groups/{id}` | Delete the group binding and unassign SCIM members. Non-SCIM assignments are unaffected. Returns `204` on success. | + +### Metadata Endpoints + +| Method | Endpoint | Description | +| ------ | -------------------------------- | ----------------------------------------------------------------- | +| `GET` | `/scim/v2/ResourceTypes` | List all supported SCIM resource types (`User`, `Group`). | +| `GET` | `/scim/v2/Schemas` | List supported SCIM schemas for user and group resources. | +| `GET` | `/scim/v2/ServiceProviderConfig` | Return service provider configuration and supported capabilities. | diff --git a/src/content/docs/aws/enterprise/sso/scim/okta.mdx b/src/content/docs/aws/enterprise/sso/scim/okta.mdx new file mode 100644 index 00000000..ce0f090a --- /dev/null +++ b/src/content/docs/aws/enterprise/sso/scim/okta.mdx @@ -0,0 +1,203 @@ +--- +title: SCIM with Okta +description: Configuring Okta as the SCIM client for LocalStack user and license provisioning. +template: doc +tags: ['Enterprise'] +sidebar: + order: 2 +--- + +This page covers configuring **Okta** as your SCIM client to provision users, groups, and licenses into LocalStack. Before starting, make sure you've completed the steps in the [SCIM overview](/aws/enterprise/sso/scim/) to enable SCIM and obtain the **SCIM Base Connector URL** and **Bearer Auth Token** from the LocalStack web app. + +## Configuring SCIM with Okta + +Use the following steps to configure SCIM Base Connector URL and Bearer Auth Token: + +1. **Select your application** — Go to **Applications → Applications** and select the application you want to enable SCIM provisioning for. +2. **Navigate to Provisioning settings** — In the application settings, go to the **Provisioning** tab and click **Integration** or **Edit** (wording may vary). +3. **Enter the SCIM connection details:** + - **SCIM connector base URL:** Paste the SCIM Base Connector URL from the LocalStack SCIM configuration panel. + - **Authentication Mode:** Select **HTTP Header**. + - **Bearer Token:** Paste the SCIM bearer token from the LocalStack SCIM configuration panel. +4. **Test the connection** — Click **Test Connector Configuration** to confirm Okta can connect successfully. +5. **Enable provisioning features** (optional) — Once the connection succeeds, enable the desired provisioning actions (Create Users, Update User Attributes, Deactivate Users) under the **To App** settings tab. There is no need to enable Sync Password, as SSO does not require a password. +6. **Save** — Save and apply the integration settings. + +:::note +The exact menu names may vary depending on Okta's UI version and app type, but the key settings are always the SCIM Base Connector URL and Bearer Auth Token under the provisioning or integration section. +::: + +### User Management + +#### Provisioning Individual Users + +LocalStack supports full provisioning and deprovisioning of individual user accounts via SCIM. + +:::note +For security reasons, SCIM can only provision user accounts for users who do not already exist in the LocalStack web app. If a user was originally created via SCIM and later removed from your workspace, you must invite them again through the LocalStack [**Users & Licenses**](https://app.localstack.cloud/settings/members). The user will receive an email invitation and must explicitly accept it to rejoin the workspace. +::: + +1. In the Okta Admin Console, go to your application and click the **Assignments** tab. +2. Select **Assign → Assign to People**. +3. Search for and select the users you want to provision, then click **Assign** and **Done**. +4. Okta will automatically send a SCIM request to LocalStack to create the user account. The user will be visible in LocalStack and their account details will sync from Okta. + +:::tip +Legacy users (existing LocalStack accounts) can also be assigned to the Okta application, provided their email address matches the one they used to register with the LocalStack web app. +::: + +#### Updating User Accounts + +Changes to user attributes (first name, last name, email) in Okta are automatically pushed to LocalStack via SCIM while the integration is active. + +#### Deprovisioning Users + +1. In Okta, go to your application's **Assignments** tab. +2. Find the user you want to remove and click **Remove** next to their name. +3. Confirm the action. + +Okta will send a SCIM deprovisioning request and the user will be removed from LocalStack. + +#### Provisioning Groups of Users + +Groups in Okta can be used to provision multiple users to LocalStack at once. + +##### Assigning a Group + +1. In the Okta Admin Console, go to your application and click the **Assignments** tab. +2. Select **Assign → Assign to Groups**. +3. Search for and select the groups you want to provision, then click **Assign** and **Done**. + +Okta will send a SCIM request to LocalStack to create a user account for each member of the group. Changes to a group's membership in Okta are automatically pushed to LocalStack via SCIM. + +##### Deprovisioning a Group + +1. In Okta, return to your application's **Assignments** tab. +2. Find the group and click **Remove** next to its name. +3. Confirm the action. + +Okta will send a SCIM request to remove the group's users from LocalStack. Users who were provisioned solely through this group assignment will also be deprovisioned. + +:::tip +Any changes in Okta (user/group attribute changes, group memberships, etc.) are automatically synchronized with LocalStack as long as the SCIM integration is active. +::: + +##### Migrating an Existing OpenID Connect or SAML Application + +If you have an existing OIDC or SAML app in Okta that already has SSO users assigned, follow these steps to add SCIM provisioning: + +1. On the **General** tab of your Okta application, set **Provisioning** to **SCIM**. + ![Provisioning to SCIM for SAML application](/images/aws/SCIM-SAML-provisioning.jpg) +2. Go to the **Provisioning** tab and click **Edit** to configure the SCIM connection: + - **SCIM connector base URL:** Paste the URL from LocalStack. + - **Unique identifier field for users:** Enter `userName` (the Okta default). + - **Supported provisioning actions:** Enable all available options. + + ![Adding the LocalStack settings for SAML application](/images/aws/SCIM-SAML-provisioning-2.jpg) + +3. Select **HTTP Header** as the Authentication Mode and paste the Bearer token from the LocalStack SCIM configuration panel. Click **Save**. + ![Adding the Bearer token for SAML application](/images/aws/SCIM-SAML-provisioning-3.jpg) +4. After a successful connection test, go to the **To App** tab, click **Edit**, and enable **Create Users**, **Update User Attributes**, and **Deactivate Users**. Save your changes. + ![Testing the connection for SAML application](/images/aws/SCIM-SAML-provisioning-4.jpg) +5. Click the **Assignments** tab. Okta will show error messages for users who were assigned before provisioning was enabled. Click **Provision User** and confirm the action to sync all existing users. If the task fails, you can retry it under **Dashboard → Tasks**. + ![Errors when provisioning users for SAML application](/images/aws/SCIM-SAML-provisioning-5.jpg) +6. After syncing completes, refresh the page — the error messages should be gone and all users will be fully managed via Okta SCIM. + +### Role Management + +LocalStack workspace roles (**admin** and **member**) are assigned to users by pushing SCIM groups whose name identifies the target role. The role groups themselves do not need to exist in LocalStack before the push — they are synthetic SCIM groups keyed off the `displayName`. + +:::caution +Each user can only be in **one** role group at a time. Attempting to add a user who is already in the admin role group to the member role group (or vice versa) returns a `409` conflict. Remove the user from the previous role group first, then add to the new one. +::: + +#### Group Name Convention + +Role groups are matched by `displayName` using a case-insensitive substring check: + +- Any group whose name contains `admin` → admin role group +- Any group whose name contains `member` → member role group + +All of the following are valid names for the admin role group: + +- `LocalStack-Admin` +- `LocalStack-Admins-Prod` +- `engineering-admins` + +The first time you push a role group from Okta, LocalStack persists that `displayName` so subsequent GET responses to your IdP reflect the name you sent. You can also rename the group later via SCIM and LocalStack will track the rename. + +#### Creating and Pushing a Role Group in Okta + +1. Create a new Okta group whose name contains either `Admin` (for the admin role) or `Member` (for the member role). For example: `LocalStack-Admin` or `LocalStack-Member`. +2. Add users to the group (users must already be assigned to the LocalStack SCIM application). +3. In your application, go to the **Push Groups** tab. +4. Push the group to LocalStack via SCIM. +5. Once synced, LocalStack will assign the corresponding role to all members of the group. + + ![Pushing a role group from Okta](/images/aws/SCIM_okta_role_group.jpg) + +#### Moving a User Between Roles + +To change a user's role from member to admin (or vice versa): + +1. Remove the user from their current role group in Okta. +2. Add them to the target role group. + +Perform these operations as a single atomic action where possible. Adding a user to the new role group while they are still in the old one will return a `409` conflict. + +#### Last-Admin Protection + +LocalStack will reject any SCIM request that would leave the workspace without an admin. If you attempt to remove the only admin from the admin role group, the request fails with `409 Cannot remove the last workspace admin`. Assign another admin in LocalStack first, then retry the removal. + +### License Management + +Licenses are assigned to users by pushing specifically named SCIM groups that correspond to your LocalStack subscriptions. + +:::caution +Each user can only be a member of one license group (subscription) per organization. Assigning a user to multiple license groups will result in an error and provisioning will fail for that user. +::: + +#### Group Name Format + +License group names follow this format: + +```text +{PLAN}-{EMULATOR}-{SUBSCRIPTION_ID} +``` + +For example: `Enterprise Plan-AWS-sub_1RqpMYGCs0LNOzY9UszOGJkL` + +The exact group name for each subscription is displayed in the SCIM configuration panel in the LocalStack web app. Use the subscription dropdown to select the plan you want to manage, and the correct group name will be shown for you to copy. + +:::tip +Legacy users can be added to a license assignment group in Okta, provided their email address matches their LocalStack registration email, they have been assigned to the Okta application, and the group name matches the correct subscription. +::: + +#### Creating and Pushing a License Group in Okta + +1. Create a new Okta group named exactly as shown in the LocalStack SCIM configuration panel. +2. Add users to the group (users must already be assigned to the LocalStack SCIM application). +3. In your application, go to the **Push Groups** tab. +4. Push the group to LocalStack via SCIM. +5. Once synced, LocalStack will recognize the group and assign the corresponding license to all members. + +:::danger[License revocation risk] + +The Okta group's membership is the source of truth for license assignments on this subscription. Any change to this group in Okta (adding users, removing users, or syncing it) will reconcile the subscription's licenses to match the group exactly. Users who are licensed on this subscription but not in the Okta group will have their licenses revoked, regardless of how the license was originally assigned (manually or via SCIM). + +This means: + +- If you sync an **empty group**, every license on this subscription will be revoked. +- If you sync a **partial group** (for example, 2 users in Okta but 5 currently licensed), the 3 users not in the group will lose their licenses. + +If you are enabling SCIM on a subscription that already has licensed users, follow the [Migrating Users with Existing Licenses](#migrating-users-with-existing-licenses) steps below **before** any sync occurs. Once SCIM is enabled, manage license assignments exclusively through Okta. + +::: + +#### Migrating Users with Existing Licenses + +If your organization already has users with assigned licenses and you want to manage them through SCIM: + +1. Create a license group in Okta with the correct name. +2. Add it to the application via the **Push Groups** tab. +3. Add the existing licensed users to that group through the application. Once added, they will be automatically synced (Push Status becomes **Active**) and managed through SCIM going forward. diff --git a/src/styles/custom.css b/src/styles/custom.css index a6b460d4..0a473e38 100644 --- a/src/styles/custom.css +++ b/src/styles/custom.css @@ -184,6 +184,16 @@ border-radius: 4px; } +/* Nested group label (e.g. SCIM under Single Sign-On) - match sibling links. + Starlight renders these as ; the rule targets the class + directly when it appears inside a nested level. */ +.sidebar-content .top-level li details ul li .large { + font-family: var(--font-aeonik-fono) !important; + font-size: 14px !important; + font-weight: normal !important; + color: var(--sl-color-gray-3) !important; +} + /* Third level navigation text styles */ .sidebar-content .top-level li details ul li details ul li a span { font-size: 14px !important; diff --git a/src/styles/global.css b/src/styles/global.css index c00c4feb..f6c42660 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -12,7 +12,7 @@ --localstack-purple: #4D0DCF; --sl-text-h1: 40px; --sl-text-h2: 32px; - --sl-text-h3: 24px; + --sl-text-h3: 28px; /* Dark mode colors. */ --sl-color-accent-low: #241b47; --sl-color-accent: #6e3ae8;