Require authentication to participate in chat (#4762)
* feat(chat): require authentication to participate in chat * fix: it's pretty much impossible to bypass the auth requirement, addressing review feedback anyway * feat(chat): render chat text input as disabled if chat auth is required * Commit updated API documentation --------- Co-authored-by: Owncast <owncast@owncast.online>
This commit is contained in:
@@ -19,6 +19,7 @@ import {
|
||||
FIELD_PROPS_ENABLE_CHAT_SLUR_FILTER,
|
||||
CHAT_ESTABLISHED_USER_MODE,
|
||||
FIELD_PROPS_DISABLE_CHAT,
|
||||
FIELD_PROPS_CHAT_REQUIRE_AUTHENTICATION,
|
||||
postConfigUpdateToAPI,
|
||||
RESET_TIMEOUT,
|
||||
TEXTFIELD_PROPS_CHAT_FORBIDDEN_USERNAMES,
|
||||
@@ -47,6 +48,7 @@ export default function ConfigChat() {
|
||||
chatEstablishedUserMode,
|
||||
chatSpamProtectionEnabled,
|
||||
chatSlurFilterEnabled,
|
||||
chatRequireAuthentication,
|
||||
} = serverConfig;
|
||||
const { welcomeMessage } = instanceDetails;
|
||||
|
||||
@@ -77,6 +79,10 @@ export default function ConfigChat() {
|
||||
handleFieldChange({ fieldName: 'chatSlurFilterEnabled', value: enabled });
|
||||
}
|
||||
|
||||
function handleChatRequireAuthenticationChange(enabled: boolean) {
|
||||
handleFieldChange({ fieldName: 'chatRequireAuthentication', value: enabled });
|
||||
}
|
||||
|
||||
function resetForbiddenUsernameState() {
|
||||
setForbiddenUsernameSaveState(null);
|
||||
}
|
||||
@@ -169,6 +175,7 @@ export default function ConfigChat() {
|
||||
chatEstablishedUserMode,
|
||||
chatSpamProtectionEnabled,
|
||||
chatSlurFilterEnabled,
|
||||
chatRequireAuthentication,
|
||||
});
|
||||
}, [serverConfig]);
|
||||
|
||||
@@ -250,6 +257,12 @@ export default function ConfigChat() {
|
||||
checked={formDataValues.chatSlurFilterEnabled}
|
||||
onChange={handleChatSlurFilterChange}
|
||||
/>
|
||||
<ToggleSwitch
|
||||
fieldName="chatRequireAuthentication"
|
||||
{...FIELD_PROPS_CHAT_REQUIRE_AUTHENTICATION}
|
||||
checked={formDataValues.chatRequireAuthentication}
|
||||
onChange={handleChatRequireAuthenticationChange}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useEffect } from 'react';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
import { useTranslation } from 'next-export-i18n';
|
||||
import { ChatMessage } from '../../../../interfaces/chat-message.model';
|
||||
import { ChatContainer } from '../../../../components/chat/ChatContainer/ChatContainer';
|
||||
import {
|
||||
@@ -12,6 +13,7 @@ import {
|
||||
appStateAtom,
|
||||
serverStatusState,
|
||||
isChatAvailableSelector,
|
||||
chatAuthenticatedAtom,
|
||||
} from '../../../../components/stores/ClientConfigStore';
|
||||
import Header from '../../../../components/ui/Header/Header';
|
||||
import { ClientConfig } from '../../../../interfaces/client-config.model';
|
||||
@@ -19,8 +21,10 @@ import { AppStateOptions } from '../../../../components/stores/application-state
|
||||
import { ServerStatus } from '../../../../interfaces/server-status.model';
|
||||
import { Theme } from '../../../../components/theme/Theme';
|
||||
import { ComponentError } from '../../../../components/ui/ComponentError/ComponentError';
|
||||
import { Localization } from '../../../../types/localization';
|
||||
|
||||
export default function ReadWriteChatEmbed() {
|
||||
const { t } = useTranslation();
|
||||
const currentUser = useRecoilValue(currentUserAtom);
|
||||
const messages = useRecoilValue<ChatMessage[]>(visibleChatMessagesSelector);
|
||||
const clientConfig = useRecoilValue<ClientConfig>(clientConfigStateAtom);
|
||||
@@ -28,8 +32,19 @@ export default function ReadWriteChatEmbed() {
|
||||
|
||||
const appState = useRecoilValue<AppStateOptions>(appStateAtom);
|
||||
const isChatAvailable = useRecoilValue(isChatAvailableSelector);
|
||||
const isUserAuthenticated = useRecoilValue<boolean>(chatAuthenticatedAtom);
|
||||
|
||||
const { name, chatDisabled } = clientConfig;
|
||||
const { name, chatDisabled, chatRequireAuthentication } = clientConfig;
|
||||
|
||||
// Determine if chat input should be enabled based on authentication requirements.
|
||||
// Moderators bypass the authentication requirement.
|
||||
const chatInputEnabled = !!(
|
||||
isChatAvailable &&
|
||||
(!chatRequireAuthentication || isUserAuthenticated || currentUser?.isModerator)
|
||||
);
|
||||
const chatInputDisabledMessage = chatRequireAuthentication
|
||||
? t(Localization.Frontend.Chat.authenticateToChat)
|
||||
: t(Localization.Frontend.chatDisabled);
|
||||
const { videoAvailable } = appState;
|
||||
const { streamTitle, online } = clientStatus;
|
||||
|
||||
@@ -73,6 +88,8 @@ export default function ReadWriteChatEmbed() {
|
||||
showInput
|
||||
height="92vh"
|
||||
chatAvailable={isChatAvailable}
|
||||
inputEnabled={chatInputEnabled}
|
||||
inputDisabledPlaceholder={chatInputDisabledMessage}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user