diff --git a/public/access-control.md b/public/access-control.md
index 968505d5..f422fefe 100644
--- a/public/access-control.md
+++ b/public/access-control.md
@@ -34,9 +34,13 @@ User authentication in Phase is designed for seamless and secure access for huma
Programmatic access to secrets in Phase is facilitated by Service Accounts. A Service Account is a special type of account that represents non-human users, such as applications, automation processes, or CI/CD pipelines, that need to interact with Phase programmatically through the API, SDK, or CLI.
-[Read more](/access-control/service-accounts) about how service accounts work.
+[Read more](/access-control/service-accounts) about how service accounts work.
+## Teams
+Teams let you group members and service accounts together and grant them scoped access to apps and environments. Instead of managing access individually, you assign access at the team level — when someone joins the team, they automatically receive the right encryption keys. Teams support optional role overrides and work with SCIM provisioning for automated group sync.
+
+[Read more](/access-control/teams) about how teams work.
## Access Control - Key Concepts
@@ -78,6 +82,7 @@ Phase's RBAC system allows you to define permissions for Create, Read, Update, a
| **Members** | Manage user access within the app |
| **Integrations** | Control setup and management of app integrations |
| **Encryption Mode** | Manage encryption settings for the app |
+| **Teams** | Manage team-based access within the app |
## Global Access
diff --git a/public/access-control/provisioning/scim.md b/public/access-control/provisioning/scim.md
new file mode 100644
index 00000000..7630dc81
--- /dev/null
+++ b/public/access-control/provisioning/scim.md
@@ -0,0 +1,358 @@
+import { Tag } from '@/components/Tag'
+import { DocActions } from '@/components/DocActions'
+
+export const description = 'Set up SCIM v2 provisioning to automatically sync users and groups from your identity provider to Phase.'
+
+PROVISIONING
+
+# SCIM Provisioning
+
+SCIM (System for Cross-domain Identity Management) v2 enables automatic user and group provisioning from your identity provider (IdP) to Phase. When configured, your IdP can automatically create, update, and deactivate user accounts in Phase, eliminating the need for manual user management.
+
+
+
+
+ SCIM provisioning is available for organisations with an `Enterprise` tier subscription. See [Pricing](https://phase.dev/pricing).
+
+
+## How it works
+
+When SCIM is enabled, your identity provider communicates with Phase's SCIM v2 API to:
+
+- **Provision users**: Automatically create Phase accounts when users are assigned to the application in your IdP. Users are created with a `Pending` status until they complete their first login and account setup.
+- **Deprovision users**: Automatically deactivate Phase accounts when users are unassigned or disabled in your IdP. The user's environment keys are revoked and their org membership is soft-deleted.
+- **Sync groups**: Map IdP groups to Phase Teams, automatically managing team membership as group assignments change.
+- **Update user attributes**: Keep user details (name, email) in sync between your IdP and Phase.
+
+### User lifecycle
+
+1. **IdP assigns user** → SCIM creates a Phase user account with `Pending` status (SCIM + Pending badges shown in the console)
+2. **User logs in via SSO** → Phase auto-links the SSO account to the SCIM-provisioned user
+3. **User completes account setup** → User sets up their sudo password and recovery phrase, enabling them to decrypt secrets
+4. **IdP unassigns user** → SCIM deactivates the user, revokes environment keys, and removes them from the organisation
+
+## Enable SCIM in Phase
+
+Before configuring your identity provider, enable SCIM in the Phase Console and create a provisioning token:
+
+1. Log into the Phase Console as an **Owner** or **Admin**.
+
+2. Navigate to **Access** > **SCIM** in the sidebar.
+
+3. Toggle **Enable SCIM** to on.
+
+4. Copy the **SCIM Base URL** shown on the page. It will look like:
+
+ ```
+ https://[YOUR_PHASE_HOST]/service/scim/v2/
+ ```
+
+5. Click **Create token** to generate a SCIM bearer token. Give it a descriptive name (e.g., "Azure Entra ID") and select an expiry period.
+
+6. Copy the generated token immediately — it will not be shown again. This token will be used as the **Secret Token** in your identity provider's SCIM configuration.
+
+
+ Treat the SCIM token like a password. Anyone with this token can provision and deprovision users in your Phase organisation.
+
+
+## Microsoft Entra ID (Azure AD)
+
+Microsoft Entra ID supports SCIM-based provisioning through Enterprise Applications. Follow these steps to configure automatic user and group provisioning from Entra ID to Phase.
+
+### Prerequisites
+
+- A Microsoft Entra ID tenant with a P1 or P2 license (required for enterprise app provisioning)
+- An existing Phase Enterprise organisation with SCIM enabled and a SCIM token (see above)
+- OIDC SSO configured for Microsoft Entra ID (see [Entra ID OIDC setup](/access-control/authentication/oidc-sso#microsoft-entra-id--azure-ad))
+
+
+ SCIM provisioning and OIDC SSO are complementary features. SCIM handles account lifecycle (creation, deactivation), while OIDC handles authentication (login). You should configure both for the best experience.
+
+
+### Step 1: Create an Enterprise Application
+
+1. Sign in to the [Azure Portal](https://portal.azure.com).
+
+2. Navigate to **Microsoft Entra ID** > **Enterprise applications**.
+
+3. Click **+ New application** at the top.
+
+4. Click **+ Create your own application**.
+
+5. Enter a name for the application (e.g., "Phase Console SCIM"), select **Integrate any other application you find in the gallery (Non-gallery)**, and click **Create**.
+
+### Step 2: Configure Provisioning
+
+1. In your newly created Enterprise Application, select **Provisioning** from the left sidebar under *Manage*.
+
+2. Click **Get started** or set the **Provisioning Mode** to **Automatic**.
+
+3. Under **Admin Credentials**, enter:
+ - **Tenant URL**: The SCIM Base URL from Phase (e.g., `https://[YOUR_PHASE_HOST]/service/scim/v2/`)
+ - **Secret Token**: The SCIM token you generated in Phase
+
+4. Click **Test Connection** to verify connectivity. Azure should report "The supplied credentials are authorized to enable provisioning".
+
+5. Click **Save**.
+
+### Step 3: Configure Attribute Mappings
+
+After saving credentials, Azure will show the **Mappings** section. You need to configure both user and group mappings.
+
+#### User Attribute Mappings
+
+1. Click **Provision Microsoft Entra ID Users**.
+
+2. Ensure the following mappings are present (these are the minimum required by Phase's SCIM implementation):
+
+ | Microsoft Entra ID Attribute | customappsso Attribute | Mapping Type |
+ |------|-------|------|
+ | `userPrincipalName` | `userName` | Direct |
+ | `Switch([IsSoftDeleted], , "False", "True", "True", "False")` | `active` | Expression |
+ | `mail` | `emails[type eq "work"].value` | Direct |
+ | `displayName` | `displayName` | Direct |
+ | `givenName` | `name.givenName` | Direct |
+ | `surname` | `name.familyName` | Direct |
+
+
+ The `mail` → `emails[type eq "work"].value` mapping is important. Phase uses this email to link the SCIM-provisioned account with the SSO login. If your users don't have the `mail` attribute populated in Entra ID, you can map `userPrincipalName` to `emails[type eq "work"].value` instead.
+
+
+3. Click **Save**.
+
+#### Group Attribute Mappings
+
+1. Go back to the Provisioning page and click **Provision Microsoft Entra ID Groups**.
+
+2. Ensure the following mappings are present:
+
+ | Microsoft Entra ID Attribute | customappsso Attribute | Mapping Type |
+ |------|-------|------|
+ | `displayName` | `displayName` | Direct |
+ | `objectId` | `externalId` | Direct |
+ | `members` | `members` | Direct |
+
+3. Click **Save**.
+
+### Step 4: Assign Users and Groups
+
+1. Navigate to **Users and groups** in the left sidebar of your Enterprise Application.
+
+2. Click **+ Add user/group**.
+
+3. Select the users and/or groups you want to provision to Phase, then click **Assign**.
+
+
+ Only users and groups explicitly assigned to the Enterprise Application will be provisioned. The default scope setting is "Sync only assigned users and groups".
+
+
+### Step 5: Start Provisioning
+
+1. Go back to the **Provisioning** page.
+
+2. Click **Start provisioning** to begin the initial sync.
+
+3. The initial provisioning cycle may take a few minutes. You can monitor progress under **Provisioning logs** in Azure, or in the **SCIM > Provisioning Logs** section of the Phase Console.
+
+4. Once complete, provisioned users will appear on the Phase **Members** page with `SCIM` and `Pending` badges. Groups will appear as **Teams** in Phase.
+
+### Step 6: Test the Flow
+
+1. Verify that provisioned users appear in the Phase Console under **Access** > **Members** with the SCIM and Pending badges.
+
+2. Have a provisioned user log in via SSO (Entra ID OIDC). They will be redirected to the **account setup** page to set up their sudo password and recovery phrase.
+
+3. After completing account setup, the user should be able to access secrets in any environments their team has access to.
+
+### Troubleshooting: Microsoft Entra ID
+
+#### Provisioning shows "quarantine" status
+
+This usually means Azure received multiple errors from the SCIM endpoint. When quarantined, Azure exponentially backs off the retry interval (up to several hours between attempts). Check:
+- The SCIM token hasn't expired (check the Phase Console SCIM page)
+- The Phase instance is accessible from Azure's IP ranges
+- Review the provisioning logs in both Azure and the Phase Console for specific error messages
+
+To clear a quarantine and resume normal provisioning:
+
+1. In the Azure Portal, go to your Enterprise Application > **Provisioning**.
+2. Click **Stop provisioning** and wait for it to confirm.
+3. Click **Start provisioning** — this clears the quarantine state and triggers a fresh initial sync.
+4. Verify the **Provisioning Status** toggle is set to **On**, then click **Save**.
+
+
+ "Restart provisioning" clears the quarantine but does not always re-enable the provisioning toggle. Always verify the toggle is **On** and click **Save** after restarting.
+
+
+#### User can't log in after SCIM provisioning
+
+Ensure that:
+1. OIDC SSO is configured for Entra ID (SCIM handles provisioning, OIDC handles login)
+2. The user's email in Entra ID matches between the SCIM provisioning and the OIDC token claims
+3. The user is assigned to both the SCIM Enterprise Application and the OIDC App Registration (or the OIDC app allows all directory users)
+
+#### Re-provisioning a previously deactivated user
+
+If you remove a user from the application and then re-add them, Azure may skip provisioning with a `RedundantSoftDelete` error. To resolve this:
+
+1. Stop provisioning in the Enterprise Application
+2. Ensure the user is assigned under **Users and groups**
+3. Go to **Provisioning** > click **Restart provisioning** (this clears Azure's internal cycle state)
+4. If the issue persists, use **Provision on demand** for the specific user after restarting provisioning
+
+#### Groups not syncing as Teams
+
+Ensure that:
+- Group provisioning is enabled in the attribute mappings
+- The groups are assigned to the Enterprise Application
+- The `displayName` and `members` attributes are mapped correctly
+
+## Okta
+
+Okta supports SCIM-based provisioning through its application integration platform. Follow these steps to configure automatic user and group provisioning from Okta to Phase.
+
+### Prerequisites
+
+- An Okta organisation with the ability to create custom SCIM integrations
+- An existing Phase Enterprise organisation with SCIM enabled and a SCIM token (see [Enable SCIM in Phase](#enable-scim-in-phase))
+- OIDC SSO configured for Okta (see [Okta OIDC setup](/access-control/authentication/oidc-sso#okta))
+
+
+ SCIM provisioning and OIDC SSO are complementary features. SCIM handles account lifecycle (creation, deactivation), while OIDC handles authentication (login). You should configure both for the best experience.
+
+
+### Step 1: Create a SCIM Application in Okta
+
+If you already have an Okta OIDC app for Phase SSO, you can add SCIM provisioning to it. Otherwise, create a new app:
+
+1. Sign in to the [Okta Admin Console](https://admin.okta.com).
+
+2. Navigate to **Applications** > **Applications**.
+
+3. If adding SCIM to an existing app, click on your Phase OIDC app and skip to Step 2. Otherwise, click **Create App Integration**.
+
+4. Select **SWA - Secure Web Authentication** (this creates a bookmark app that supports SCIM provisioning), and click **Next**.
+
+5. Enter a name (e.g., "Phase Console") and the login URL for your Phase instance. Click **Finish**.
+
+
+ If you already have a separate OIDC app for SSO, you can use a SWA app purely for SCIM provisioning. Users will still authenticate via the OIDC app — the SWA app only handles user/group lifecycle.
+
+
+### Step 2: Enable SCIM Provisioning
+
+1. In your application, go to the **General** tab.
+
+2. Click **Edit** on the App Settings panel.
+
+3. Under **Provisioning**, select **SCIM** and click **Save**.
+
+4. A new **Provisioning** tab will appear. Click on it.
+
+5. Under **SCIM Connection**, click **Edit** and enter:
+ - **SCIM connector base URL**: The SCIM Base URL from Phase (e.g., `https://[YOUR_PHASE_HOST]/service/scim/v2`)
+ - **Unique identifier field for users**: `userName`
+ - **Supported provisioning actions**: Check **Push New Users**, **Push Profile Updates**, and **Push Groups**
+ - **Authentication Mode**: `HTTP Header`
+ - **Authorization**: Paste the SCIM bearer token you generated in Phase (the token value only, without the `Bearer` prefix)
+
+6. Click **Test Connector Configuration** to verify connectivity. Okta should report success for the enabled actions.
+
+7. Click **Save**.
+
+### Step 3: Configure Provisioning Actions
+
+1. Still on the **Provisioning** tab, click **To App** in the left panel.
+
+2. Click **Edit** and enable:
+ - **Create Users** — Allows Okta to provision new users in Phase
+ - **Update User Attributes** — Keeps user details in sync
+ - **Deactivate Users** — Deactivates Phase accounts when users are unassigned in Okta
+
+3. Click **Save**.
+
+### Step 4: Verify Attribute Mappings
+
+1. Still under **Provisioning** > **To App**, scroll down to the **Attribute Mappings** section.
+
+2. Okta pre-populates a comprehensive set of default attribute mappings. No changes are required — the defaults already include everything Phase needs. Verify that the following key mappings are present:
+
+ | Okta Attribute | App Attribute | Default Value |
+ |------|-------|------|
+ | `userName` | `userName` | Configured in Sign On settings |
+ | `givenName` | `name.givenName` | `user.firstName` |
+ | `familyName` | `name.familyName` | `user.lastName` |
+ | `email` | `emails[type eq "work"].value` | `user.email` |
+ | `displayName` | `displayName` | `user.displayName` |
+
+
+ Okta sends many additional attributes by default (phone, address, title, etc.). Phase safely ignores any attributes it doesn't need — you can leave them as-is or remove them if you prefer a minimal mapping.
+
+
+
+ Okta uses `userName` (typically the user's email address) as the primary identifier. Ensure that the `userName` in Okta matches the email address used for SSO login, so Phase can link the SCIM-provisioned account with the OIDC identity.
+
+
+### Step 5: Assign Users and Groups
+
+1. Go to the **Assignments** tab in your application.
+
+2. Click **Assign** and select either **Assign to People** or **Assign to Groups**.
+
+3. Select the users and/or groups you want to provision to Phase, then click **Assign** and **Save and Go Back**.
+
+4. If you assigned groups, go to the **Push Groups** tab:
+ - Click **Push Groups** > **Find groups by name** (or by rule)
+ - Select the groups you want to push to Phase
+ - Click **Save**
+
+
+ In Okta, assigning a group under **Assignments** grants group members access to the app (user provisioning). The **Push Groups** tab controls whether the group itself is synced to Phase as a Team. You typically need both: assign the group for user provisioning, and push the group for team creation.
+
+
+### Step 6: Verify Provisioning
+
+1. Navigate to **Dashboard** > **Tasks** in the Okta Admin Console to monitor provisioning progress.
+
+2. Check the Phase Console under **Access** > **Members** — provisioned users should appear with `SCIM` and `Pending` badges.
+
+3. Check **Access** > **Teams** — pushed groups should appear as teams with a `SCIM` badge.
+
+4. Have a provisioned user log in via Okta SSO. They will be redirected to the account setup page to set up their sudo password and recovery phrase.
+
+### Troubleshooting: Okta
+
+#### "Error authenticating" when testing the connector
+
+- Verify the SCIM base URL ends with `/scim/v2` (no trailing slash)
+- Verify the token is entered without the `Bearer` prefix — Okta adds it automatically
+- Ensure the SCIM token hasn't expired in the Phase Console
+
+#### Users not being provisioned
+
+- Check that **Create Users** is enabled under **Provisioning** > **To App**
+- Verify users are assigned to the application under the **Assignments** tab
+- Check the **Tasks** page in Okta for specific error messages
+- Review the SCIM provisioning logs in the Phase Console for request/response details
+
+#### Groups not syncing as Teams
+
+- Ensure the group is both **assigned** to the app (Assignments tab) and **pushed** (Push Groups tab)
+- Check that the group push status shows "Active" — if it shows an error, click on it for details
+- Verify that group push is enabled in the SCIM connector settings (**Push Groups** checkbox)
+
+#### User deprovisioned after removing direct assignment, despite being in a pushed group
+
+In Okta, **Push Groups** and **Assignments** are independent:
+- **Assignments** controls user provisioning (who gets a Phase account)
+- **Push Groups** controls group/team syncing (which groups appear as Phase Teams)
+
+If a user is only directly assigned (not via a group assignment), removing that direct assignment deprovisions them — even if they belong to a pushed group. Push Groups does not grant app assignment.
+
+**Fix**: Assign groups under the **Assignments** tab (not just Push Groups). This way, group membership is the single source of truth for both user provisioning and team sync. When a user is removed from the group in Okta, they are automatically deprovisioned and removed from the team.
+
+#### User can't log in after SCIM provisioning
+
+- Ensure OIDC SSO is configured for Okta (SCIM handles provisioning, OIDC handles login)
+- The user's email in Okta must match between the SCIM `userName` and the OIDC token `email` claim
+- If using separate apps for SCIM and OIDC, the user must be assigned to both
diff --git a/public/access-control/roles.md b/public/access-control/roles.md
index c7ada805..99c4b63c 100644
--- a/public/access-control/roles.md
+++ b/public/access-control/roles.md
@@ -44,6 +44,8 @@ The organization owner. This role is automatically assigned when a user creates
| **Roles** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Integration Credentials** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Network Access Policies** | Full access | ✅ | ✅ | ✅ | ✅ |
+| **Teams** | Full access | ✅ | ✅ | ✅ | ✅ |
+| **SCIM** | Full access | ✅ | ✅ | ✅ | ✅ |
| **SSO** | Full access | ✅ | ✅ | ✅ | ✅ |
#### App-level permissions:
@@ -60,6 +62,7 @@ The organization owner. This role is automatically assigned when a user creates
| **Service Accounts** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Integrations** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Encryption Mode** | Full access | ✅ | | ✅ | |
+| **Teams** | Full access | ✅ | ✅ | ✅ | ✅ |
### Admin
@@ -80,6 +83,8 @@ Admin users have access to most resources and permissions, and have global acces
| **Roles** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Integration Credentials** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Network Access Policies** | Full access | ✅ | ✅ | ✅ | ✅ |
+| **Teams** | Full access | ✅ | ✅ | ✅ | ✅ |
+| **SCIM** | Full access | ✅ | ✅ | ✅ | ✅ |
| **SSO** | Full access | ✅ | ✅ | ✅ | ✅ |
#### App-level permissions:
@@ -96,6 +101,7 @@ Admin users have access to most resources and permissions, and have global acces
| **Service Accounts** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Integrations** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Encryption Mode** | Custom access | ✅ | | ✅ | |
+| **Teams** | Full access | ✅ | ✅ | ✅ | ✅ |
The `Owner` and `Admin` roles have global access. Learn more about global access [here](/access-control#global-access).
@@ -118,6 +124,8 @@ Management users with broad access to environments, secrets, and service account
| **Roles** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Integration Credentials** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Network Access Policies** | Full access | ✅ | ✅ | ✅ | ✅ |
+| **Teams** | Full access | ✅ | ✅ | ✅ | ✅ |
+| **SCIM** | No access | ❌ | ❌ | ❌ | ❌ |
| **SSO** | No access | ❌ | ❌ | ❌ | ❌ |
#### App-level permissions:
@@ -134,6 +142,7 @@ Management users with broad access to environments, secrets, and service account
| **Service Accounts** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Integrations** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Encryption Mode** | Custom access | ✅ | ❌ | ✅ | ❌ |
+| **Teams** | Full access | ✅ | ✅ | ✅ | ✅ |
### Service
@@ -154,6 +163,8 @@ Default role for Service Accounts, providing programmatic access to secrets with
| **Roles** | Read access | ✅ | ❌ | ❌ | ❌ |
| **Integration Credentials** | Read access | ✅ | ❌ | ❌ | ❌ |
| **Network Access Policies** | Read access | ✅ | ❌ | ❌ | ❌ |
+| **Teams** | No access | ❌ | ❌ | ❌ | ❌ |
+| **SCIM** | No access | ❌ | ❌ | ❌ | ❌ |
| **SSO** | No access | ❌ | ❌ | ❌ | ❌ |
#### App-level permissions:
@@ -170,6 +181,7 @@ Default role for Service Accounts, providing programmatic access to secrets with
| **Service Accounts** | Read access | ✅ | ❌ | ❌ | ❌ |
| **Integrations** | Read access | ✅ | ❌ | ❌ | ❌ |
| **Encryption Mode** | Read access | ✅ | ❌ | ❌ | ❌ |
+| **Teams** | Read access | ✅ | ❌ | ❌ | ❌ |
### Developer
@@ -190,6 +202,8 @@ Developers have limited permissions at the organization level and must be given
| **Roles** | Read access | ✅ | ❌ | ❌ | ❌ |
| **Integration Credentials** | Custom access | ✅ | ✅ | ✅ | ❌ |
| **Network Access Policies** | Read access | ✅ | ❌ | ❌ | ❌ |
+| **Teams** | Read access | ✅ | ❌ | ❌ | ❌ |
+| **SCIM** | No access | ❌ | ❌ | ❌ | ❌ |
| **SSO** | No access | ❌ | ❌ | ❌ | ❌ |
#### App-level permissions:
@@ -203,9 +217,10 @@ Developers have limited permissions at the organization level and must be given
| **Logs** | Read access | ✅ | ❌ | ❌ | ❌ |
| **Tokens (Legacy)** | Custom access | ✅ | ✅ | ❌ | ❌ |
| **Members** | Read access | ✅ | ❌ | ❌ | ❌ |
-| **Service Accounts** | Read access | ✅ | ❌ | ❌ | ❌ |
+| **Service Accounts** | Custom access | ❌ | ✅ | ❌ | ❌ |
| **Integrations** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Encryption Mode** | Custom access | ✅ | | ✅ | |
+| **Teams** | Read access | ✅ | ❌ | ❌ | ❌ |
## Custom Roles
@@ -265,6 +280,12 @@ Some actions require a combination of permissions across multiple resources. Bel
- To view and delete other users Personal Access Tokens:
- `MemberPersonalAccessTokens:read`
- `MemberPersonalAccessTokens:delete`
+- Adding or removing a Team's access to an App:
+ - `Teams:create` or `Teams:delete` (app-level)
+ - `Teams:read` (organisation-level)
+- Managing a Team's environment scope within an App:
+ - `Teams:update` (app-level)
+ - `Teams:read` (organisation-level)
- To Manage Network Access Policies of a User or Service Account:
- `Members:read`
- `Members:update`
diff --git a/public/access-control/service-accounts.md b/public/access-control/service-accounts.md
index cc41d414..389b35b9 100644
--- a/public/access-control/service-accounts.md
+++ b/public/access-control/service-accounts.md
@@ -9,7 +9,16 @@ Service Accounts provide a secure and controlled method for programmatic access
Service accounts share many of the properties and behavior of human user accounts. Service Accounts follow an Access Policy that can be defined by [Managed Roles](/access-control/roles#managed-roles) or [Custom Roles](/access-control/roles#creating-custom-roles) based on the permissions required. Service accounts are secured with the same security and cryptographic architecture as user accounts, and must be manually provisioned access to Apps and Environments in order to access secrets.
-
+
+
+## Org-level vs Team-owned Service Accounts
+
+Service accounts exist in two categories:
+
+- **Org-level** (default): Visible to all organisation members with `ServiceAccounts.read` permission. Created from the organisation-level Service Accounts page. This is the default behavior and works on all plans.
+- **Team-owned**: Created within a [Team](/access-control/teams), visible only to team members and users with global access (Owner/Admin). The service account's lifecycle is tied to the team — if the team is deleted, team-owned SAs are deleted too.
+
+Team-owned service accounts are useful when a team needs dedicated programmatic access for team resources, that is isolated from other teams and users. See [Team-owned service accounts](/access-control/teams#team-owned-service-accounts) for details on creating and managing them.
## Create a new Service Account
@@ -79,12 +88,16 @@ To delete a Service Account, click on the "Delete" button at the bottom of the p
Each Service Account has its own unique keyring, just like User accounts. KMS modes determine who has access to the service account's keyring and can create and manage tokens for this service account.
#### Client-side KMS
-By default, Service Accounts use **Client-side KMS**. This means only designated users with the required `ServiceAccountTokens` permissions have access to create and manage tokens for this service account. These users are called *Service Account Handlers* and have access the service account's keyring, encrypted with their own keys.
+By default, org-level Service Accounts use **Client-side KMS**. This means only designated users with the required `ServiceAccountTokens` permissions have access to create and manage tokens for this service account. These users are called *Service Account Handlers* and have access the service account's keyring, encrypted with their own keys.
#### Server-side KMS
You can optionally enable **Server-side KMS** for a Service Account. This grants the Phase backend access to the service account's keyring, effectively making the backend a *Service Account Handler*. Enabling Server-side KMS allows the backend to create and manage tokens on behalf of the Service Account. This is required to use features such as [External Identities](/access-control/external-identities).
+
+ **Team-owned service accounts** always use Server-side KMS. This is enabled automatically when the account is created, so that any team member with the appropriate permissions can generate tokens — without needing to be a designated Service Account Handler. This is important for dynamic team membership, including teams managed via [SCIM provisioning](/access-control/provisioning/scim), where members may join or leave at any time.
+
+
#### Manage KMS mode
You can manage the KMS mode for a Service Account by clicking the **Manage** button beside the account KMS indicator at the top of the account page:
diff --git a/public/access-control/teams.md b/public/access-control/teams.md
new file mode 100644
index 00000000..96877f4c
--- /dev/null
+++ b/public/access-control/teams.md
@@ -0,0 +1,222 @@
+import { Tag } from '@/components/Tag'
+import { DocActions } from '@/components/DocActions'
+
+export const description = 'Organize members and service accounts into Teams with scoped app access and optional role overrides.'
+
+ACCESS CONTROL
+
+# Teams
+
+Teams let you group members and service accounts together and grant them access to specific apps and environments. Instead of managing access for each individual, you assign access at the team level — when someone joins the team, they automatically get the right keys.
+
+
+
+
+ Teams require apps with [Server-Side Encryption (SSE)](/console/apps#encryption-mode) enabled, since the server provisions encryption keys on behalf of team members.
+
+
+## How it works
+
+A Team is an organisation-level group that contains **members** (human users) and/or **service accounts**. Teams are granted access to one or more apps, scoped to specific environments within each app.
+
+When a team is added to an app:
+
+1. You select which environments the team should have access to (e.g. `Development`, `Staging`)
+2. Phase automatically provisions environment encryption keys for every team member
+3. Team members can immediately decrypt secrets in those environments
+
+When a member is added to a team that already has app access, keys are provisioned automatically. When a member is removed, their team-granted keys are revoked — unless they also have individual access to the same environment.
+
+### Key concepts
+
+| Concept | Description |
+|---------|-------------|
+| **Team ownership** | Each team has an owner who retains full management access regardless of role overrides |
+| **Team membership** | A user or service account belongs to one or more teams |
+| **Team app access** | A team is granted access to an app, scoped to specific environments |
+| **Role overrides** | Teams can optionally override the org role's app-level permissions for their members |
+| **Union semantics** | If a user has multiple access grants to the same app (individual, one or more teams), they get the **union** of all applicable permissions |
+| **Access tracking** | Phase tracks *how* each environment key was granted (individual vs team) so that removing team access doesn't accidentally revoke individually-granted keys |
+
+## Creating a Team
+
+1. Navigate to **Access** > **Teams** in the sidebar.
+
+2. Click **Create Team**.
+
+3. Enter a **name** and optional **description** for the team.
+
+4. Optionally set **role overrides** (see [Role overrides](#role-overrides) below). If you skip this, team members will use their org role for app-level permissions.
+
+5. Click **Create**. The team is created with no members or app access — you'll add those next. You are automatically set as the team **owner**.
+
+## Team ownership
+
+Each team has an **owner** — a team member with elevated management privileges. The team owner can always manage the team's app access and environment scope, even if their effective role (via a team role override) would not normally grant those permissions.
+
+When a team is created manually, the creator is automatically set as the owner. Teams created via [SCIM provisioning](/access-control/provisioning/scim) start with no owner, since the SCIM specification does not include group ownership information.
+
+### Who can set or transfer ownership
+
+| Action | Who can do it |
+|--------|---------------|
+| **Set owner** (when no owner exists) | Owner or Admin role users |
+| **Transfer ownership** | Current team owner, or Owner/Admin role users |
+
+The new owner must be an existing member of the team.
+
+### Setting or transferring ownership
+
+1. Open the team detail page by clicking on the team name.
+
+2. Scroll to the **Ownership** section near the bottom of the page. If the team has an owner, their name is displayed. If not, you'll see a prompt to assign one.
+
+3. Click **Set owner** or **Transfer ownership**.
+
+4. Select a team member from the list and confirm.
+
+
+ If a team has no owner, only organisation Owner and Admin roles can assign one. This is relevant for SCIM-managed teams, where the team is created automatically without a designated owner.
+
+
+## Managing team members
+
+### Adding members
+
+1. Open the team detail page by clicking on the team name.
+
+2. Click **Add members** or **Add service accounts**.
+
+3. Select the members or service accounts you want to add from the list. Only org members who aren't already in the team are shown.
+
+4. Click **Add**. If the team already has app access, encryption keys are provisioned immediately for the new members.
+
+### Removing members
+
+1. On the team detail page, find the member you want to remove.
+
+2. Click the remove button next to their name.
+
+3. Confirm the removal. Phase will revoke the member's team-granted environment keys. If the member also has individual access to the same environments, those keys are preserved.
+
+## Managing team app access
+
+### Adding apps to a team
+
+1. On the team detail page, click **Add apps**.
+
+2. Select the app you want to add. Only SSE-enabled apps are shown — if an app doesn't appear, enable SSE on it first.
+
+3. Select which **environments** within the app the team should have access to.
+
+4. Click **Add**. Phase provisions environment encryption keys for all active team members. Members who haven't completed their account setup yet (e.g. SCIM-provisioned users who haven't logged in) will receive keys on their first login.
+
+### Updating environment access
+
+After a team has been added to an app, you can change which environments it has access to:
+
+1. Click **Manage** next to the app on the team detail page.
+
+2. Toggle environments on or off.
+
+3. Save. Keys are provisioned for newly added environments and revoked for removed environments.
+
+### Removing an app from a team
+
+1. Click the remove button next to the app on the team detail page.
+
+2. Confirm the removal. All team-granted environment keys for that app are revoked for every team member.
+
+## Role overrides
+
+By default, team members use their **org role's app-level permissions** when accessing team-granted apps. For example, a `Developer` in a team uses the Developer role's app permissions (can read/write secrets, but can't delete environments).
+
+Teams support two optional role overrides that change what permissions members have when accessing apps through that team:
+
+| Override | Applies to | Description |
+|----------|-----------|-------------|
+| **Member role** | Human users | Overrides the org role's app-level permissions for all human members of this team |
+| **Service account role** | Service accounts | Overrides the org role's app-level permissions for all service accounts in this team |
+
+When a role override is set, it replaces the org role's `app_permissions` for resources accessed through that team. Org-level permissions are unaffected.
+
+### Union semantics
+
+When a user has multiple access grants to the same app — whether through individual (direct) access, one or more teams, or a combination — Phase takes the **union** of all applicable permissions. If *any* grant permits an action, the user is allowed to perform it.
+
+Individual access uses the user's org role. Each team uses its role override (or the org role if no override is set).
+
+**Example:** Alice has individual access (org role: `Developer`) and is also in Team B (role: `Manager`). Both grant access to App X. Alice gets the union of Developer and Manager app-level permissions for App X.
+
+## SSE requirement
+
+Teams require [Server-Side Encryption (SSE)](/console/apps#encryption-mode) to be enabled on any app they access. This is because the server needs to provision environment encryption keys on behalf of team members — it uses the server-held copy of the environment key to create individually-wrapped copies for each member.
+
+If an app has SSE disabled, it won't appear in the app selection dialog when adding apps to a team. You can enable SSE on an app from the app's settings page under **Encryption Mode**.
+
+
+ Enabling SSE changes the encryption model for the app — the server gains the ability to decrypt secrets. This is required for Teams, all third-party sync integrations, and REST API access. See [Encryption Mode](/console/apps#encryption-mode) for details.
+
+
+## Team-owned service accounts
+
+Service accounts can be **owned by a team**, meaning their lifecycle and visibility are tied to that team. This is useful when a team needs dedicated service accounts for programmatic access that only team members can see and manage.
+
+### Creating a team-owned service account
+
+1. On the team detail page, click **Create Team Service Account** in the Service Accounts section.
+
+2. Enter a name for the account.
+
+3. If the team has a defined Service Account role, the role for this account will be fixed. If there is no defined role for service accounts in the team, select a role for this account.
+
+4. Click **Create**. The service account is automatically added as a member of the team and can access all apps and environments that the team has access to.
+
+Team-owned service accounts always use **Server-side KMS**, which is enabled automatically on creation. This allows any team member with the appropriate `ServiceAccountTokens` permissions to generate tokens for the account — without needing to be a designated Service Account Handler. This is essential for dynamic team membership, including [SCIM-managed teams](#scim-managed-teams), where members may join or leave at any time.
+
+Team-owned service accounts are visually distinguished with a team badge on both the team detail page and the organisation-level service accounts list.
+
+### Visibility and management rules
+
+| Account type | Who can see it | Who can manage it |
+|---------|---------------|-------------------|
+| **Org-level** (no team) | Anyone with `ServiceAccounts.read` | Anyone with `ServiceAccounts.update/delete` |
+| **Team-owned** | Team members + Owner/Admin | Team members with appropriate permissions + team owner + Owner/Admin |
+
+Users who are not members of the owning team will not see team-owned service accounts in the organisation-level service accounts list — unless they have global access (Owner/Admin).
+
+Management permissions for team-owned service accounts are determined by the team's [role overrides](#role-overrides). If the team has a **Member role** override, that role's permissions are used for actions like creating tokens, updating the account, or managing KMS settings. If no override is set, the member's org role is used. The **team owner** always has full management access regardless of role overrides.
+
+### Team deletion
+
+When a team is deleted, all team-owned service accounts are deleted and their tokens are revoked. Org-level service accounts that were added to the team as members are unaffected — they lose team-based app access but remain visible org-wide.
+
+### Removing a team-owned SA from its team
+
+A team-owned service account cannot be removed from its owning team. To dissociate it from the team, delete the service account. Ownership cannot be changed after creation — if you need an organisation-level account instead, create a new service account at the organisation level.
+
+## SCIM-managed teams
+
+When [SCIM provisioning](/access-control/provisioning/scim) is configured, your identity provider can create and manage teams automatically by syncing IdP groups to Phase teams. SCIM-managed teams have the following restrictions:
+
+- Cannot be renamed, deleted, or have members manually added/removed from the Phase Console
+- Membership changes must be made in the identity provider
+- A `SCIM` badge is shown next to the team name in the console
+- Role overrides and app access can still be managed from Phase
+- SCIM-managed teams are created without an owner — an Owner or Admin must [assign one](#setting-or-transferring-ownership) to enable owner-level management
+
+## Access tracking
+
+Phase tracks **how** each environment key was granted — whether through direct (individual) access or through team membership. Each key grant records its source, including a reference to the specific team when applicable.
+
+This prevents a common pitfall: if a user has both individual and team access to the same environment, removing the team should *not* revoke their access. Phase only revokes an environment key when all of its access grants have been removed — so individually-granted keys are preserved even after a team is removed.
+
+## Viewing team access on member and app pages
+
+### Member detail page
+
+Each member's detail page (under **Access** > **Members** > member name) shows a **Teams** section listing all teams the member belongs to, along with links to each team's detail page.
+
+### App access page
+
+Each app's **Access** > **Teams** tab shows all teams that have access to the app, the environments they can access, and their role overrides. You can add/remove teams and manage environment scoping directly from this page.
diff --git a/public/cli/install.md b/public/cli/install.md
index 937fc346..1cf5fa74 100644
--- a/public/cli/install.md
+++ b/public/cli/install.md
@@ -37,11 +37,21 @@ curl -fsSL https://pkg.phase.dev/install.sh | sh
# curl -fsSL https://pkg.phase.dev/install.sh | sh -s -- --version 2.0.0
```
-```fish {{ title: 'Windows' }}
+```fish {{ title: 'Scoop' }}
scoop bucket add phasehq https://github.com/phasehq/scoop-cli.git
scoop install phase
```
+```fish {{ title: 'Windows (.exe)' }}
+# Download the latest .exe for your architecture from:
+# https://github.com/phasehq/cli/releases/latest
+#
+# phase_cli__windows_amd64.exe (Intel / AMD 64-bit)
+# phase_cli__windows_arm64.exe (ARM 64-bit)
+#
+# Rename to phase.exe and add it to a directory on your PATH.
+```
+
```fish {{ title: 'Alpine Linux' }}
apk add --no-cache curl
curl -fsSL https://pkg.phase.dev/install.sh | sh
@@ -73,10 +83,16 @@ brew update && brew upgrade phase
phase update
```
-```fish {{ title: 'Windows' }}
+```fish {{ title: 'Scoop' }}
scoop update phase
```
+```fish {{ title: 'Windows (.exe)' }}
+# Re-download the latest .exe from:
+# https://github.com/phasehq/cli/releases/latest
+# and replace your existing phase.exe.
+```
+
```fish {{ title: 'Alpine Linux' }}
phase update
```
diff --git a/public/llms.txt b/public/llms.txt
index 936b3b49..68276138 100644
--- a/public/llms.txt
+++ b/public/llms.txt
@@ -28,6 +28,8 @@
- [access-control/authentication/oauth-sso](https://docs.phase.dev/access-control/authentication/oauth-sso.md)
- [access-control/authentication/oidc-sso](https://docs.phase.dev/access-control/authentication/oidc-sso.md)
- [access-control/authentication/tokens](https://docs.phase.dev/access-control/authentication/tokens.md)
+- [access-control/provisioning/scim](https://docs.phase.dev/access-control/provisioning/scim.md)
+- [access-control/teams](https://docs.phase.dev/access-control/teams.md)
- [access-control/service-accounts](https://docs.phase.dev/access-control/service-accounts.md)
- [access-control/external-identities](https://docs.phase.dev/access-control/external-identities.md)
- [access-control/roles](https://docs.phase.dev/access-control/roles.md)
diff --git a/public/quickstart.md b/public/quickstart.md
index 2d630d63..7ff4a092 100644
--- a/public/quickstart.md
+++ b/public/quickstart.md
@@ -42,11 +42,21 @@ brew install phasehq/cli/phase
curl -fsSL https://pkg.phase.dev/install.sh | bash
```
-```fish {{ title: 'Windows' }}
+```fish {{ title: 'Scoop' }}
scoop bucket add phasehq https://github.com/phasehq/scoop-cli.git
scoop install phase
```
+```fish {{ title: 'Windows (.exe)' }}
+# Download the latest .exe for your architecture from:
+# https://github.com/phasehq/cli/releases/latest
+#
+# phase_cli__windows_amd64.exe (Intel / AMD 64-bit)
+# phase_cli__windows_arm64.exe (ARM 64-bit)
+#
+# Rename to phase.exe and add it to a directory on your PATH.
+```
+
```fish {{ title: 'Alpine Linux' }}
curl -fsSL https://pkg.phase.dev/install.sh | bash
```
diff --git a/public/sitemap.xml b/public/sitemap.xml
index dba06453..46c06d83 100644
--- a/public/sitemap.xml
+++ b/public/sitemap.xml
@@ -1,86 +1,88 @@
-https://docs.phase.dev2026-04-28T12:43:37.759Zdaily0.7
-https://docs.phase.dev/access-control2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/access-control/authentication2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/access-control/authentication/oauth-sso2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/access-control/authentication/oidc-sso2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/access-control/authentication/password2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/access-control/authentication/sso2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/access-control/authentication/tokens2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/access-control/external-identities2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/access-control/network2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/access-control/roles2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/access-control/service-accounts2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/cli2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/cli/commands2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/cli/install2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/cli/usage2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/console2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/console/apps2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/console/dynamic-secrets2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/console/environments2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/console/organisation2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/console/secrets2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/console/users2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/agents/claude-code2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/agents/codex2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/agents/cursor2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/agents/opencode2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/agents/vscode-copilot2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/frameworks2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/aws-codebuild2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/aws-elastic-container-service2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/aws-iam2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/aws-secrets-manager2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/azure-key-vault2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/azure-pipelines2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/bitbucket-pipelines2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/buildkite2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/circleci2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/cloudflare-pages2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/cloudflare-workers2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/docker2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/docker-compose2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/drone-ci2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/github-actions2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/github-dependabot2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/gitlab-ci2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/hashicorp-nomad2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/hashicorp-terraform2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/hashicorp-vault2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/jenkins2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/kubernetes2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/railway2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/render2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/teamcity2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/travis-ci2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/integrations/platforms/vercel2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/public-api2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/public-api/dynamic-secrets2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/public-api/errors2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/public-api/external-identities2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/public-api/secrets2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/quickstart2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/sdks2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/sdks/go2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/sdks/js2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/sdks/node2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/sdks/python2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/security2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/security/architecture2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/security/cryptography2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/self-hosting2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/self-hosting/aws2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/self-hosting/aws-eks2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/self-hosting/azure2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/self-hosting/configuration/envars2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/self-hosting/digitalocean2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/self-hosting/docker-compose2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/self-hosting/gcp2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/self-hosting/kubernetes2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/self-hosting/maintenance2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/self-hosting/railway2026-04-28T12:43:37.760Zdaily0.7
-https://docs.phase.dev/self-hosting/raspberrypi2026-04-28T12:43:37.760Zdaily0.7
+https://docs.phase.dev2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/access-control2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/access-control/authentication2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/access-control/authentication/oauth-sso2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/access-control/authentication/oidc-sso2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/access-control/authentication/password2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/access-control/authentication/sso2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/access-control/authentication/tokens2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/access-control/external-identities2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/access-control/network2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/access-control/provisioning/scim2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/access-control/roles2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/access-control/service-accounts2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/access-control/teams2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/cli2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/cli/commands2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/cli/install2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/cli/usage2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/console2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/console/apps2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/console/dynamic-secrets2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/console/environments2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/console/organisation2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/console/secrets2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/console/users2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/agents/claude-code2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/agents/codex2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/agents/cursor2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/agents/opencode2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/agents/vscode-copilot2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/frameworks2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/aws-codebuild2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/aws-elastic-container-service2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/aws-iam2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/aws-secrets-manager2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/azure-key-vault2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/azure-pipelines2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/bitbucket-pipelines2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/buildkite2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/circleci2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/cloudflare-pages2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/cloudflare-workers2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/docker2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/docker-compose2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/drone-ci2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/github-actions2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/github-dependabot2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/gitlab-ci2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/hashicorp-nomad2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/hashicorp-terraform2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/hashicorp-vault2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/jenkins2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/kubernetes2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/railway2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/render2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/teamcity2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/travis-ci2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/integrations/platforms/vercel2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/public-api2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/public-api/dynamic-secrets2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/public-api/errors2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/public-api/external-identities2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/public-api/secrets2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/quickstart2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/sdks2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/sdks/go2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/sdks/js2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/sdks/node2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/sdks/python2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/security2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/security/architecture2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/security/cryptography2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/self-hosting2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/self-hosting/aws2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/self-hosting/aws-eks2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/self-hosting/azure2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/self-hosting/configuration/envars2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/self-hosting/digitalocean2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/self-hosting/docker-compose2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/self-hosting/gcp2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/self-hosting/kubernetes2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/self-hosting/maintenance2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/self-hosting/railway2026-05-01T11:33:56.846Zdaily0.7
+https://docs.phase.dev/self-hosting/raspberrypi2026-05-01T11:33:56.846Zdaily0.7
\ No newline at end of file
diff --git a/src/components/Navigation.jsx b/src/components/Navigation.jsx
index cb7fbcc9..9153584b 100644
--- a/src/components/Navigation.jsx
+++ b/src/components/Navigation.jsx
@@ -261,6 +261,11 @@ export const navigation = [
href: '/access-control/authentication/oidc-sso',
},
{ title: 'Tokens', href: '/access-control/authentication/tokens' },
+ {
+ title: 'SCIM Provisioning',
+ href: '/access-control/provisioning/scim',
+ },
+ { title: 'Teams', href: '/access-control/teams' },
{ title: 'Service Accounts', href: '/access-control/service-accounts' },
{
title: 'External Identities',
diff --git a/src/pages/access-control/index.mdx b/src/pages/access-control/index.mdx
index 968505d5..f422fefe 100644
--- a/src/pages/access-control/index.mdx
+++ b/src/pages/access-control/index.mdx
@@ -34,9 +34,13 @@ User authentication in Phase is designed for seamless and secure access for huma
Programmatic access to secrets in Phase is facilitated by Service Accounts. A Service Account is a special type of account that represents non-human users, such as applications, automation processes, or CI/CD pipelines, that need to interact with Phase programmatically through the API, SDK, or CLI.
-[Read more](/access-control/service-accounts) about how service accounts work.
+[Read more](/access-control/service-accounts) about how service accounts work.
+## Teams
+Teams let you group members and service accounts together and grant them scoped access to apps and environments. Instead of managing access individually, you assign access at the team level — when someone joins the team, they automatically receive the right encryption keys. Teams support optional role overrides and work with SCIM provisioning for automated group sync.
+
+[Read more](/access-control/teams) about how teams work.
## Access Control - Key Concepts
@@ -78,6 +82,7 @@ Phase's RBAC system allows you to define permissions for Create, Read, Update, a
| **Members** | Manage user access within the app |
| **Integrations** | Control setup and management of app integrations |
| **Encryption Mode** | Manage encryption settings for the app |
+| **Teams** | Manage team-based access within the app |
## Global Access
diff --git a/src/pages/access-control/provisioning/scim.mdx b/src/pages/access-control/provisioning/scim.mdx
new file mode 100644
index 00000000..7630dc81
--- /dev/null
+++ b/src/pages/access-control/provisioning/scim.mdx
@@ -0,0 +1,358 @@
+import { Tag } from '@/components/Tag'
+import { DocActions } from '@/components/DocActions'
+
+export const description = 'Set up SCIM v2 provisioning to automatically sync users and groups from your identity provider to Phase.'
+
+PROVISIONING
+
+# SCIM Provisioning
+
+SCIM (System for Cross-domain Identity Management) v2 enables automatic user and group provisioning from your identity provider (IdP) to Phase. When configured, your IdP can automatically create, update, and deactivate user accounts in Phase, eliminating the need for manual user management.
+
+
+
+
+ SCIM provisioning is available for organisations with an `Enterprise` tier subscription. See [Pricing](https://phase.dev/pricing).
+
+
+## How it works
+
+When SCIM is enabled, your identity provider communicates with Phase's SCIM v2 API to:
+
+- **Provision users**: Automatically create Phase accounts when users are assigned to the application in your IdP. Users are created with a `Pending` status until they complete their first login and account setup.
+- **Deprovision users**: Automatically deactivate Phase accounts when users are unassigned or disabled in your IdP. The user's environment keys are revoked and their org membership is soft-deleted.
+- **Sync groups**: Map IdP groups to Phase Teams, automatically managing team membership as group assignments change.
+- **Update user attributes**: Keep user details (name, email) in sync between your IdP and Phase.
+
+### User lifecycle
+
+1. **IdP assigns user** → SCIM creates a Phase user account with `Pending` status (SCIM + Pending badges shown in the console)
+2. **User logs in via SSO** → Phase auto-links the SSO account to the SCIM-provisioned user
+3. **User completes account setup** → User sets up their sudo password and recovery phrase, enabling them to decrypt secrets
+4. **IdP unassigns user** → SCIM deactivates the user, revokes environment keys, and removes them from the organisation
+
+## Enable SCIM in Phase
+
+Before configuring your identity provider, enable SCIM in the Phase Console and create a provisioning token:
+
+1. Log into the Phase Console as an **Owner** or **Admin**.
+
+2. Navigate to **Access** > **SCIM** in the sidebar.
+
+3. Toggle **Enable SCIM** to on.
+
+4. Copy the **SCIM Base URL** shown on the page. It will look like:
+
+ ```
+ https://[YOUR_PHASE_HOST]/service/scim/v2/
+ ```
+
+5. Click **Create token** to generate a SCIM bearer token. Give it a descriptive name (e.g., "Azure Entra ID") and select an expiry period.
+
+6. Copy the generated token immediately — it will not be shown again. This token will be used as the **Secret Token** in your identity provider's SCIM configuration.
+
+
+ Treat the SCIM token like a password. Anyone with this token can provision and deprovision users in your Phase organisation.
+
+
+## Microsoft Entra ID (Azure AD)
+
+Microsoft Entra ID supports SCIM-based provisioning through Enterprise Applications. Follow these steps to configure automatic user and group provisioning from Entra ID to Phase.
+
+### Prerequisites
+
+- A Microsoft Entra ID tenant with a P1 or P2 license (required for enterprise app provisioning)
+- An existing Phase Enterprise organisation with SCIM enabled and a SCIM token (see above)
+- OIDC SSO configured for Microsoft Entra ID (see [Entra ID OIDC setup](/access-control/authentication/oidc-sso#microsoft-entra-id--azure-ad))
+
+
+ SCIM provisioning and OIDC SSO are complementary features. SCIM handles account lifecycle (creation, deactivation), while OIDC handles authentication (login). You should configure both for the best experience.
+
+
+### Step 1: Create an Enterprise Application
+
+1. Sign in to the [Azure Portal](https://portal.azure.com).
+
+2. Navigate to **Microsoft Entra ID** > **Enterprise applications**.
+
+3. Click **+ New application** at the top.
+
+4. Click **+ Create your own application**.
+
+5. Enter a name for the application (e.g., "Phase Console SCIM"), select **Integrate any other application you find in the gallery (Non-gallery)**, and click **Create**.
+
+### Step 2: Configure Provisioning
+
+1. In your newly created Enterprise Application, select **Provisioning** from the left sidebar under *Manage*.
+
+2. Click **Get started** or set the **Provisioning Mode** to **Automatic**.
+
+3. Under **Admin Credentials**, enter:
+ - **Tenant URL**: The SCIM Base URL from Phase (e.g., `https://[YOUR_PHASE_HOST]/service/scim/v2/`)
+ - **Secret Token**: The SCIM token you generated in Phase
+
+4. Click **Test Connection** to verify connectivity. Azure should report "The supplied credentials are authorized to enable provisioning".
+
+5. Click **Save**.
+
+### Step 3: Configure Attribute Mappings
+
+After saving credentials, Azure will show the **Mappings** section. You need to configure both user and group mappings.
+
+#### User Attribute Mappings
+
+1. Click **Provision Microsoft Entra ID Users**.
+
+2. Ensure the following mappings are present (these are the minimum required by Phase's SCIM implementation):
+
+ | Microsoft Entra ID Attribute | customappsso Attribute | Mapping Type |
+ |------|-------|------|
+ | `userPrincipalName` | `userName` | Direct |
+ | `Switch([IsSoftDeleted], , "False", "True", "True", "False")` | `active` | Expression |
+ | `mail` | `emails[type eq "work"].value` | Direct |
+ | `displayName` | `displayName` | Direct |
+ | `givenName` | `name.givenName` | Direct |
+ | `surname` | `name.familyName` | Direct |
+
+
+ The `mail` → `emails[type eq "work"].value` mapping is important. Phase uses this email to link the SCIM-provisioned account with the SSO login. If your users don't have the `mail` attribute populated in Entra ID, you can map `userPrincipalName` to `emails[type eq "work"].value` instead.
+
+
+3. Click **Save**.
+
+#### Group Attribute Mappings
+
+1. Go back to the Provisioning page and click **Provision Microsoft Entra ID Groups**.
+
+2. Ensure the following mappings are present:
+
+ | Microsoft Entra ID Attribute | customappsso Attribute | Mapping Type |
+ |------|-------|------|
+ | `displayName` | `displayName` | Direct |
+ | `objectId` | `externalId` | Direct |
+ | `members` | `members` | Direct |
+
+3. Click **Save**.
+
+### Step 4: Assign Users and Groups
+
+1. Navigate to **Users and groups** in the left sidebar of your Enterprise Application.
+
+2. Click **+ Add user/group**.
+
+3. Select the users and/or groups you want to provision to Phase, then click **Assign**.
+
+
+ Only users and groups explicitly assigned to the Enterprise Application will be provisioned. The default scope setting is "Sync only assigned users and groups".
+
+
+### Step 5: Start Provisioning
+
+1. Go back to the **Provisioning** page.
+
+2. Click **Start provisioning** to begin the initial sync.
+
+3. The initial provisioning cycle may take a few minutes. You can monitor progress under **Provisioning logs** in Azure, or in the **SCIM > Provisioning Logs** section of the Phase Console.
+
+4. Once complete, provisioned users will appear on the Phase **Members** page with `SCIM` and `Pending` badges. Groups will appear as **Teams** in Phase.
+
+### Step 6: Test the Flow
+
+1. Verify that provisioned users appear in the Phase Console under **Access** > **Members** with the SCIM and Pending badges.
+
+2. Have a provisioned user log in via SSO (Entra ID OIDC). They will be redirected to the **account setup** page to set up their sudo password and recovery phrase.
+
+3. After completing account setup, the user should be able to access secrets in any environments their team has access to.
+
+### Troubleshooting: Microsoft Entra ID
+
+#### Provisioning shows "quarantine" status
+
+This usually means Azure received multiple errors from the SCIM endpoint. When quarantined, Azure exponentially backs off the retry interval (up to several hours between attempts). Check:
+- The SCIM token hasn't expired (check the Phase Console SCIM page)
+- The Phase instance is accessible from Azure's IP ranges
+- Review the provisioning logs in both Azure and the Phase Console for specific error messages
+
+To clear a quarantine and resume normal provisioning:
+
+1. In the Azure Portal, go to your Enterprise Application > **Provisioning**.
+2. Click **Stop provisioning** and wait for it to confirm.
+3. Click **Start provisioning** — this clears the quarantine state and triggers a fresh initial sync.
+4. Verify the **Provisioning Status** toggle is set to **On**, then click **Save**.
+
+
+ "Restart provisioning" clears the quarantine but does not always re-enable the provisioning toggle. Always verify the toggle is **On** and click **Save** after restarting.
+
+
+#### User can't log in after SCIM provisioning
+
+Ensure that:
+1. OIDC SSO is configured for Entra ID (SCIM handles provisioning, OIDC handles login)
+2. The user's email in Entra ID matches between the SCIM provisioning and the OIDC token claims
+3. The user is assigned to both the SCIM Enterprise Application and the OIDC App Registration (or the OIDC app allows all directory users)
+
+#### Re-provisioning a previously deactivated user
+
+If you remove a user from the application and then re-add them, Azure may skip provisioning with a `RedundantSoftDelete` error. To resolve this:
+
+1. Stop provisioning in the Enterprise Application
+2. Ensure the user is assigned under **Users and groups**
+3. Go to **Provisioning** > click **Restart provisioning** (this clears Azure's internal cycle state)
+4. If the issue persists, use **Provision on demand** for the specific user after restarting provisioning
+
+#### Groups not syncing as Teams
+
+Ensure that:
+- Group provisioning is enabled in the attribute mappings
+- The groups are assigned to the Enterprise Application
+- The `displayName` and `members` attributes are mapped correctly
+
+## Okta
+
+Okta supports SCIM-based provisioning through its application integration platform. Follow these steps to configure automatic user and group provisioning from Okta to Phase.
+
+### Prerequisites
+
+- An Okta organisation with the ability to create custom SCIM integrations
+- An existing Phase Enterprise organisation with SCIM enabled and a SCIM token (see [Enable SCIM in Phase](#enable-scim-in-phase))
+- OIDC SSO configured for Okta (see [Okta OIDC setup](/access-control/authentication/oidc-sso#okta))
+
+
+ SCIM provisioning and OIDC SSO are complementary features. SCIM handles account lifecycle (creation, deactivation), while OIDC handles authentication (login). You should configure both for the best experience.
+
+
+### Step 1: Create a SCIM Application in Okta
+
+If you already have an Okta OIDC app for Phase SSO, you can add SCIM provisioning to it. Otherwise, create a new app:
+
+1. Sign in to the [Okta Admin Console](https://admin.okta.com).
+
+2. Navigate to **Applications** > **Applications**.
+
+3. If adding SCIM to an existing app, click on your Phase OIDC app and skip to Step 2. Otherwise, click **Create App Integration**.
+
+4. Select **SWA - Secure Web Authentication** (this creates a bookmark app that supports SCIM provisioning), and click **Next**.
+
+5. Enter a name (e.g., "Phase Console") and the login URL for your Phase instance. Click **Finish**.
+
+
+ If you already have a separate OIDC app for SSO, you can use a SWA app purely for SCIM provisioning. Users will still authenticate via the OIDC app — the SWA app only handles user/group lifecycle.
+
+
+### Step 2: Enable SCIM Provisioning
+
+1. In your application, go to the **General** tab.
+
+2. Click **Edit** on the App Settings panel.
+
+3. Under **Provisioning**, select **SCIM** and click **Save**.
+
+4. A new **Provisioning** tab will appear. Click on it.
+
+5. Under **SCIM Connection**, click **Edit** and enter:
+ - **SCIM connector base URL**: The SCIM Base URL from Phase (e.g., `https://[YOUR_PHASE_HOST]/service/scim/v2`)
+ - **Unique identifier field for users**: `userName`
+ - **Supported provisioning actions**: Check **Push New Users**, **Push Profile Updates**, and **Push Groups**
+ - **Authentication Mode**: `HTTP Header`
+ - **Authorization**: Paste the SCIM bearer token you generated in Phase (the token value only, without the `Bearer` prefix)
+
+6. Click **Test Connector Configuration** to verify connectivity. Okta should report success for the enabled actions.
+
+7. Click **Save**.
+
+### Step 3: Configure Provisioning Actions
+
+1. Still on the **Provisioning** tab, click **To App** in the left panel.
+
+2. Click **Edit** and enable:
+ - **Create Users** — Allows Okta to provision new users in Phase
+ - **Update User Attributes** — Keeps user details in sync
+ - **Deactivate Users** — Deactivates Phase accounts when users are unassigned in Okta
+
+3. Click **Save**.
+
+### Step 4: Verify Attribute Mappings
+
+1. Still under **Provisioning** > **To App**, scroll down to the **Attribute Mappings** section.
+
+2. Okta pre-populates a comprehensive set of default attribute mappings. No changes are required — the defaults already include everything Phase needs. Verify that the following key mappings are present:
+
+ | Okta Attribute | App Attribute | Default Value |
+ |------|-------|------|
+ | `userName` | `userName` | Configured in Sign On settings |
+ | `givenName` | `name.givenName` | `user.firstName` |
+ | `familyName` | `name.familyName` | `user.lastName` |
+ | `email` | `emails[type eq "work"].value` | `user.email` |
+ | `displayName` | `displayName` | `user.displayName` |
+
+
+ Okta sends many additional attributes by default (phone, address, title, etc.). Phase safely ignores any attributes it doesn't need — you can leave them as-is or remove them if you prefer a minimal mapping.
+
+
+
+ Okta uses `userName` (typically the user's email address) as the primary identifier. Ensure that the `userName` in Okta matches the email address used for SSO login, so Phase can link the SCIM-provisioned account with the OIDC identity.
+
+
+### Step 5: Assign Users and Groups
+
+1. Go to the **Assignments** tab in your application.
+
+2. Click **Assign** and select either **Assign to People** or **Assign to Groups**.
+
+3. Select the users and/or groups you want to provision to Phase, then click **Assign** and **Save and Go Back**.
+
+4. If you assigned groups, go to the **Push Groups** tab:
+ - Click **Push Groups** > **Find groups by name** (or by rule)
+ - Select the groups you want to push to Phase
+ - Click **Save**
+
+
+ In Okta, assigning a group under **Assignments** grants group members access to the app (user provisioning). The **Push Groups** tab controls whether the group itself is synced to Phase as a Team. You typically need both: assign the group for user provisioning, and push the group for team creation.
+
+
+### Step 6: Verify Provisioning
+
+1. Navigate to **Dashboard** > **Tasks** in the Okta Admin Console to monitor provisioning progress.
+
+2. Check the Phase Console under **Access** > **Members** — provisioned users should appear with `SCIM` and `Pending` badges.
+
+3. Check **Access** > **Teams** — pushed groups should appear as teams with a `SCIM` badge.
+
+4. Have a provisioned user log in via Okta SSO. They will be redirected to the account setup page to set up their sudo password and recovery phrase.
+
+### Troubleshooting: Okta
+
+#### "Error authenticating" when testing the connector
+
+- Verify the SCIM base URL ends with `/scim/v2` (no trailing slash)
+- Verify the token is entered without the `Bearer` prefix — Okta adds it automatically
+- Ensure the SCIM token hasn't expired in the Phase Console
+
+#### Users not being provisioned
+
+- Check that **Create Users** is enabled under **Provisioning** > **To App**
+- Verify users are assigned to the application under the **Assignments** tab
+- Check the **Tasks** page in Okta for specific error messages
+- Review the SCIM provisioning logs in the Phase Console for request/response details
+
+#### Groups not syncing as Teams
+
+- Ensure the group is both **assigned** to the app (Assignments tab) and **pushed** (Push Groups tab)
+- Check that the group push status shows "Active" — if it shows an error, click on it for details
+- Verify that group push is enabled in the SCIM connector settings (**Push Groups** checkbox)
+
+#### User deprovisioned after removing direct assignment, despite being in a pushed group
+
+In Okta, **Push Groups** and **Assignments** are independent:
+- **Assignments** controls user provisioning (who gets a Phase account)
+- **Push Groups** controls group/team syncing (which groups appear as Phase Teams)
+
+If a user is only directly assigned (not via a group assignment), removing that direct assignment deprovisions them — even if they belong to a pushed group. Push Groups does not grant app assignment.
+
+**Fix**: Assign groups under the **Assignments** tab (not just Push Groups). This way, group membership is the single source of truth for both user provisioning and team sync. When a user is removed from the group in Okta, they are automatically deprovisioned and removed from the team.
+
+#### User can't log in after SCIM provisioning
+
+- Ensure OIDC SSO is configured for Okta (SCIM handles provisioning, OIDC handles login)
+- The user's email in Okta must match between the SCIM `userName` and the OIDC token `email` claim
+- If using separate apps for SCIM and OIDC, the user must be assigned to both
diff --git a/src/pages/access-control/roles.mdx b/src/pages/access-control/roles.mdx
index c7ada805..99c4b63c 100644
--- a/src/pages/access-control/roles.mdx
+++ b/src/pages/access-control/roles.mdx
@@ -44,6 +44,8 @@ The organization owner. This role is automatically assigned when a user creates
| **Roles** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Integration Credentials** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Network Access Policies** | Full access | ✅ | ✅ | ✅ | ✅ |
+| **Teams** | Full access | ✅ | ✅ | ✅ | ✅ |
+| **SCIM** | Full access | ✅ | ✅ | ✅ | ✅ |
| **SSO** | Full access | ✅ | ✅ | ✅ | ✅ |
#### App-level permissions:
@@ -60,6 +62,7 @@ The organization owner. This role is automatically assigned when a user creates
| **Service Accounts** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Integrations** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Encryption Mode** | Full access | ✅ | | ✅ | |
+| **Teams** | Full access | ✅ | ✅ | ✅ | ✅ |
### Admin
@@ -80,6 +83,8 @@ Admin users have access to most resources and permissions, and have global acces
| **Roles** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Integration Credentials** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Network Access Policies** | Full access | ✅ | ✅ | ✅ | ✅ |
+| **Teams** | Full access | ✅ | ✅ | ✅ | ✅ |
+| **SCIM** | Full access | ✅ | ✅ | ✅ | ✅ |
| **SSO** | Full access | ✅ | ✅ | ✅ | ✅ |
#### App-level permissions:
@@ -96,6 +101,7 @@ Admin users have access to most resources and permissions, and have global acces
| **Service Accounts** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Integrations** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Encryption Mode** | Custom access | ✅ | | ✅ | |
+| **Teams** | Full access | ✅ | ✅ | ✅ | ✅ |
The `Owner` and `Admin` roles have global access. Learn more about global access [here](/access-control#global-access).
@@ -118,6 +124,8 @@ Management users with broad access to environments, secrets, and service account
| **Roles** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Integration Credentials** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Network Access Policies** | Full access | ✅ | ✅ | ✅ | ✅ |
+| **Teams** | Full access | ✅ | ✅ | ✅ | ✅ |
+| **SCIM** | No access | ❌ | ❌ | ❌ | ❌ |
| **SSO** | No access | ❌ | ❌ | ❌ | ❌ |
#### App-level permissions:
@@ -134,6 +142,7 @@ Management users with broad access to environments, secrets, and service account
| **Service Accounts** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Integrations** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Encryption Mode** | Custom access | ✅ | ❌ | ✅ | ❌ |
+| **Teams** | Full access | ✅ | ✅ | ✅ | ✅ |
### Service
@@ -154,6 +163,8 @@ Default role for Service Accounts, providing programmatic access to secrets with
| **Roles** | Read access | ✅ | ❌ | ❌ | ❌ |
| **Integration Credentials** | Read access | ✅ | ❌ | ❌ | ❌ |
| **Network Access Policies** | Read access | ✅ | ❌ | ❌ | ❌ |
+| **Teams** | No access | ❌ | ❌ | ❌ | ❌ |
+| **SCIM** | No access | ❌ | ❌ | ❌ | ❌ |
| **SSO** | No access | ❌ | ❌ | ❌ | ❌ |
#### App-level permissions:
@@ -170,6 +181,7 @@ Default role for Service Accounts, providing programmatic access to secrets with
| **Service Accounts** | Read access | ✅ | ❌ | ❌ | ❌ |
| **Integrations** | Read access | ✅ | ❌ | ❌ | ❌ |
| **Encryption Mode** | Read access | ✅ | ❌ | ❌ | ❌ |
+| **Teams** | Read access | ✅ | ❌ | ❌ | ❌ |
### Developer
@@ -190,6 +202,8 @@ Developers have limited permissions at the organization level and must be given
| **Roles** | Read access | ✅ | ❌ | ❌ | ❌ |
| **Integration Credentials** | Custom access | ✅ | ✅ | ✅ | ❌ |
| **Network Access Policies** | Read access | ✅ | ❌ | ❌ | ❌ |
+| **Teams** | Read access | ✅ | ❌ | ❌ | ❌ |
+| **SCIM** | No access | ❌ | ❌ | ❌ | ❌ |
| **SSO** | No access | ❌ | ❌ | ❌ | ❌ |
#### App-level permissions:
@@ -203,9 +217,10 @@ Developers have limited permissions at the organization level and must be given
| **Logs** | Read access | ✅ | ❌ | ❌ | ❌ |
| **Tokens (Legacy)** | Custom access | ✅ | ✅ | ❌ | ❌ |
| **Members** | Read access | ✅ | ❌ | ❌ | ❌ |
-| **Service Accounts** | Read access | ✅ | ❌ | ❌ | ❌ |
+| **Service Accounts** | Custom access | ❌ | ✅ | ❌ | ❌ |
| **Integrations** | Full access | ✅ | ✅ | ✅ | ✅ |
| **Encryption Mode** | Custom access | ✅ | | ✅ | |
+| **Teams** | Read access | ✅ | ❌ | ❌ | ❌ |
## Custom Roles
@@ -265,6 +280,12 @@ Some actions require a combination of permissions across multiple resources. Bel
- To view and delete other users Personal Access Tokens:
- `MemberPersonalAccessTokens:read`
- `MemberPersonalAccessTokens:delete`
+- Adding or removing a Team's access to an App:
+ - `Teams:create` or `Teams:delete` (app-level)
+ - `Teams:read` (organisation-level)
+- Managing a Team's environment scope within an App:
+ - `Teams:update` (app-level)
+ - `Teams:read` (organisation-level)
- To Manage Network Access Policies of a User or Service Account:
- `Members:read`
- `Members:update`
diff --git a/src/pages/access-control/service-accounts.mdx b/src/pages/access-control/service-accounts.mdx
index cc41d414..389b35b9 100644
--- a/src/pages/access-control/service-accounts.mdx
+++ b/src/pages/access-control/service-accounts.mdx
@@ -9,7 +9,16 @@ Service Accounts provide a secure and controlled method for programmatic access
Service accounts share many of the properties and behavior of human user accounts. Service Accounts follow an Access Policy that can be defined by [Managed Roles](/access-control/roles#managed-roles) or [Custom Roles](/access-control/roles#creating-custom-roles) based on the permissions required. Service accounts are secured with the same security and cryptographic architecture as user accounts, and must be manually provisioned access to Apps and Environments in order to access secrets.
-
+
+
+## Org-level vs Team-owned Service Accounts
+
+Service accounts exist in two categories:
+
+- **Org-level** (default): Visible to all organisation members with `ServiceAccounts.read` permission. Created from the organisation-level Service Accounts page. This is the default behavior and works on all plans.
+- **Team-owned**: Created within a [Team](/access-control/teams), visible only to team members and users with global access (Owner/Admin). The service account's lifecycle is tied to the team — if the team is deleted, team-owned SAs are deleted too.
+
+Team-owned service accounts are useful when a team needs dedicated programmatic access for team resources, that is isolated from other teams and users. See [Team-owned service accounts](/access-control/teams#team-owned-service-accounts) for details on creating and managing them.
## Create a new Service Account
@@ -79,12 +88,16 @@ To delete a Service Account, click on the "Delete" button at the bottom of the p
Each Service Account has its own unique keyring, just like User accounts. KMS modes determine who has access to the service account's keyring and can create and manage tokens for this service account.
#### Client-side KMS
-By default, Service Accounts use **Client-side KMS**. This means only designated users with the required `ServiceAccountTokens` permissions have access to create and manage tokens for this service account. These users are called *Service Account Handlers* and have access the service account's keyring, encrypted with their own keys.
+By default, org-level Service Accounts use **Client-side KMS**. This means only designated users with the required `ServiceAccountTokens` permissions have access to create and manage tokens for this service account. These users are called *Service Account Handlers* and have access the service account's keyring, encrypted with their own keys.
#### Server-side KMS
You can optionally enable **Server-side KMS** for a Service Account. This grants the Phase backend access to the service account's keyring, effectively making the backend a *Service Account Handler*. Enabling Server-side KMS allows the backend to create and manage tokens on behalf of the Service Account. This is required to use features such as [External Identities](/access-control/external-identities).
+
+ **Team-owned service accounts** always use Server-side KMS. This is enabled automatically when the account is created, so that any team member with the appropriate permissions can generate tokens — without needing to be a designated Service Account Handler. This is important for dynamic team membership, including teams managed via [SCIM provisioning](/access-control/provisioning/scim), where members may join or leave at any time.
+
+
#### Manage KMS mode
You can manage the KMS mode for a Service Account by clicking the **Manage** button beside the account KMS indicator at the top of the account page:
diff --git a/src/pages/access-control/teams.mdx b/src/pages/access-control/teams.mdx
new file mode 100644
index 00000000..96877f4c
--- /dev/null
+++ b/src/pages/access-control/teams.mdx
@@ -0,0 +1,222 @@
+import { Tag } from '@/components/Tag'
+import { DocActions } from '@/components/DocActions'
+
+export const description = 'Organize members and service accounts into Teams with scoped app access and optional role overrides.'
+
+ACCESS CONTROL
+
+# Teams
+
+Teams let you group members and service accounts together and grant them access to specific apps and environments. Instead of managing access for each individual, you assign access at the team level — when someone joins the team, they automatically get the right keys.
+
+
+
+
+ Teams require apps with [Server-Side Encryption (SSE)](/console/apps#encryption-mode) enabled, since the server provisions encryption keys on behalf of team members.
+
+
+## How it works
+
+A Team is an organisation-level group that contains **members** (human users) and/or **service accounts**. Teams are granted access to one or more apps, scoped to specific environments within each app.
+
+When a team is added to an app:
+
+1. You select which environments the team should have access to (e.g. `Development`, `Staging`)
+2. Phase automatically provisions environment encryption keys for every team member
+3. Team members can immediately decrypt secrets in those environments
+
+When a member is added to a team that already has app access, keys are provisioned automatically. When a member is removed, their team-granted keys are revoked — unless they also have individual access to the same environment.
+
+### Key concepts
+
+| Concept | Description |
+|---------|-------------|
+| **Team ownership** | Each team has an owner who retains full management access regardless of role overrides |
+| **Team membership** | A user or service account belongs to one or more teams |
+| **Team app access** | A team is granted access to an app, scoped to specific environments |
+| **Role overrides** | Teams can optionally override the org role's app-level permissions for their members |
+| **Union semantics** | If a user has multiple access grants to the same app (individual, one or more teams), they get the **union** of all applicable permissions |
+| **Access tracking** | Phase tracks *how* each environment key was granted (individual vs team) so that removing team access doesn't accidentally revoke individually-granted keys |
+
+## Creating a Team
+
+1. Navigate to **Access** > **Teams** in the sidebar.
+
+2. Click **Create Team**.
+
+3. Enter a **name** and optional **description** for the team.
+
+4. Optionally set **role overrides** (see [Role overrides](#role-overrides) below). If you skip this, team members will use their org role for app-level permissions.
+
+5. Click **Create**. The team is created with no members or app access — you'll add those next. You are automatically set as the team **owner**.
+
+## Team ownership
+
+Each team has an **owner** — a team member with elevated management privileges. The team owner can always manage the team's app access and environment scope, even if their effective role (via a team role override) would not normally grant those permissions.
+
+When a team is created manually, the creator is automatically set as the owner. Teams created via [SCIM provisioning](/access-control/provisioning/scim) start with no owner, since the SCIM specification does not include group ownership information.
+
+### Who can set or transfer ownership
+
+| Action | Who can do it |
+|--------|---------------|
+| **Set owner** (when no owner exists) | Owner or Admin role users |
+| **Transfer ownership** | Current team owner, or Owner/Admin role users |
+
+The new owner must be an existing member of the team.
+
+### Setting or transferring ownership
+
+1. Open the team detail page by clicking on the team name.
+
+2. Scroll to the **Ownership** section near the bottom of the page. If the team has an owner, their name is displayed. If not, you'll see a prompt to assign one.
+
+3. Click **Set owner** or **Transfer ownership**.
+
+4. Select a team member from the list and confirm.
+
+
+ If a team has no owner, only organisation Owner and Admin roles can assign one. This is relevant for SCIM-managed teams, where the team is created automatically without a designated owner.
+
+
+## Managing team members
+
+### Adding members
+
+1. Open the team detail page by clicking on the team name.
+
+2. Click **Add members** or **Add service accounts**.
+
+3. Select the members or service accounts you want to add from the list. Only org members who aren't already in the team are shown.
+
+4. Click **Add**. If the team already has app access, encryption keys are provisioned immediately for the new members.
+
+### Removing members
+
+1. On the team detail page, find the member you want to remove.
+
+2. Click the remove button next to their name.
+
+3. Confirm the removal. Phase will revoke the member's team-granted environment keys. If the member also has individual access to the same environments, those keys are preserved.
+
+## Managing team app access
+
+### Adding apps to a team
+
+1. On the team detail page, click **Add apps**.
+
+2. Select the app you want to add. Only SSE-enabled apps are shown — if an app doesn't appear, enable SSE on it first.
+
+3. Select which **environments** within the app the team should have access to.
+
+4. Click **Add**. Phase provisions environment encryption keys for all active team members. Members who haven't completed their account setup yet (e.g. SCIM-provisioned users who haven't logged in) will receive keys on their first login.
+
+### Updating environment access
+
+After a team has been added to an app, you can change which environments it has access to:
+
+1. Click **Manage** next to the app on the team detail page.
+
+2. Toggle environments on or off.
+
+3. Save. Keys are provisioned for newly added environments and revoked for removed environments.
+
+### Removing an app from a team
+
+1. Click the remove button next to the app on the team detail page.
+
+2. Confirm the removal. All team-granted environment keys for that app are revoked for every team member.
+
+## Role overrides
+
+By default, team members use their **org role's app-level permissions** when accessing team-granted apps. For example, a `Developer` in a team uses the Developer role's app permissions (can read/write secrets, but can't delete environments).
+
+Teams support two optional role overrides that change what permissions members have when accessing apps through that team:
+
+| Override | Applies to | Description |
+|----------|-----------|-------------|
+| **Member role** | Human users | Overrides the org role's app-level permissions for all human members of this team |
+| **Service account role** | Service accounts | Overrides the org role's app-level permissions for all service accounts in this team |
+
+When a role override is set, it replaces the org role's `app_permissions` for resources accessed through that team. Org-level permissions are unaffected.
+
+### Union semantics
+
+When a user has multiple access grants to the same app — whether through individual (direct) access, one or more teams, or a combination — Phase takes the **union** of all applicable permissions. If *any* grant permits an action, the user is allowed to perform it.
+
+Individual access uses the user's org role. Each team uses its role override (or the org role if no override is set).
+
+**Example:** Alice has individual access (org role: `Developer`) and is also in Team B (role: `Manager`). Both grant access to App X. Alice gets the union of Developer and Manager app-level permissions for App X.
+
+## SSE requirement
+
+Teams require [Server-Side Encryption (SSE)](/console/apps#encryption-mode) to be enabled on any app they access. This is because the server needs to provision environment encryption keys on behalf of team members — it uses the server-held copy of the environment key to create individually-wrapped copies for each member.
+
+If an app has SSE disabled, it won't appear in the app selection dialog when adding apps to a team. You can enable SSE on an app from the app's settings page under **Encryption Mode**.
+
+
+ Enabling SSE changes the encryption model for the app — the server gains the ability to decrypt secrets. This is required for Teams, all third-party sync integrations, and REST API access. See [Encryption Mode](/console/apps#encryption-mode) for details.
+
+
+## Team-owned service accounts
+
+Service accounts can be **owned by a team**, meaning their lifecycle and visibility are tied to that team. This is useful when a team needs dedicated service accounts for programmatic access that only team members can see and manage.
+
+### Creating a team-owned service account
+
+1. On the team detail page, click **Create Team Service Account** in the Service Accounts section.
+
+2. Enter a name for the account.
+
+3. If the team has a defined Service Account role, the role for this account will be fixed. If there is no defined role for service accounts in the team, select a role for this account.
+
+4. Click **Create**. The service account is automatically added as a member of the team and can access all apps and environments that the team has access to.
+
+Team-owned service accounts always use **Server-side KMS**, which is enabled automatically on creation. This allows any team member with the appropriate `ServiceAccountTokens` permissions to generate tokens for the account — without needing to be a designated Service Account Handler. This is essential for dynamic team membership, including [SCIM-managed teams](#scim-managed-teams), where members may join or leave at any time.
+
+Team-owned service accounts are visually distinguished with a team badge on both the team detail page and the organisation-level service accounts list.
+
+### Visibility and management rules
+
+| Account type | Who can see it | Who can manage it |
+|---------|---------------|-------------------|
+| **Org-level** (no team) | Anyone with `ServiceAccounts.read` | Anyone with `ServiceAccounts.update/delete` |
+| **Team-owned** | Team members + Owner/Admin | Team members with appropriate permissions + team owner + Owner/Admin |
+
+Users who are not members of the owning team will not see team-owned service accounts in the organisation-level service accounts list — unless they have global access (Owner/Admin).
+
+Management permissions for team-owned service accounts are determined by the team's [role overrides](#role-overrides). If the team has a **Member role** override, that role's permissions are used for actions like creating tokens, updating the account, or managing KMS settings. If no override is set, the member's org role is used. The **team owner** always has full management access regardless of role overrides.
+
+### Team deletion
+
+When a team is deleted, all team-owned service accounts are deleted and their tokens are revoked. Org-level service accounts that were added to the team as members are unaffected — they lose team-based app access but remain visible org-wide.
+
+### Removing a team-owned SA from its team
+
+A team-owned service account cannot be removed from its owning team. To dissociate it from the team, delete the service account. Ownership cannot be changed after creation — if you need an organisation-level account instead, create a new service account at the organisation level.
+
+## SCIM-managed teams
+
+When [SCIM provisioning](/access-control/provisioning/scim) is configured, your identity provider can create and manage teams automatically by syncing IdP groups to Phase teams. SCIM-managed teams have the following restrictions:
+
+- Cannot be renamed, deleted, or have members manually added/removed from the Phase Console
+- Membership changes must be made in the identity provider
+- A `SCIM` badge is shown next to the team name in the console
+- Role overrides and app access can still be managed from Phase
+- SCIM-managed teams are created without an owner — an Owner or Admin must [assign one](#setting-or-transferring-ownership) to enable owner-level management
+
+## Access tracking
+
+Phase tracks **how** each environment key was granted — whether through direct (individual) access or through team membership. Each key grant records its source, including a reference to the specific team when applicable.
+
+This prevents a common pitfall: if a user has both individual and team access to the same environment, removing the team should *not* revoke their access. Phase only revokes an environment key when all of its access grants have been removed — so individually-granted keys are preserved even after a team is removed.
+
+## Viewing team access on member and app pages
+
+### Member detail page
+
+Each member's detail page (under **Access** > **Members** > member name) shows a **Teams** section listing all teams the member belongs to, along with links to each team's detail page.
+
+### App access page
+
+Each app's **Access** > **Teams** tab shows all teams that have access to the app, the environments they can access, and their role overrides. You can add/remove teams and manage environment scoping directly from this page.