Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The State of SSO (google,facebook,twitter,microsoft,github,discourse) #12430

Closed
3 tasks done
autr opened this issue Mar 28, 2022 · 12 comments
Closed
3 tasks done

The State of SSO (google,facebook,twitter,microsoft,github,discourse) #12430

autr opened this issue Mar 28, 2022 · 12 comments

Comments

@autr
Copy link

autr commented Mar 28, 2022

Preflight Checklist

Describe the Bug

Hello, I thought I'd write up my findings on SSO (single-sign-on) for Directus on Directus latest 9.7.1 (28/03/22), which is something I'm keen to use since it simplifies user registration.

Here is an overview followed by the full .env configuration:

Google openid

✅ Works w/ setup via cloud console.

Facebook openid

✅ Works w/ setup via myapps.

Discord oauth2

✅ Works w/ setup via apps.

Microsoft openid

❌ Callback code seems to be given as an URL parameter instead of in POST:

[XX:XX:XX] ⚠️Service returned unexpected response
[XX:XX:XX] ✨ request completed GET 302 /auth/login/microsoft/callback?code=XXXXXX
  • For setup, everything is in hard-to-find azure active directory > app registrations.
  • If public-facing, it's also important to set signInAudience=AzureADandPersonalMicrosoftAccount inside Manifest.
  • Tenant ID can be found inside default directory > settings.
  • Client Secret is also only valid w/ Azure for maximum 24 months, so maybe not worth the hassle.

Twitter oauth2

❌ Twitter landing page is: Whoa there! There is no request token for this page.

  • Initial authorization url seems to be using OAUTH1.0 (unrecognised by Directus), whereas access url is OAUTH2.0
  • General Twitter OAuth flow is unusually formatted.

Github oauth2

EDIT: works if Github user email address is public

Directus gives error: [XX:XX:XX] ⚠️ [OAuth2] Failed to find user identifier for provider "github". Perhaps from missing email?

DiscourseHub oauth2

✨ Not tested yet but this plugin seems a viable method.

Misc

If there is already a Directus DB user with the same email address as an attempted SSO login, the login page will reload without an error message. Deleting the Directus DB user then allows the SSO login, but it would be good to a) combine SSO with DB user, or b) give an err ("user already exists").

Environment (.env)

# AUTH
# ====

# Resource: https://learndirectus.com/how-to-add-sso-to-directus
# Callback format: [BASE_URL]/auth/login/[PROVIDER_NAME]/callback

AUTH_PROVIDERS="google,facebook,twitter,microsoft,github,discourse"

# MICROSOFT ❌
# ============
# Where: https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps
# Callback: https://vidicon.org/auth/login/microsoft/callback

AUTH_MICROSOFT_CLIENT_ID="XXXX"
# Replace with the App ID (Client ID)
AUTH_MICROSOFT_CLIENT_SECRET="XXXX"
# Replace with the App Secret (Client Secret)
AUTH_MICROSOFT_ISSUER_URL="https://login.microsoftonline.com/XXXX/v2.0/.well-known/openid-configuration"
# Replace XXXX with your tenant ID or "common"
AUTH_MICROSOFT_SCOPE="openid profile email"
AUTH_MICROSOFT_IDENTIFIER_KEY="sub"
AUTH_MICROSOFT_ICON="microsoft"
AUTH_MICROSOFT_ALLOW_PUBLIC_REGISTRATION="true" 
AUTH_MICROSOFT_DRIVER="openid"
AUTH_MICROSOFT_DEFAULT_ROLE_ID="XXXX"

# DISCOURSE ✨
# ============

# UNTESTED
# INSTALL THIS: https://github.com/Parkour-Vienna/distrust
# SET "redirectURIs" Callback: https://vidicon.org/auth/login/discourse/callback

AUTH_DISCOURSE_CLIENT_ID="XXXX" # SET THIS
AUTH_DISCOURSE_CLIENT_SECRET="XXXX" # SET THIS

AUTH_DISCOURSE_AUTHORIZE_URL="https://example.com/oauth2/auth"
AUTH_DISCOURSE_ACCESS_URL="https://example.com/oauth2/token"
AUTH_DISCOURSE_PROFILE_URL="https://example.com/user"

AUTH_DISCOURSE_DRIVER="oauth2"
AUTH_DISCOURSE_ALLOW_PUBLIC_REGISTRATION="true" 
AUTH_DISCOURSE_ICON="scanlines"
AUTH_DISCOURSE_DEFAULT_ROLE_ID="XXXX"

# TWITTER ❌
# =========

# Where: https://developer.twitter.com/en/apps
# Callback: https://vidicon.org/auth/login/twitter/callback

AUTH_TWITTER_CLIENT_ID="XXXX" # SET THIS
AUTH_TWITTER_CLIENT_SECRET="XXXX" # SET THIS

AUTH_TWITTER_AUTHORIZE_URL="https://api.twitter.com/oauth/authorize"
AUTH_TWITTER_ACCESS_URL="https://api.twitter.com/oauth2/token"
AUTH_TWITTER_PROFILE_URL="https://twitter.com/settings"

AUTH_TWITTER_DRIVER="oauth2"
AUTH_TWITTER_ALLOW_PUBLIC_REGISTRATION="true" 
AUTH_TWITTER_ICON="twitter"
AUTH_TWITTER_DEFAULT_ROLE_ID="XXXX"

# DISCORD ✅
# ==========

# WHERE: https://discord.com/developers/applications
# CALLBACK: https://dev.vidicon.org/auth/login/discord/callback
# Discord: add callback to "CUSTOM URL"
# CLIENT_ID must have "string:" prepended to it (integer)

AUTH_DISCORD_CLIENT_ID="string:XXXX" # do not remove "string:"
AUTH_DISCORD_CLIENT_SECRET="XXXX"

AUTH_DISCORD_AUTHORIZE_URL="https://discord.com/api/oauth2/authorize"
AUTH_DISCORD_ACCESS_URL="https://discord.com/api/oauth2/token"
AUTH_DISCORD_PROFILE_URL="https://discord.com/api/users/@me"

AUTH_DISCORD_SCOPE="identify"
AUTH_DISCORD_IDENTIFIER_KEY="username"
AUTH_DISCORD_DRIVER="oauth2"
AUTH_DISCORD_ALLOW_PUBLIC_REGISTRATION="true" 
AUTH_DISCORD_ICON="discord"
AUTH_DISCORD_DEFAULT_ROLE_ID="XXXX"

# GOOGLE ✅
# =========
# Where: https://console.cloud.google.com/apis/credentials
# Callback: https://vidicon.org/auth/login/Google/callback

AUTH_GOOGLE_CLIENT_ID="XXXX" 
# Replace XXXX with the App ID (Client ID)
AUTH_GOOGLE_CLIENT_SECRET="XXXX" 
# Replace XXXX with the App Secret (Client Secret)
AUTH_GOOGLE_ISSUER_URL="https://accounts.google.com"
AUTH_GOOGLE_IDENTIFIER_KEY="email"
AUTH_GOOGLE_ICON="google"
AUTH_GOOGLE_ALLOW_PUBLIC_REGISTRATION="true"
AUTH_GOOGLE_DRIVER="openid"
AUTH_GOOGLE_DEFAULT_ROLE_ID="XXXX"

# FACEBOOK ✅
# ===========
# Callback: https://vidicon.org/auth/login/facebook/callback

AUTH_FACEBOOK_DRIVER="openid"
AUTH_FACEBOOK_CLIENT_ID="XXXX" 
# Replace XXXX with the App ID
AUTH_FACEBOOK_CLIENT_SECRET="XXXX" 
# Replace XXXX with the App Secret

AUTH_FACEBOOK_SCOPE="email"
AUTH_FACEBOOK_AUTHORIZE_URL="https://www.facebook.com/dialog/oauth"
AUTH_FACEBOOK_ACCESS_URL="https://graph.facebook.com/oauth/access_token"
AUTH_FACEBOOK_PROFILE_URL="https://graph.facebook.com/me?fields=email"

AUTH_FACEBOOK_ICON="facebook"
AUTH_FACEBOOK_ALLOW_PUBLIC_REGISTRATION="true"
AUTH_FACEBOOK_DRIVER="oauth2"
AUTH_FACEBOOK_DEFAULT_ROLE_ID="XXXX"

# GITHUB ✅
# =========
# Where: https://github.com/settings/apps
# Callback: https://vidicon.org/auth/login/github/callback
# IMPORTANT: Github user email must be public to work

AUTH_GITHUB_CLIENT_ID="XXXX"
AUTH_GITHUB_CLIENT_SECRET="XXXX"

AUTH_GITHUB_SCOPE="email"
AUTH_GITHUB_AUTHORIZE_URL="https://github.com/login/oauth/authorize"
AUTH_GITHUB_ACCESS_URL="https://github.com/login/oauth/access_token"
AUTH_GITHUB_PROFILE_URL="https://api.github.com/user"

AUTH_GITHUB_ICON="github"
AUTH_GITHUB_ALLOW_PUBLIC_REGISTRATION="true" 
AUTH_GITHUB_DRIVER="oauth2"
AUTH_GITHUB_DEFAULT_ROLE_ID="XXXX"

To Reproduce

Recreate these .env settings on a Directus instance.

Errors Shown

No response

What version of Directus are you using?

9.7.1

What version of Node.js are you using?

v14.15.4

What database are you using?

pg@^8.7.3

What browser are you using?

Safari / Ungoogled Chromium

What operating system are you using?

macOS

How are you deploying Directus?

Ubuntu Droplet (Digital Ocean)

@autr
Copy link
Author

autr commented Mar 28, 2022

PS. this is for a community video art festival

@aidenfoxx
Copy link
Contributor

aidenfoxx commented Mar 28, 2022

Great initiative on doing the research!

OAuth/OpenID is a nightmare since it can vary wildly between provider implementations, but we've tried to build the system as generically as possible to support as much as possible. Often provider quirks can be worked around by providing the right config, either through the AUTH_<PROVIDER>_PARAMS_ or AUTH_<PROVIDER>_CLIENT_ to customize the authorization parameters and client configurations respectively.

Implementations I've personally verified are: facebook, github, discord, okta, auth0, keycloak, twitch and google.

Github requires the authenticating user to have their email as public in their settings in order for it to be picked up when authenticating. There might be a way around it by providing the proper grants or scope, but I never found it. That should probably be documented somewhere! 😅

Microsoft has been confirmed working by a few users in discussions. I just pulled this example (you can exclude AUTH_MICROSOFT_EMAIL_KEY) #11316 (reply in thread)

Twitter is a lost cause, however. Their OAuth 2 implementation is so bespoke that it requires a targeted implementation (unless it's changed during the beta).

@aidenfoxx
Copy link
Contributor

Actually it looks like Twitter has changed things. You can try AUTH_TWITTER_AUTHORIZE_URL="https://twitter.com/i/oauth2/authorize" and it may work. 🤞

@autr
Copy link
Author

autr commented Mar 28, 2022

Thanks @aidenfoxx !

Public Github email fixed it so I’ve updated the original post.

AUTH_TWITTER_AUTHORIZE_URL="https://twitter.com/i/oauth2/authorize" no such luck but perhaps more AUTH_TWITTER_PARAMS_ experimentation will do it.

Recreating #11316 keys for MICROSOFT still same error, so perhaps some config needed with Azure settings.

Would you be able to share your Discord, Twitch configs? It would be great to document more examples. 💪

@autr
Copy link
Author

autr commented Mar 28, 2022

Here's a hack for Github SSO button:

.sso-link:nth-child(5) .sso-title {
    display: flex!important;
    flex-direction: column!important;
    justify-content: center!important;
    align-items: flex-start!important;
}
.sso-link:nth-child(5) .sso-title:after {
    content:  "Public email address only";
    font-style:  italic!important;
    font-size: 14px!important;
}

Screenshot 2022-03-28 at 20 08 29

@aidenfoxx
Copy link
Contributor

aidenfoxx commented Mar 28, 2022

Sure thing!

Twitch:

AUTH_TWITCH_DRIVER="openid"
AUTH_TWITCH_ISSUER_URL="https://id.twitch.tv/oauth2/.well-known/openid-configuration"
AUTH_TWITCH_CLIENT_ID="123"
AUTH_TWITCH_CLIENT_SECRET="123"
AUTH_TWITCH_SCOPE="openid user:read:email"
AUTH_TWITCH_PARAMS__CLAIMS="string:{"id_token":{"email":null,"email_verified":null}}"

Discord:

AUTH_DISCORD_DRIVER="oauth2"
AUTH_DISCORD_AUTHORIZE_URL="https://discord.com/api/oauth2/authorize"
AUTH_DISCORD_ACCESS_URL="https://discord.com/api/oauth2/token"
AUTH_DISCORD_PROFILE_URL="https://discord.com/api/users/@me"
AUTH_DISCORD_CLIENT_ID="string:123"
AUTH_DISCORD_CLIENT_SECRET="123"

If I remember correctly, Discord uses numeric client ids, so you need to specify string: to keep things happy.

@autr
Copy link
Author

autr commented Mar 28, 2022

Awesome :)

I've discovered a funny bug in Directus for Discord. If the Client ID is an integer then it causes Directus to crash.AUTH_DISCORD_CLIENT_ID="123456" or AUTH_DISCORD_CLIENT_ID=123456: But if I set AUTH_DISCORD_CLIENT_ID with string chars I don't get the bug.

Just noticed the flag string in the variable.

@aidenfoxx
Copy link
Contributor

aidenfoxx commented Mar 28, 2022

Looking further into the docs, the correct Twitter profile URL would be AUTH_TWITTER_PROFILE_URL="https://api.twitter.com/2/users/me".

I think you also need additional scopes AUTH_TWITTER_SCOPE="email users.read", and they also don't provide email, so you need to use the username as the identifier AUTH_TWITTER_IDENTIFIER_KEY="username"

@autr
Copy link
Author

autr commented Mar 28, 2022

Yep, before moving back onto Twitter I think this is the same issue as with Discord, where as with Github the page refreshes without creating the new user - presumably because there is no email to use as a key?

Can you explain how this works? ie. How does the OAuth2 flow get some field to use as a unique key / email for Directus?

This is my config for Discord:

EDIT: Discord now work with this config:

# DISCORD ✅
# ==========

# WHERE: https://discord.com/developers/applications
# CALLBACK: https://dev.vidicon.org/auth/login/discord/callback
# Discord: add callback to "CUSTOM URL"
# CLIENT_ID must have "string:" prepended to it (integer)

AUTH_DISCORD_CLIENT_ID="string:XXXX"
AUTH_DISCORD_CLIENT_SECRET="XXXX"

AUTH_DISCORD_AUTHORIZE_URL="https://discord.com/api/oauth2/authorize"
AUTH_DISCORD_ACCESS_URL="https://discord.com/api/oauth2/token"
AUTH_DISCORD_PROFILE_URL="https://discord.com/api/users/@me"

AUTH_DISCORD_SCOPE="identify"
AUTH_DISCORD_IDENTIFIER_KEY="username"
AUTH_DISCORD_DRIVER="oauth2"
AUTH_DISCORD_ALLOW_PUBLIC_REGISTRATION="true" 
AUTH_DISCORD_ICON="discord"
AUTH_DISCORD_DEFAULT_ROLE_ID="XXXX"

Checking to see if this isn't the same for Github...

@aidenfoxx
Copy link
Contributor

aidenfoxx commented Mar 28, 2022

The IDENTIFIER_KEY and EMAIL_KEY specify the keys to extract user data when reading the profile response from the SSO provider. The IDENTIFIER_KEY specifies the profile key that links the SSO user to a Directus user (as defined in "External Identifier" in the admin). If no IDENTIFIER_KEY is specified in the config we fallback to EMAIL_KEY as the identifier.

So if Twitter returns the following profile for the authenticating user:

{
  "id": "2244994945",
  ...
  "username": "SomeUser"
}

Then authenticating with AUTH_TWITTER_IDENTIFIER_KEY="username", we'd expect a Directus user with the "Provider" value "Twitter" and the "External Identifier" value "SomeUser".

Unfortunately I see the issue with Twitter now. Their actual profile response is in a sub-object, and we have no way to query sub-objects in the current implementation:

{
  "data": {
    "id": "2244994945",
    "name": "TwitterDev",
    "username": "Twitter Dev"
  }
}

@autr
Copy link
Author

autr commented Mar 28, 2022

OK thank you, that makes perfect sense.

What is the best way to see the response via an npm instance of Directus (ie. directus start)?

Twitter and Microsoft I'm going to park for now, but will try with others.

@aidenfoxx
Copy link
Contributor

Well you can get more info about what's going wrong by enabling trace logging. Other than that I'd probably manually modify the "node_modules/directus/dist/auth/drivers/oauth2.js" and log userInfo in the getUserID function.

@directus directus locked and limited conversation to collaborators Mar 28, 2022
@azrikahar azrikahar converted this issue into discussion #12443 Mar 28, 2022

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants