feat(auth): Add X v2 provider (#41276)

* feat(auth): Add X v2 provider

* chore: reuse the existing twitter docs which will include instructions for both versions
This commit is contained in:
issuedat authored and GitHub committed 2025-12-23 12:33:29 +01:00
1 parent f3af2c3d3c
commit 1ee61059ef
6 files changed
+64 -3

No files matched your search

@@ -29,6 +29,7 @@ export interface Provider {
validationSchema: any // todo: use Yup type
misc: {
iconKey: 'gitlab-icon'
hasLightIcon: boolean
requiresRedirect: true
helper: string
alert: {
@@ -4,6 +4,7 @@ import { useQueryState } from 'nuqs'
import { useEffect, useState } from 'react'
import ReactMarkdown from 'react-markdown'
import { toast } from 'sonner'
import { useTheme } from 'next-themes'
import { useParams } from 'common'
import { Markdown } from 'components/interfaces/Markdown'
@@ -33,6 +34,7 @@ interface ProviderFormProps {
const doubleNegativeKeys = ['SMS_AUTOCONFIRM']
export const ProviderForm = ({ config, provider, isActive }: ProviderFormProps) => {
const { resolvedTheme } = useTheme()
const { ref: projectRef } = useParams()
const { data: organization } = useSelectedOrganizationQuery()
const [urlProvider, setUrlProvider] = useQueryState('provider', { defaultValue: '' })
@@ -141,7 +143,7 @@ export const ProviderForm = ({ config, provider, isActive }: ProviderFormProps)
onClick={handleProviderClick}
media={
<img
src={`${BASE_PATH}/img/icons/${provider.misc.iconKey}.svg`}
src={`${BASE_PATH}/img/icons/${provider.misc.iconKey}${provider.misc.hasLightIcon && !resolvedTheme?.includes('dark') ? '-light' : ''}.svg`}
width={18}
height={18}
alt={`${provider.title} auth icon`}
@@ -169,7 +171,7 @@ export const ProviderForm = ({ config, provider, isActive }: ProviderFormProps)
<SheetContent className="flex flex-col gap-0">
<SheetHeader className="shrink-0 flex items-center gap-4">
<img
src={`${BASE_PATH}/img/icons/${provider.misc.iconKey}.svg`}
src={`${BASE_PATH}/img/icons/${provider.misc.iconKey}${provider.misc.hasLightIcon && !resolvedTheme?.includes('dark') ? '-light' : ''}.svg`}
width={18}
height={18}
alt={`${provider.title} auth icon`}
@@ -105,6 +105,8 @@ export const AuthProvidersForm = () => {
isActive = authConfig && (authConfig as any)['EXTERNAL_SLACK_OIDC_ENABLED']
} else if (providerSchema.title.includes('Web3')) {
isActive = authConfig && (authConfig as any)['EXTERNAL_WEB3_SOLANA_ENABLED']
} else if (providerSchema.title.includes('X / Twitter (OAuth 2.0)')) {
isActive = authConfig && (authConfig as any)['EXTERNAL_X_ENABLED']
} else {
isActive =
authConfig &&
@@ -1201,7 +1201,7 @@ const EXTERNAL_PROVIDER_TWITCH = {
const EXTERNAL_PROVIDER_TWITTER = {
$schema: JSON_SCHEMA_VERSION,
type: 'object',
title: 'Twitter',
title: 'Twitter (Deprecated)',
link: `${DOCS_URL}/guides/auth/social-login/auth-twitter`,
properties: {
EXTERNAL_TWITTER_ENABLED: {
@@ -1244,6 +1244,53 @@ const EXTERNAL_PROVIDER_TWITTER = {
},
}
const EXTERNAL_PROVIDER_X = {
$schema: JSON_SCHEMA_VERSION,
type: 'object',
title: 'X / Twitter (OAuth 2.0)',
link: `${DOCS_URL}/guides/auth/social-login/auth-twitter`,
properties: {
EXTERNAL_X_ENABLED: {
title: 'X / Twitter enabled',
type: 'boolean',
},
EXTERNAL_X_CLIENT_ID: {
title: 'Client ID',
type: 'string',
},
EXTERNAL_X_SECRET: {
title: 'Client Secret',
type: 'string',
isSecret: true,
},
EXTERNAL_X_EMAIL_OPTIONAL: {
title: 'Allow users without an email',
description:
'Allows the user to successfully authenticate when the provider does not return an email address.',
type: 'boolean',
},
},
validationSchema: object().shape({
EXTERNAL_X_ENABLED: boolean().required(),
EXTERNAL_X_CLIENT_ID: string().when('EXTERNAL_X_ENABLED', {
is: true,
then: (schema) => schema.required('Client ID is required'),
otherwise: (schema) => schema,
}),
EXTERNAL_X_SECRET: string().when('EXTERNAL_X_ENABLED', {
is: true,
then: (schema) => schema.required('Client Secret is required'),
otherwise: (schema) => schema,
}),
EXTERNAL_X_EMAIL_OPTIONAL: boolean().optional(),
}),
misc: {
iconKey: 'x-icon',
hasLightIcon: true,
requiresRedirect: true,
},
}
const EXTERNAL_PROVIDER_SLACK = {
$schema: JSON_SCHEMA_VERSION,
type: 'object',
@@ -1562,6 +1609,7 @@ export const PROVIDERS_SCHEMAS = [
EXTERNAL_PROVIDER_LINKEDIN_OIDC,
EXTERNAL_PROVIDER_NOTION,
EXTERNAL_PROVIDER_TWITCH,
EXTERNAL_PROVIDER_X,
EXTERNAL_PROVIDER_TWITTER,
EXTERNAL_PROVIDER_SLACK_OIDC,
EXTERNAL_PROVIDER_SLACK,
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="256px" height="209px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" fill="#000"></path>
</svg>

After

Width:  |  Height:  |  Size: 427 B

+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="256px" height="209px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" fill="#ffffff"></path>
</svg>

After

Width:  |  Height:  |  Size: 432 B