diff --git a/apps/documentation/pages/users/organisations/_meta.json b/apps/documentation/pages/users/organisations/_meta.json
index b77f9137c..dfc75fc95 100644
--- a/apps/documentation/pages/users/organisations/_meta.json
+++ b/apps/documentation/pages/users/organisations/_meta.json
@@ -3,5 +3,6 @@
"members": "Members",
"groups": "Groups",
"teams": "Teams",
+ "sso": "SSO",
"billing": "Billing"
-}
\ No newline at end of file
+}
diff --git a/apps/documentation/pages/users/organisations/sso/_meta.json b/apps/documentation/pages/users/organisations/sso/_meta.json
new file mode 100644
index 000000000..4ba07c6f6
--- /dev/null
+++ b/apps/documentation/pages/users/organisations/sso/_meta.json
@@ -0,0 +1,4 @@
+{
+ "index": "Configuration",
+ "microsoft-entra-id": "Microsoft Entra ID"
+}
diff --git a/apps/documentation/pages/users/organisations/sso/index.mdx b/apps/documentation/pages/users/organisations/sso/index.mdx
new file mode 100644
index 000000000..c909b3336
--- /dev/null
+++ b/apps/documentation/pages/users/organisations/sso/index.mdx
@@ -0,0 +1,149 @@
+---
+title: SSO Portal
+description: Learn how to set up a custom SSO login portal for your organisation.
+---
+
+import Image from 'next/image';
+
+import { Callout, Steps } from 'nextra/components';
+
+# Organisation SSO Portal
+
+The SSO Portal provides a dedicated login URL for your organisation that integrates with any OIDC compliant identity provider. This feature provides:
+
+- **Single Sign-On**: Access Documenso using your own authentication system
+- **Automatic onboarding**: New users will be automatically added to your organisation when they sign in through the portal
+- **Delegated account management**: Your organisation has full control over the users who sign in through the portal
+
+
+ Anyone who signs in through your portal will be added to your organisation as a member.
+
+
+## Getting Started
+
+To set up the SSO Portal, you need to be an organisation owner, admin, or manager.
+
+
+ **Enterprise Only**: This feature is only available to Enterprise customers.
+
+
+
+
+### Access Organisation SSO Settings
+
+
+
+### Configure SSO Portal
+
+See the [Microsoft Entra ID](/users/organisations/sso/microsoft-entra-id) guide to find the values for the following fields.
+
+#### Issuer URL
+
+Enter the OpenID discovery endpoint URL for your provider. Here are some common examples:
+
+- **Google Workspace**: `https://accounts.google.com/.well-known/openid-configuration`
+- **Microsoft Entra ID**: `https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration`
+- **Okta**: `https://{your-domain}.okta.com/.well-known/openid-configuration`
+- **Auth0**: `https://{your-domain}.auth0.com/.well-known/openid-configuration`
+
+#### Client Credentials
+
+Enter the client ID and client secret provided by your identity provider:
+
+- **Client ID**: The unique identifier for your application
+- **Client Secret**: The secret key for authenticating your application
+
+#### Default Organisation Role
+
+Select the default Organisation role that new users will receive when they first sign in through the portal.
+
+#### Allowed Email Domains
+
+Specify which email domains are allowed to sign in through your SSO portal. Separate domains with spaces:
+
+```
+your-domain.com another-domain.com
+```
+
+Leave this field empty to allow all domains.
+
+### Configure Your Identity Provider
+
+You'll need to configure your identity provider with the following information:
+
+- Redirect URI
+- Scopes
+
+These values are found at the top of the page.
+
+### Save Configuration
+
+Toggle the "Enable SSO portal" switch to activate the feature for your organisation.
+
+Click "Update" to save your SSO portal configuration. The portal will be activated once all required fields are completed.
+
+
+
+## Testing Your SSO Portal
+
+Once configured, you can test your SSO portal by:
+
+1. Navigating to your portal URL found at the top of the organisation SSO portal settings page
+2. Sign in with a test account from your configured domain
+3. Verifying that the user is properly provisioned with the correct organisation role
+
+## Best Practices
+
+### Reduce Friction
+
+Create a custom subdomain for your organisation's SSO portal. For example, you can create a subdomain like `documenso.your-organisation.com` which redirects to the portal link.
+
+### Security Considerations
+
+Please note that anyone who signs in through your portal will be added to your organisation as a member.
+
+- **Domain Restrictions**: Use allowed domains to prevent unauthorized access
+- **Role Assignment**: Carefully consider the default organisation role for new users
+
+## Troubleshooting
+
+### Common Issues
+
+**"Invalid issuer URL"**
+
+- Verify the issuer URL is correct and accessible
+- Ensure the URL follows the OpenID Connect discovery format
+
+**"Client authentication failed"**
+
+- Check that your client ID and client secret are correct
+- Verify that your application is properly registered with your identity provider
+
+**"User not provisioned"**
+
+- Check that the user's email domain is in the allowed domains list
+- Verify the default organisation role is set correctly
+
+**"Redirect URI mismatch"**
+
+- Ensure the redirect URI in Documenso matches exactly what's configured in your identity provider
+- Check for any trailing slashes or protocol mismatches
+
+### Getting Help
+
+If you encounter issues with your SSO portal configuration:
+
+1. Review your identity provider's documentation for OpenID Connect setup
+2. Check the Documenso logs for detailed error messages
+3. Contact your identity provider's support for provider-specific issues
+
+
+ For additional support for SSO Portal configuration, contact our support team at
+ support@documenso.com.
+
+
+## Identity Provider Guides
+
+For detailed setup instructions for specific identity providers:
+
+- [Microsoft Entra ID](/users/organisations/sso/microsoft-entra-id) - Complete guide for Azure AD configuration
diff --git a/apps/documentation/pages/users/organisations/sso/microsoft-entra-id.mdx b/apps/documentation/pages/users/organisations/sso/microsoft-entra-id.mdx
new file mode 100644
index 000000000..6b3ed6d65
--- /dev/null
+++ b/apps/documentation/pages/users/organisations/sso/microsoft-entra-id.mdx
@@ -0,0 +1,76 @@
+---
+title: Microsoft Entra ID
+description: Learn how to configure Microsoft Entra ID (Azure AD) for your organisation's SSO portal.
+---
+
+import Image from 'next/image';
+
+import { Callout, Steps } from 'nextra/components';
+
+# Microsoft Entra ID Configuration
+
+Microsoft Entra ID (formerly Azure Active Directory) is a popular identity provider for enterprise SSO. This guide will walk you through creating an app registration and configuring it for use with your Documenso SSO portal.
+
+## Prerequisites
+
+- Access to Microsoft Entra ID (Azure AD) admin center
+- Access to your Documenso organisation as an administrator or manager
+
+Each user in your Azure AD will need an email associated with it.
+
+## Creating an App Registration
+
+
+
+### Access Azure Portal
+
+1. Navigate to the Azure Portal
+2. Sign in with your Microsoft Entra ID administrator account
+3. Search for "Azure Active Directory" or "Microsoft Entra ID" in the search bar
+4. Click on "Microsoft Entra ID" from the results
+
+### Create App Registration
+
+1. In the left sidebar, click on "App registrations"
+2. Click the "New registration" button
+
+### Configure App Registration
+
+Fill in the registration form with the following details:
+
+- **Name**: Your preferred name (e.g. `Documenso SSO Portal`)
+- **Supported account types**: Choose based on your needs
+- **Redirect URI (Web)**: Found in the Documenso SSO portal settings page
+
+Click "Register" to create the app registration.
+
+### Get Client ID
+
+After registration, you'll be taken to the app's overview page. The **Application (client) ID** is displayed prominently - this is your Client ID for Documenso.
+
+### Create Client Secret
+
+1. In the left sidebar, click on "Certificates & secrets"
+2. Click "New client secret"
+3. Add a description (e.g., "Documenso SSO Secret")
+4. Choose an expiration period (recommended 12-24 months)
+5. Click "Add"
+
+Make sure you copy the "Secret value", not the "Secret ID", you won't be able to access it again after you leave the page.
+
+
+
+## Getting Your OpenID Configuration URL
+
+1. In the Azure portal, go to "Microsoft Entra ID"
+2. Click on "Overview" in the left sidebar
+3. Click the "Endpoints" in the horizontal tab
+4. Copy the "OpenID Connect metadata document" value
+
+## Configure Documenso SSO Portal
+
+Now you have all the information needed to configure your Documenso SSO portal:
+
+- **Issuer URL**: The "OpenID Connect metadata document" value from the previous step
+- **Client ID**: The Application (client) ID from your app registration
+- **Client Secret**: The secret value you copied during creation
diff --git a/apps/documentation/public/organisations/organisations-sso-settings.webp b/apps/documentation/public/organisations/organisations-sso-settings.webp
new file mode 100644
index 000000000..b6d46f77e
Binary files /dev/null and b/apps/documentation/public/organisations/organisations-sso-settings.webp differ
diff --git a/apps/remix/app/routes/_authenticated+/o.$orgUrl.settings._layout.tsx b/apps/remix/app/routes/_authenticated+/o.$orgUrl.settings._layout.tsx
index 87326a5fa..1ef247147 100644
--- a/apps/remix/app/routes/_authenticated+/o.$orgUrl.settings._layout.tsx
+++ b/apps/remix/app/routes/_authenticated+/o.$orgUrl.settings._layout.tsx
@@ -6,6 +6,7 @@ import {
GroupIcon,
MailboxIcon,
Settings2Icon,
+ ShieldCheckIcon,
Users2Icon,
} from 'lucide-react';
import { FaUsers } from 'react-icons/fa6';
@@ -77,6 +78,11 @@ export default function SettingsLayout() {
label: t`Groups`,
icon: GroupIcon,
},
+ {
+ path: `/o/${organisation.url}/settings/sso`,
+ label: t`SSO`,
+ icon: ShieldCheckIcon,
+ },
{
path: `/o/${organisation.url}/settings/billing`,
label: t`Billing`,
@@ -94,6 +100,13 @@ export default function SettingsLayout() {
return false;
}
+ if (
+ (!isBillingEnabled || !organisation.organisationClaim.flags.authenticationPortal) &&
+ route.path.includes('/sso')
+ ) {
+ return false;
+ }
+
return true;
});
diff --git a/apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.sso.tsx b/apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.sso.tsx
new file mode 100644
index 000000000..db6b7c38d
--- /dev/null
+++ b/apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.sso.tsx
@@ -0,0 +1,432 @@
+import { zodResolver } from '@hookform/resolvers/zod';
+import { msg } from '@lingui/core/macro';
+import { Trans, useLingui } from '@lingui/react/macro';
+import { OrganisationMemberRole } from '@prisma/client';
+import { useForm } from 'react-hook-form';
+import { z } from 'zod';
+
+import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
+import { ORGANISATION_MEMBER_ROLE_HIERARCHY } from '@documenso/lib/constants/organisations';
+import { ORGANISATION_MEMBER_ROLE_MAP } from '@documenso/lib/constants/organisations-translations';
+import {
+ formatOrganisationCallbackUrl,
+ formatOrganisationLoginUrl,
+} from '@documenso/lib/utils/organisation-authentication-portal';
+import { trpc } from '@documenso/trpc/react';
+import { domainRegex } from '@documenso/trpc/server/enterprise-router/create-organisation-email-domain.types';
+import type { TGetOrganisationAuthenticationPortalResponse } from '@documenso/trpc/server/enterprise-router/get-organisation-authentication-portal.types';
+import { ZUpdateOrganisationAuthenticationPortalRequestSchema } from '@documenso/trpc/server/enterprise-router/update-organisation-authentication-portal.types';
+import { CopyTextButton } from '@documenso/ui/components/common/copy-text-button';
+import { Alert, AlertDescription } from '@documenso/ui/primitives/alert';
+import { Button } from '@documenso/ui/primitives/button';
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from '@documenso/ui/primitives/form/form';
+import { Input } from '@documenso/ui/primitives/input';
+import { Label } from '@documenso/ui/primitives/label';
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from '@documenso/ui/primitives/select';
+import { SpinnerBox } from '@documenso/ui/primitives/spinner';
+import { Switch } from '@documenso/ui/primitives/switch';
+import { Textarea } from '@documenso/ui/primitives/textarea';
+import { useToast } from '@documenso/ui/primitives/use-toast';
+
+import { SettingsHeader } from '~/components/general/settings-header';
+import { appMetaTags } from '~/utils/meta';
+
+const ZProviderFormSchema = ZUpdateOrganisationAuthenticationPortalRequestSchema.shape.data
+ .pick({
+ enabled: true,
+ wellKnownUrl: true,
+ clientId: true,
+ autoProvisionUsers: true,
+ defaultOrganisationRole: true,
+ })
+ .extend({
+ clientSecret: z.string().nullable(),
+ allowedDomains: z.string().refine(
+ (value) => {
+ const domains = value.split(' ').filter(Boolean);
+
+ return domains.every((domain) => domainRegex.test(domain));
+ },
+ {
+ message: msg`Invalid domains`.id,
+ },
+ ),
+ });
+
+type TProviderFormSchema = z.infer;
+
+export function meta() {
+ return appMetaTags('Organisation SSO Portal');
+}
+
+export default function OrganisationSettingSSOLoginPage() {
+ const { t } = useLingui();
+ const organisation = useCurrentOrganisation();
+
+ const { data: authenticationPortal, isLoading: isLoadingAuthenticationPortal } =
+ trpc.enterprise.organisation.authenticationPortal.get.useQuery({
+ organisationId: organisation.id,
+ });
+
+ if (isLoadingAuthenticationPortal || !authenticationPortal) {
+ return ;
+ }
+
+ return (
+
+
+
+
+ Data access:{' '}
+ Access all data associated with your account
+
+
+
+
+
+
+
+
+ This organisation will have administrative control over your account. You can
+ revoke this access later, but they will retain access to any data they've
+ already collected.
+
+
+
+