Dosu LogoDosu Logo
Ask
Join our Discord
StrapiPublic
Strapi
DocumentsStrapi
configure-sso
configure-sso
Type
External
Status
Published
Created
Mar 5, 2026
Updated
Jun 29, 2026
Updated by
Dosu Bot
Source
View

import NotV5 from '/docs/snippets/_not-updated-to-v5.md'

How to configure SSO providers#

Configure SSO providers in the Strapi admin panel by registering OAuth/OIDC applications, adding credentials to auth.providers in /config/admin, and setting up callback URLs to enable additional sign-in methods.

Single Sign-On (SSO) on Strapi allows you to configure additional sign-in and sign-up methods for the Strapi admin panel.

  • To configure SSO on your application, you will need an plan or the add-on.
  • Make sure Strapi is part of the applications you can access with your provider. For example, with Microsoft (Azure) Active Directory, you must first ask someone with the right permissions to add Strapi to the list of allowed applications. Please refer to your provider(s) documentation to learn more about that.
  • It is currently not possible to associate a unique SSO provider to an email address used for a Strapi account, meaning that the access to a Strapi account cannot be restricted to only one SSO provider. For more information and workarounds to solve this issue, .
  • Deploying the admin and backend on entirely different unrelated domains is not possible at this time when using SSO.

Accessing the configuration#

The SSO configuration lives in the /config/admin file.

The providers' configuration should be written in the auth.providers path of the admin panel as an array of provider configurations:


module.exports = ({ env }) => ({
  // ...
  auth: {
    providers: [], // The providers' configuration lives there
  },
});

export default ({ env }) => ({
  // ...
  auth: {
    providers: [], // The providers' configuration lives there
  },
});

Setting up provider configuration#

Parts of the documentation below assume that some steps have been done previously both in Strapi and in your identity provider. If these steps are skipped, the login button might appear on the Strapi login page but the flow will fail with a redirect or "invalid client" error. Make sure to follow all the steps of the checklist before moving onto the rest of the documentation.

[Enable SSO in Strapi](/cms/features/sso#admin-panel-settings)
Go to Global settings > Single Sign-On in the admin panel and set up the feature (e.g. toggle auto-registration and choose the default role). Register Strapi in your identity provider
In the provider's dashboard (e.g. Azure AD, Okta, Google, GitHub), create a new OAuth/OIDC application for Strapi. Copy the client ID and client secret generated by the provider. [Add the Strapi callback URL to the provider](#the-createstrategy-factory)
Set the redirect/callback URL in the provider configuration to the value generated by {"strapi.admin.services.passport.getStrategyCallbackURL('')"} (e.g. /admin/connect/google if the UID is google). The provider must accept this URL or the login will be blocked. [Provide credentials to Strapi](#configuring-the-provider)
Add the client ID and client secret as environment variables (e.g. GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET) so they can be read in {"/config/admin.js|ts"}. [Configure the provider in code](#configuring-the-provider)
Import the provider's Passport strategy and add it to auth.providers. Rebuild and restart Strapi
Run yarn build && yarn develop or npm run build && npm run develop so the new provider appears on the login page. If the admin panel is hosted separately, also ensure the url setting matches the deployed admin URL (see [Host, port and path](/cms/admin-panel-customization/host-port-path).

A provider's configuration is a JavaScript object built with the following properties:

NameRequiredTypeDescription
uidYesStringThe UID of the strategy. It must match the strategy's name.
displayNameYesStringThe name that will be used on the login page to reference the provider.
createStrategyYesFunctionA factory that will build and return a new passport strategy for your provider. Takes the strapi instance as parameter.
iconNoStringAn image URL. If specified, it will replace the displayName on the login page.
Info

The uid property is the unique identifier of each strategy and is generally found in the strategy's package. If you are not sure of what it refers to, please contact the maintainer of the strategy.

Displaying providers logos#

By default, Strapi security policy does not allow loading images from external URLs, so provider logos will not show up on the login screen of the admin panel unless a security exception is added through middlewares configuration, as in the following example:

module.exports = [
  // ...
  {
    name: 'strapi::security',
    config: {
      contentSecurityPolicy: {
        useDefaults: true,
        directives: {
          'connect-src': ["'self'", 'https:'],
          'img-src': [
            "'self'",
            'data:',
            'blob:',
            'dl.airtable.com',
            'www.okta.com', // Base URL of the provider's logo
          ],
          'media-src': [
            "'self'",
            'data:',
            'blob:',
            'dl.airtable.com',
            'www.okta.com', // Base URL of the provider's logo
          ],
          upgradeInsecureRequests: null,
        },
      },
    },
  },
  // ...
]
export default [
  // ...
  {
    name: 'strapi::security',
    config: {
      contentSecurityPolicy: {
        useDefaults: true,
        directives: {
          'connect-src': ["'self'", 'https:'],
          'img-src': [
            "'self'",
            'data:',
            'blob:',
            'dl.airtable.com',
            'www.okta.com', // Base URL of the provider's logo
          ],
          'media-src': [
            "'self'",
            'data:',
            'blob:',
            'dl.airtable.com',
            'www.okta.com', // Base URL of the provider's logo
          ],
          upgradeInsecureRequests: null,
        },
      },
    },
  },
  // ...
]

Setting common domain for cookies#

When deploying the admin panel to a different location or on a different subdomain, an additional configuration is required to set the common domain for the cookies. This is required to ensure the cookies are shared across the domains.

module.exports = ({ env }) => ({
  auth: {
    domain: env("ADMIN_SSO_DOMAIN", ".test.example.com"),
    providers: [
      // ...
    ],
  },
  url: env("ADMIN_URL", "http://admin.test.example.com"),
  // ...
});
export default ({ env }) => ({
  auth: {
    domain: env("ADMIN_SSO_DOMAIN", ".test.example.com"),
    providers: [
      // ...
    ],
  },
  url: env("ADMIN_URL", "http://admin.test.example.com"),
  // ...
});

The createStrategy Factory#

A passport strategy is usually built by instantiating it using 2 parameters: the configuration object, and the verify function.

Configuration object#

The configuration object depends on the strategy needs, but often asks for a callback URL to be redirected to once the connection has been made on the provider side.

A specific callback URL can be generated for your provider using the getStrategyCallbackURL method. This URL also needs to be written on the provider side in order to allow redirection from it.

The format of the callback URL is the following: /admin/connect/<provider_uid>.

Tip

strapi.admin.services.passport.getStrategyCallbackURL is a Strapi helper you can use to get a callback URL for a specific provider. It takes a provider name as a parameter and returns a URL.

If needed, this is also where you will put your client ID and secret key for your OAuth2 application.

Verify function#

The verify function is used here as a middleware allowing the user to transform and make extra processing on the data returned from the provider API.

This function always takes a done method as last parameter which is used to transfer needed data to the Strapi layer of SSO.

Its signature is the following: void done(error: any, data: object); and it follows the following rules:

  • If error is not set to null, then the data sent is ignored, and the controller will throw an error.
  • If the SSO's auto-registration feature is disabled, then the data object only need to be composed of an email property.
  • If the SSO's auto-registration feature is enabled, then you will need to define (in addition to the email) either a username property or both firstname and lastname within the data object.

Adding a provider#

Adding a new provider means adding a new way for your administrators to log-in.

Strapi uses , which enables a large selection of providers. Any valid passport strategy that doesn't need additional custom data should therefore work with Strapi.

Strategies such as don't work out of the box since they require extra data to be sent from the admin panel.
If you want to add an LDAP provider to your application, you will need to write a .
You can also use services such as Okta and Auth0 as bridge services.

Configuring the provider#

To configure a provider, follow the procedure below:

  1. Make sure to import your strategy in your admin configuration file, either from an installed package or a local file.
  2. Add a new item to the auth.providers array in your admin panel configuration that matches the format given above.
  3. Rebuild and restart your application with yarn build && yarn develop or npm run build && npm run develop. The provider should appear on your admin login page.

Provider configuration examples#

The following examples show how SSO is configured for the most common providers:

Performing advanced customization#

Admin panel URL#

If the administration panel lives on a host/port different from the Strapi server, the admin panel URL needs to be updated: Update the url key in the /config/admin file.

Custom logic#

In some scenarios, you will want to write additional logic for your connection workflow such as:

  • restricting connection and registration for a specific domain
  • triggering actions on connection attempt
  • adding analytics

The easiest way to do so is to plug into the verify function of your strategy and write some code.

For example, if you want to allow only people with an official strapi.io email address, you can instantiate your strategy like follows:


const strategyInstance = new Strategy(configuration, ({ email, username }, done) => {
  // If the email ends with @strapi.io
  if (email.endsWith('@strapi.io')) {
    // then we continue with the data given by the provider
    return done(null, { email, username });
  }

  // Otherwise, we continue by sending an error to the done function
  done(new Error('Forbidden email address'));
});

const strategyInstance = new Strategy(configuration, ({ email, username }, done) => {
  // If the email ends with @strapi.io
  if (email.endsWith('@strapi.io')) {
    // then we continue with the data given by the provider
    return done(null, { email, username });
  }

  // Otherwise, we continue by sending an error to the done function
  done(new Error('Forbidden email address'));
});

Authentication events#

The SSO feature adds a new authentication event: onSSOAutoRegistration.

This event is triggered whenever a user is created using the auto-register feature added by SSO.
It contains the created user (event.user), and the provider used to make the registration (event.provider).


module.exports = () => ({
    auth: {
      // ...
      events: {
        onConnectionSuccess(e) {},
        onConnectionError(e) {},
        // ...
        onSSOAutoRegistration(e) {
          const { user, provider } = e;

          console.log(
            `A new user (${user.id}) has been automatically registered using ${provider}`
          );
        },
      },
    },
});

export default () => ({
    auth: {
      // ...
      events: {
        onConnectionSuccess(e) {},
        onConnectionError(e) {},
        // ...
        onSSOAutoRegistration(e) {
          const { user, provider } = e;

          console.log(
            `A new user (${user.id}) has been automatically registered using ${provider}`
          );
        },
      },
    },
});
Documents
Admin Modal Management
Admin Panel Locale Management
admin-panel
faq
setting-up-admin-panel
Admin User Management
Blocks Editor
Clone Sanitizer Field Permissions
CodeMirror Integration
Content API Input Validation
breaking-changes
populate-select
rest
status
Content Manager Homepage
admin-panel
faq
quick-start
rest
Content Manager i18n Preview
Content Manager Layout Synchronization
Content Manager Preview
Content Manager URL & Filter State
Content Type Builder
admin-panel-api
controllers
create-components-for-plugins
populate-creator-fields
quick-start
store-and-access-data
Custom Field Prop Spreading
Date Field Serialization
Discard-Drafts Migration
Document ID Migration
breaking-changes
Draft & Publish Relation Synchronization
do-not-update-repeatable-components-with-document-service-api
document-service
lifecycle-hooks-document-service
populate
publishedat-always-set-when-dandp-disabled
relations
rest
DynamicZone Stability
components-and-dynamic-zones-do-not-return-id
components-dynamic-zones
no-shared-population-strategy-components-dynamic-zones
Edit View Layout Configuration
admin-panel
admin-panel-api
edit-view-layout-and-list-view-layout-rewritten
EnumerationInput Component
GraphQL Context Propagation
breaking-changes
document-service
draft-and-publish
graphql
i18n Locale Validation
breaking-changes
locale
i18n Locale-Scoped Operations
database-columns
document
document-service
draft-and-publish
i18n-content-manager-locale
locale
locale
locale
no-locale-all
parameters
populate
relations
rest
Monorepo Module Resolution
create-a-plugin
Nested Relation Modal Navigation
Polymorphic Relations
faq
Relation Field Validation
Relation Modal State Management
relations
Sharp Image Processing
media-library
Strapi Package Version Management
Strapi Project Scaffolding
create-a-plugin
How can I create my first Strapi project on a brand new MacBook with no prior coding experience, including all necessary terminal commands and setup steps?
quick-start
Strapi v5 Plugin API
admin-panel-rbac-store-updated
admin-permissions-for-plugins
breaking-changes
controllers
extension
faq
get-where-removed
helper-plugin
helper-plugin-deprecated
inject-content-manager-component
introduction
introduction-and-faq
model-config-path-uses-uid
pass-data-from-server-to-admin
plugins-migration
rbac
redux-content-manager-app-state
step-by-step
strapi-imports
Users-Permissions Plugin i18n
users-permissions
Vite Build Configuration
admin-panel
introduction-and-faq
vite
webpack-aliases-removed
Zod Schema Validation
document-service
Content Manager Mobile and Tablet UI Improvements
FilesManager API Reference
Focal Point Picker in Strapi Media Library
Nested Route File Structure for Strapi Plugins
Plugin-Specific Handling in the Strapi JavaScript Client
Strapi SDK Initialization and Architecture Guide
access-cast-environment-variables
access-configuration-values
adding-support-to-existing-project
admin-panel-customization
advanced-policies
advanced-queries
amazon-s3
api
api-tokens
attributes-and-content-types-names-reserved
audit-logs
auth-zero
authentication
aws-cognito
backend-customization
bulk-operations
bundlers
cas
cli
cli
client
cloudinary
community
configurations
configure-sso
content-api
content-history
content-manager
content-manager-apis
content-type-builder
core-service-methods-use-document-service
cron
crud
custom-fields
customization
data-management
database
database-identifiers-shortened
database-migrations
database-transactions
default-index-removed
default-input-validation
deployment
design-system
developing-plugins
development
discord
discord
docker
documentation
documents-and-entries
email
email-custom-providers
email-nodemailer
entity-service
entity-service-deprecated
environment
error-handling
examples
facebook
favicon
features
fetch
fields
filter
filtering
filters
filters
from-entity-service-to-document-service
functions
github
github
google
google
graphql
graphql-api-updated
guides
homepage
host-port-path
instagram
installation
installing-plugins-via-marketplace
interactive-query-builder
internationalization
intro
intro
is-supported-image-removed
keycloak
keycloak
koa-body-v6
license-only
linkedin
local-upload
locales-translations
logos
mailgun-provider-variables
media-library-providers
microsoft
middlewares
middlewares
middlewares
middlewares
models
mysql5-unsupported
new-provider-guide
new-response-format
no-find-page-in-document-service
no-upload-at-entry-creation
okta
only-better-sqlite3-for-sqlite
only-mysql2-package-for-mysql
openapi
order-pagination
order-pagination
patreon
plugin-sdk
plugin-structure
plugins
plugins-extension
policies
policies
populate
populating
preview
project-structure
publication-state-removed
query-engine
rbac
react-router-dom-6
reddit
register-allowed-fields
releases
remove-webhook-populate-relations
removed-support-for-some-env-options
requests-responses
review-workflows
routes
routes
sentry
server
server-api
server-default-log-level
server-proxy
services
services-and-controllers
setup-deployment
single-operations
sort-by-id
sort-pagination
sort-pagination
sso
status
strapi-container
strapi-utils-refactored
strict-requirements-config-files
templates
templates
testing
theme-extension
twitch
twitter
typescript
typescript
understanding-populate
upgrade-to-apollov4
upgrade-tool
upgrades
upload
usage-information
use-document-id
users-and-permissions-providers
vk
webhooks
wysiwyg-editor
yarn-not-default