snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
34
apps/meteor/client/views/oauth/OAuthAuthorizationPage.tsx
Normal file
34
apps/meteor/client/views/oauth/OAuthAuthorizationPage.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { useSearchParameter, useUser } from '@rocket.chat/ui-contexts';
|
||||
import RegistrationPageRouter from '@rocket.chat/web-ui-registration';
|
||||
|
||||
import { getErrorMessage } from '../../lib/errorHandling';
|
||||
import PageLoading from '../root/PageLoading';
|
||||
import AuthorizationFormPage from './components/AuthorizationFormPage';
|
||||
import ErrorPage from './components/ErrorPage';
|
||||
import { useOAuthAppQuery } from './hooks/useOAuthAppQuery';
|
||||
|
||||
const OAuthAuthorizationPage = () => {
|
||||
const user = useUser();
|
||||
const clientId = useSearchParameter('client_id');
|
||||
const redirectUri = useSearchParameter('redirect_uri');
|
||||
|
||||
const oauthAppQuery = useOAuthAppQuery(clientId, {
|
||||
enabled: !!user,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return <RegistrationPageRouter />;
|
||||
}
|
||||
|
||||
if (oauthAppQuery.isPending) {
|
||||
return <PageLoading />;
|
||||
}
|
||||
|
||||
if (oauthAppQuery.isError) {
|
||||
return <ErrorPage error={getErrorMessage(oauthAppQuery.error)} />;
|
||||
}
|
||||
|
||||
return <AuthorizationFormPage oauthApp={oauthAppQuery.data} redirectUri={redirectUri ?? ''} user={user} />;
|
||||
};
|
||||
|
||||
export default OAuthAuthorizationPage;
|
||||
20
apps/meteor/client/views/oauth/OAuthErrorPage.tsx
Normal file
20
apps/meteor/client/views/oauth/OAuthErrorPage.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { useRouteParameter } from '@rocket.chat/ui-contexts';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import ErrorPage from './components/ErrorPage';
|
||||
|
||||
const OAuthErrorPage = () => {
|
||||
const errorType = useRouteParameter('error');
|
||||
const { t } = useTranslation();
|
||||
|
||||
switch (errorType) {
|
||||
case '404':
|
||||
return <ErrorPage error={t('core.Invalid_OAuth_client')} />;
|
||||
case 'invalid_redirect_uri':
|
||||
return <ErrorPage error={t('core.Redirect_URL_does_not_match')} />;
|
||||
default:
|
||||
return <ErrorPage error={t('core.Error_something_went_wrong')} />;
|
||||
}
|
||||
};
|
||||
|
||||
export default OAuthErrorPage;
|
||||
@@ -0,0 +1,86 @@
|
||||
import type { IOAuthApps, IUser } from '@rocket.chat/core-typings';
|
||||
import { Box, Button, ButtonGroup } from '@rocket.chat/fuselage';
|
||||
import { Form, FormContainer, FormFooter, FormHeader, FormTitle } from '@rocket.chat/layout';
|
||||
import { useLoginToken, useLogout, useRoute } from '@rocket.chat/ui-contexts';
|
||||
import { useEffect, useId, useRef } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
|
||||
import CurrentUserDisplay from './CurrentUserDisplay';
|
||||
import Layout from './Layout';
|
||||
|
||||
export type AuthorizationFormPageProps = {
|
||||
oauthApp: IOAuthApps;
|
||||
redirectUri: string;
|
||||
user: IUser;
|
||||
};
|
||||
|
||||
const AuthorizationFormPage = ({ oauthApp, redirectUri, user }: AuthorizationFormPageProps) => {
|
||||
const token = useLoginToken() ?? undefined;
|
||||
|
||||
const formLabelId = useId();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const homeRoute = useRoute('home');
|
||||
|
||||
const handleCancelButtonClick = () => {
|
||||
queueMicrotask(() => {
|
||||
homeRoute.push();
|
||||
});
|
||||
window.close();
|
||||
};
|
||||
|
||||
const logout = useLogout();
|
||||
|
||||
const handleLogoutButtonClick = () => {
|
||||
logout();
|
||||
};
|
||||
|
||||
const submitRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const hasAuthorized = user.oauth?.authorizedClients?.includes(oauthApp.clientId);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasAuthorized) {
|
||||
submitRef.current?.click();
|
||||
}
|
||||
}, [oauthApp.clientId, hasAuthorized]);
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Form method='post' action='' aria-labelledby={formLabelId}>
|
||||
<FormHeader>
|
||||
<FormTitle id={formLabelId}>{t('core.Authorize_access_to_your_account')}</FormTitle>
|
||||
</FormHeader>
|
||||
<FormContainer>
|
||||
<Box withRichContent>
|
||||
<CurrentUserDisplay user={user} />
|
||||
|
||||
<p>
|
||||
<Trans i18nKey='core.OAuth_Full_Access_Warning' t={t} values={{ appName: oauthApp.name }}>
|
||||
<strong>{oauthApp.name}</strong>
|
||||
</Trans>
|
||||
</p>
|
||||
</Box>
|
||||
<input type='hidden' name='access_token' value={token} />
|
||||
<input type='hidden' name='client_id' value={oauthApp.clientId} />
|
||||
<input type='hidden' name='redirect_uri' value={redirectUri} />
|
||||
<input type='hidden' name='response_type' value='code' />
|
||||
</FormContainer>
|
||||
<FormFooter>
|
||||
<ButtonGroup stretch>
|
||||
<Button ref={submitRef} type='submit' primary name='allow' value='yes'>
|
||||
{t('core.Authorize')}
|
||||
</Button>
|
||||
<Button onClick={handleCancelButtonClick}>{t('core.Cancel')}</Button>
|
||||
<Button danger onClick={handleLogoutButtonClick}>
|
||||
{t('core.Logout')}
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</FormFooter>
|
||||
</Form>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default AuthorizationFormPage;
|
||||
@@ -0,0 +1,60 @@
|
||||
import type { IUser } from '@rocket.chat/core-typings';
|
||||
import { css } from '@rocket.chat/css-in-js';
|
||||
import { UserStatus } from '@rocket.chat/ui-client';
|
||||
import { useRolesDescription, useSetting } from '@rocket.chat/ui-contexts';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import LocalTime from '../../../components/LocalTime';
|
||||
import MarkdownText from '../../../components/MarkdownText';
|
||||
import { UserCard, UserCardInfo, UserCardRole } from '../../../components/UserCard';
|
||||
|
||||
const clampStyle = css`
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
word-break: break-all;
|
||||
`;
|
||||
|
||||
export type CurrentUserDisplayProps = {
|
||||
user: IUser;
|
||||
};
|
||||
|
||||
const CurrentUserDisplay = ({ user }: CurrentUserDisplayProps) => {
|
||||
const showRealNames = useSetting('UI_Use_Real_Name', false);
|
||||
const getRoles = useRolesDescription();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { username, avatarETag, name, statusText, nickname, roles, utcOffset, bio } = user;
|
||||
|
||||
const data = useMemo(
|
||||
() => ({
|
||||
username,
|
||||
etag: avatarETag,
|
||||
name: showRealNames ? name : username,
|
||||
nickname,
|
||||
status: <UserStatus.Online />,
|
||||
customStatus: statusText ?? <></>,
|
||||
roles: roles && getRoles(roles).map((role, index) => <UserCardRole key={index}>{role}</UserCardRole>),
|
||||
localTime: utcOffset && Number.isInteger(utcOffset) && <LocalTime utcOffset={utcOffset} />,
|
||||
bio: bio ? (
|
||||
<UserCardInfo withTruncatedText={false} className={clampStyle} height='x60'>
|
||||
{typeof bio === 'string' ? <MarkdownText variant='inline' content={bio} /> : bio}
|
||||
</UserCardInfo>
|
||||
) : (
|
||||
<></>
|
||||
),
|
||||
}),
|
||||
[avatarETag, bio, getRoles, name, nickname, roles, showRealNames, statusText, username, utcOffset],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>{t('core.You_are_logged_in_as')}</p>
|
||||
<UserCard user={data} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CurrentUserDisplay;
|
||||
25
apps/meteor/client/views/oauth/components/ErrorPage.tsx
Normal file
25
apps/meteor/client/views/oauth/components/ErrorPage.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { States, StatesIcon, StatesSubtitle, StatesTitle } from '@rocket.chat/fuselage';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Layout from './Layout';
|
||||
import { getErrorMessage } from '../../../lib/errorHandling';
|
||||
|
||||
export type ErrorPageProps = {
|
||||
error: unknown;
|
||||
};
|
||||
|
||||
const ErrorPage = ({ error }: ErrorPageProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<States>
|
||||
<StatesIcon name='warning' variation='danger' />
|
||||
<StatesTitle>{t('core.Error')}</StatesTitle>
|
||||
<StatesSubtitle>{getErrorMessage(error)}</StatesSubtitle>
|
||||
</States>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default ErrorPage;
|
||||
25
apps/meteor/client/views/oauth/components/Layout.tsx
Normal file
25
apps/meteor/client/views/oauth/components/Layout.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Box } from '@rocket.chat/fuselage';
|
||||
import { VerticalWizardLayout } from '@rocket.chat/layout';
|
||||
import { useAssetWithDarkModePath, useSetting } from '@rocket.chat/ui-contexts';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export type LayoutProps = {
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
const Layout = ({ children }: LayoutProps) => {
|
||||
const hideLogo = useSetting('Layout_Login_Hide_Logo', false);
|
||||
const customLogo = useAssetWithDarkModePath('logo');
|
||||
const customBackground = useAssetWithDarkModePath('background');
|
||||
|
||||
return (
|
||||
<VerticalWizardLayout
|
||||
background={customBackground}
|
||||
logo={!hideLogo && customLogo ? <Box is='img' maxHeight={40} marginInline={-8} src={customLogo} alt='Logo' /> : undefined}
|
||||
>
|
||||
{children}
|
||||
</VerticalWizardLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
31
apps/meteor/client/views/oauth/hooks/useOAuthAppQuery.ts
Normal file
31
apps/meteor/client/views/oauth/hooks/useOAuthAppQuery.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { IOAuthApps } from '@rocket.chat/core-typings';
|
||||
import { useEndpoint } from '@rocket.chat/ui-contexts';
|
||||
import type { UseQueryOptions } from '@tanstack/react-query';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
type UseOAuthAppQueryOptions = Omit<
|
||||
UseQueryOptions<IOAuthApps, unknown, IOAuthApps, readonly ['oauth-app', { readonly clientId: string | undefined }]>,
|
||||
'queryKey' | 'queryFn'
|
||||
>;
|
||||
|
||||
export const useOAuthAppQuery = (clientId: string | undefined, options?: UseOAuthAppQueryOptions) => {
|
||||
const getOAuthApp = useEndpoint('GET', '/v1/oauth-apps.get');
|
||||
|
||||
return useQuery({
|
||||
queryKey: ['oauth-app', { clientId }] as const,
|
||||
|
||||
queryFn: async () => {
|
||||
if (!clientId) {
|
||||
throw new Error('Invalid OAuth client');
|
||||
}
|
||||
|
||||
const { oauthApp } = await getOAuthApp({ clientId });
|
||||
return {
|
||||
...oauthApp,
|
||||
_createdAt: new Date(oauthApp._createdAt),
|
||||
_updatedAt: new Date(oauthApp._updatedAt),
|
||||
};
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user