Skip to content

Commit 182493f

Browse files
dionisio-bot[bot]yasnagatricardogarimjulio-rocketchat
authored
fix: imported fixes 03-09-2026 (#39510)
Co-authored-by: Yasmim Nagat <[email protected]> Co-authored-by: Ricardo Garim <[email protected]> Co-authored-by: Julio Araujo <[email protected]> Co-authored-by: yasnagat <[email protected]>
1 parent 60d3126 commit 182493f

File tree

4 files changed

+41
-14
lines changed

4 files changed

+41
-14
lines changed

.changeset/blue-points-dream.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@rocket.chat/meteor': patch
3+
---
4+
5+
Security Hotfix (https://docs.rocket.chat/docs/security-fixes-and-updates)

apps/meteor/app/api/server/middlewares/authentication.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ export function authenticationMiddleware(
2727
if (userId && authToken) {
2828
req.user = (await Users.findOneByIdAndLoginToken(userId as string, hashLoginToken(authToken as string))) || undefined;
2929
} else {
30-
req.user = await oAuth2ServerAuth({
31-
headers: req.headers as Record<string, string | undefined>,
32-
query: req.query as Record<string, string | undefined>,
33-
});
30+
const { authorization } = req.headers;
31+
const accessToken = typeof req.query.access_token === 'string' ? req.query.access_token : undefined;
32+
delete req.query.access_token;
33+
req.user = await oAuth2ServerAuth({ authorization, accessToken });
3434
}
3535

3636
if (config.rejectUnauthorized && !req.user) {

apps/meteor/app/oauth2-server-config/server/oauth/oauth2-server.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,9 @@ async function getAccessToken(accessToken: string) {
1919
return OAuthAccessTokens.findOneByAccessToken(accessToken);
2020
}
2121

22-
export async function oAuth2ServerAuth(partialRequest: {
23-
headers: Record<string, string | undefined>;
24-
query: Record<string, string | undefined>;
25-
}): Promise<IUser | undefined> {
26-
const headerToken = partialRequest.headers.authorization?.replace('Bearer ', '');
27-
const queryToken = partialRequest.query.access_token;
28-
const incomingToken = headerToken || queryToken;
22+
export async function oAuth2ServerAuth(partialRequest: { authorization?: string; accessToken?: string }): Promise<IUser | undefined> {
23+
const headerToken = partialRequest.authorization?.replace('Bearer ', '');
24+
const incomingToken = headerToken || partialRequest.accessToken;
2925

3026
if (!incomingToken) {
3127
return;
@@ -76,10 +72,14 @@ oauth2server.app.get('/oauth/userinfo', async (req: Request, res: Response) => {
7672
});
7773

7874
API.v1.addAuthMethod((routeContext) => {
79-
const headers = Object.fromEntries(routeContext.request.headers.entries());
80-
const query = (isPlainObject(routeContext.queryParams) ? routeContext.queryParams : {}) as Record<string, string | undefined>;
75+
const authorization = routeContext.request.headers.get('authorization') ?? undefined;
76+
const query = isPlainObject(routeContext.queryParams) ? routeContext.queryParams : {};
77+
const accessToken = typeof query.access_token === 'string' ? query.access_token : undefined;
78+
if ((routeContext.queryParams as Record<string, unknown>)?.access_token) {
79+
delete (routeContext.queryParams as Record<string, unknown>).access_token;
80+
}
8181

82-
return oAuth2ServerAuth({ headers, query });
82+
return oAuth2ServerAuth({ authorization, accessToken });
8383
});
8484

8585
(WebApp.connectHandlers as unknown as ReturnType<typeof express>).use(oauth2server.app);

apps/meteor/tests/end-to-end/api/oauth-server.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,5 +174,27 @@ describe('[OAuth Server]', () => {
174174
expect(res.body).to.have.nested.property('user._id', 'rocketchat.internal.admin.test');
175175
});
176176
});
177+
178+
const malformedTokenPayloads = [
179+
{ query: { 'access_token[$ne]': 'null' }, description: '$ne operator' },
180+
{ query: { 'access_token[$exists]': 'true' }, description: '$exists operator' },
181+
{ query: { 'access_token[$gt]': '' }, description: '$gt operator' },
182+
{ query: { 'access_token[$regex]': '.*' }, description: '$regex operator' },
183+
{ query: { access_token: 'invalid-token' }, description: 'invalid string token' },
184+
];
185+
186+
malformedTokenPayloads.forEach(({ query, description }) => {
187+
it(`should reject access_token with ${description}`, async () => {
188+
await request
189+
.get(api('me'))
190+
.query(query)
191+
.expect('Content-Type', 'application/json')
192+
.expect(401)
193+
.expect((res: Response) => {
194+
expect(res.body).to.have.property('status', 'error');
195+
expect(res.body).to.have.property('message', 'You must be logged in to do this.');
196+
});
197+
});
198+
});
177199
});
178200
});

0 commit comments

Comments
 (0)