Discord - Admin SSO Provider
- Properly configure Strapi for SSO
- Create your Discord OAuth2 app by following the steps in the Discord Developer Console.
- Gather the required information to set as environment variables in your Strapi project:
- DISCORD_CLIENT_ID
- DISCORD_SECRET
Required configuration before setting up SSO
Server Configuration
The following server configurations are required when using SSO, for more information on available options please see the Server Configuration documentation.
url
: The public facing URL of your Strapi application. (e.g.https://api.example.com
)proxy.koa
: Enabling trusted reverse proxy support. (true
)
Admin Required Configuration Example
- JavaScript
- TypeScript
module.exports = ({ env }) => ({
// ...
url: env('PUBLIC_URL', 'https://api.example.com'),
proxy: {
koa: env.bool('TRUST_PROXY', true),
},
// ...
});
export default ({ env }) => ({
// ...
url: env('PUBLIC_URL', 'https://api.example.com'),
proxy: {
koa: env.bool('TRUST_PROXY', true),
},
// ...
});
There are also some optional configurations that you can set should it be necessary:
proxy.global
: If you are in a restricted network environment that requires a forward proxy (e.g Squid) for all outgoing requests. (e.g.http://username:password@yourProxy:3128
)
Admin Optional Configuration Example
- JavaScript
- TypeScript
module.exports = ({ env }) => ({
// ...
url: env('PUBLIC_URL', 'https://api.example.com'),
proxy: {
koa: env.bool('TRUST_PROXY', true),
global: env('GLOBAL_PROXY'),
},
// ...
});
export default ({ env }) => ({
// ...
url: env('PUBLIC_URL', 'https://api.example.com'),
proxy: {
koa: env.bool('TRUST_PROXY', true),
global: env('GLOBAL_PROXY'),
},
// ...
});
Admin Configuration
There are some optional configurations that you can set should it be necessary, for more information on available options please see the Admin Configuration documentation.
url
: The public facing URL of your Strapi administration panel. (e.g.https://admin.example.com
)auth.domain
: Setting a custom domain for cookie storage. (e.g..example.com
)
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.
Deploying the admin and backend on entirely different unrelated domains is not possible at this time when using SSO due to restrictions in cross-domain cookies.
Admin Optional Configuration Example
- JavaScript
- TypeScript
module.exports = ({ env }) => ({
// ...
url: env('PUBLIC_ADMIN_URL', 'https://admin.example.com'),
auth: {
domain: env("ADMIN_SSO_DOMAIN", ".example.com"),
providers: [
// ...
],
},
// ...
});
export default ({ env }) => ({
// ...
url: env('PUBLIC_ADMIN_URL', 'https://admin.example.com'),
auth: {
domain: env("ADMIN_SSO_DOMAIN", ".example.com"),
providers: [
// ...
],
},
// ...
});
Middlewares Configuration
The following middleware configurations are required when using SSO, for more information on available options please see the Middlewares Configuration documentation.
contentSecurityPolicy
: Allows you to configure the Content Security Policy (CSP) for your Strapi application. This is used to prevent cross-site scripting attacks by allowing you to control what resources can be loaded by your application.
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 or you use a file uploaded directly on your Strapi application.
Middlewares Configuration Example
- JavaScript
- TypeScript
module.exports = [
// ...
{
name: 'strapi::security',
config: {
contentSecurityPolicy: {
useDefaults: true,
directives: {
'connect-src': ["'self'", 'https:'],
'img-src': [
"'self'",
'data:',
'blob:',
'market-assets.strapi.io',
'cdn2.iconfinder.com', // Base URL of the provider's logo without the protocol
],
'media-src': [
"'self'",
'data:',
'blob:',
'market-assets.strapi.io',
'cdn2.iconfinder.com', // Base URL of the provider's logo without the protocol
],
upgradeInsecureRequests: null,
},
},
},
},
// ...
]
export default [
// ...
{
name: 'strapi::security',
config: {
contentSecurityPolicy: {
useDefaults: true,
directives: {
'connect-src': ["'self'", 'https:'],
'img-src': [
"'self'",
'data:',
'blob:',
'market-assets.strapi.io',
'cdn2.iconfinder.com', // Base URL of the provider's logo without the protocol
],
'media-src': [
"'self'",
'data:',
'blob:',
'market-assets.strapi.io',
'cdn2.iconfinder.com', // Base URL of the provider's logo without the protocol
],
upgradeInsecureRequests: null,
},
},
},
},
// ...
]
Provider Specific Notes
Scopes
The Discord OAuth2 provider requires the following scopes, however additional scopes can be added as needed depending on your use case and the data you need returned:
Profile Data
Data returned from the provider is dependent on how your Discord OAuth2 application is configured. The example below assumes that the Discord OAuth2 application is configured to return the user's email and username. Fields returned by the provider can change based on the scopes requested and the user's Discord account settings.
If you aren't sure what data is being returned by the provider, you can log the profile
object in the createStrategy
function to see what data is available as seen in the following example.
Configuration Example with Logging
(accessToken, refreshToken, profile, done) => {
// See what is returned by the provider
console.log(profile);
done(null, {
email: profile.email,
username: `${profile.username}`,
});
}
Redirect URL/URI
The redirect URL/URI will be dependent on your provider configuration however in most cases should combine your application's public URL and the provider's callback URL. The example below shows how to combine the public URL with the provider's callback URL.
callbackURL:
env('PUBLIC_URL', "https://api.example.com") +
strapi.admin.services.passport.getStrategyCallbackURL("discord"),
In this example the redirect URL/URI used by the provider will be https://api.example.com/admin/connect/discord
.
This is broken down as follows:
https://api.example.com
is the public URL of your Strapi application/admin/connect
is the general path for SSO callbacks in Strapi/discord
is the specific provider UID for Discord
Strapi Configuration
Using: passport-discord
Install the Provider Package
- yarn
- npm
yarn add passport-discord
npm install --save passport-discord
Adding the Provider to Strapi
- JavaScript
- TypeScript
const DiscordStrategy = require("passport-discord");
module.exports = ({ env }) => ({
auth: {
// ...
providers: [
{
uid: "discord",
displayName: "Discord",
icon: "https://cdn0.iconfinder.com/data/icons/free-social-media-set/24/discord-512.png",
createStrategy: (strapi) =>
new DiscordStrategy(
{
clientID: env("DISCORD_CLIENT_ID"),
clientSecret: env("DISCORD_SECRET"),
callbackURL:
env('PUBLIC_URL') +
strapi.admin.services.passport.getStrategyCallbackURL(
"discord"
),
scope: ["identify", "email"],
},
(accessToken, refreshToken, profile, done) => {
done(null, {
email: profile.email,
username: `${profile.username}`,
});
}
),
},
],
},
});
import { Strategy as DiscordStrategy } from "passport-discord";
export default ({ env }) => ({
auth: {
// ...
providers: [
{
uid: "discord",
displayName: "Discord",
icon: "https://cdn0.iconfinder.com/data/icons/free-social-media-set/24/discord-512.png",
createStrategy: (strapi) =>
new DiscordStrategy(
{
clientID: env("DISCORD_CLIENT_ID"),
clientSecret: env("DISCORD_SECRET"),
callbackURL:
env('PUBLIC_URL') +
strapi.admin.services.passport.getStrategyCallbackURL(
"discord"
),
scope: ["identify", "email"],
},
(accessToken, refreshToken, profile, done) => {
done(null, {
email: profile.email,
username: `${profile.username}`,
});
}
),
},
],
},
});