Skip to content

Commit

Permalink
Merge branch 'upgradeTab' of github.com:RocketChat/Rocket.Chat into i…
Browse files Browse the repository at this point in the history
…framePageUpgrade

* 'upgradeTab' of github.com:RocketChat/Rocket.Chat:
  [FIX] Ignore customClass on messages (#24845)
  [FIX] Apple OAuth (#24879)
  Language update from LingoHub 🤖 (#24895)
  [IMPROVE] New omnichannel statistics and async statistics processing. (#24749)
  [FIX] Missing dependency on useEffect at CallProvider (#24882)
  Chore: Fix MongoDB versions on release notes (#24877)
  [FIX] auto-join team channels not honoring user preferences (#24779)
  Bump pino from 7.8.1 to 7.9.1 in /ee/server/services (#24869)
  Bump pino-pretty from 7.5.3 to 7.5.4 in /ee/server/services (#24870)
  [FIX] Disable voip button when call is in progress (#24864)
  [FIX] Broken build caused by PRs modifying same file differently(#24863)
  Regression: Role Sync not always working (#24850)
  [FIX] Match SidebarFooter component with design (#24838)
  [IMPROVE] Standarize queue behavior for managers and agents when subscribing (#24837)
  [FIX] VoIP button gets disabled whenever user status changes (#24789)
  [FIX] Wrong param usage on queue summary call (#24799)
  • Loading branch information
gabriellsh committed Mar 21, 2022
2 parents a413404 + 09c2624 commit 884b0d7
Show file tree
Hide file tree
Showing 44 changed files with 701 additions and 457 deletions.
2 changes: 1 addition & 1 deletion .houston/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const getMongoVersion = async function({ version, git }) {
return [];
}

return mongoMatch[1].replace(/"/g, '').replace(/ /g, '').split(',');
return mongoMatch[1].replace(/["' ]/g, '').split(',');
} catch (e) {
console.error(e);
}
Expand Down
4 changes: 4 additions & 0 deletions app/apple/client/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { CustomOAuth } from '../../custom-oauth/client/custom_oauth_client';
import { config } from '../lib/config';

new CustomOAuth('apple', config);
10 changes: 10 additions & 0 deletions app/apple/lib/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const config = {
serverURL: 'https://appleid.apple.com',
authorizePath: '/auth/authorize?response_mode=form_post',
responseType: 'code id_token',
tokenPath: '/auth/token',
scope: 'name email',
mergeUsers: true,
accessTokenParam: 'access_token',
loginStyle: 'popup',
};
72 changes: 72 additions & 0 deletions app/apple/server/AppleCustomOAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Accounts } from 'meteor/accounts-base';
import { HTTP } from 'meteor/http';
import NodeRSA from 'node-rsa';
import { KJUR } from 'jsrsasign';

import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server';
import { MeteorError } from '../../../server/sdk/errors';

const isValidAppleJWT = (identityToken: string, header: any): any => {
const applePublicKeys = HTTP.get('https://appleid.apple.com/auth/keys').data.keys as any;
const { kid } = header;

const key = applePublicKeys.find((k: any) => k.kid === kid);

const pubKey = new NodeRSA();
pubKey.importKey({ n: Buffer.from(key.n, 'base64'), e: Buffer.from(key.e, 'base64') }, 'components-public');
const userKey = pubKey.exportKey('public');

try {
return KJUR.jws.JWS.verify(identityToken, userKey, ['RS256']);
} catch {
return false;
}
};

export class AppleCustomOAuth extends CustomOAuth {
getIdentity(_accessToken: string, query: Record<string, any>): any {
const { id_token: identityToken, user: userStr = '' } = query;

let user = {} as any;
try {
user = JSON.parse(userStr);
} catch (e) {
// ignore
}

const decodedToken = KJUR.jws.JWS.parse(identityToken);

if (!isValidAppleJWT(identityToken, decodedToken.headerObj)) {
return {
type: 'apple',
error: new MeteorError(Accounts.LoginCancelledError.numericError, 'identityToken is a invalid JWT'),
};
}

const { iss, sub, email } = decodedToken.payloadObj as any;
if (!iss) {
return {
type: 'apple',
error: new MeteorError(Accounts.LoginCancelledError.numericError, 'Insufficient data in auth response token'),
};
}

const serviceData = {
id: sub,
email,
name: '',
};

if (email) {
serviceData.email = email;
}

if (user?.name) {
serviceData.name = `${user.name.firstName}${user.name.middleName ? ` ${user.name.middleName}` : ''}${
user.name.lastName ? ` ${user.name.lastName}` : ''
}`;
}

return serviceData;
}
}
43 changes: 22 additions & 21 deletions app/apple/server/appleOauthRegisterService.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
import { KJUR } from 'jsrsasign';
import { ServiceConfiguration } from 'meteor/service-configuration';

import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server';
import { settings, settingsRegistry } from '../../settings/server';
import { config } from '../lib/config';
import { AppleCustomOAuth } from './AppleCustomOAuth';

const config = {
serverURL: 'https://appleid.apple.com',
tokenPath: '/auth/token',
scope: 'name email',
mergeUsers: true,
accessTokenParam: 'access_token',
loginStyle: 'popup',
};

new CustomOAuth('apple', config);
export const AppleOAuth = new AppleCustomOAuth('apple', config);

settingsRegistry.addGroup('OAuth', function () {
this.section('Apple', function () {
Expand Down Expand Up @@ -47,28 +39,37 @@ settings.watchMultiple(
alg: 'ES256',
};

const tokenPayload = {
iss,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 300,
aud: 'https://appleid.apple.com',
sub: clientId,
};
const now = new Date();
const exp = new Date();
exp.setMonth(exp.getMonth() + 5); // from Apple docs expiration time must no be greater than 6 months

const secret = KJUR.jws.JWS.sign(null, HEADER, tokenPayload, serverSecret as string);
const secret = KJUR.jws.JWS.sign(
null,
HEADER,
{
iss,
iat: Math.floor(now.getTime() / 1000),
exp: Math.floor(exp.getTime() / 1000),
aud: 'https://appleid.apple.com',
sub: clientId,
},
serverSecret as string,
);

ServiceConfiguration.configurations.upsert(
{
service: 'apple',
},
{
$set: {
// We'll hide this button on Web Client
showButton: false,
showButton: true,
secret,
enabled: settings.get('Accounts_OAuth_Apple'),
loginStyle: 'popup',
clientId,
buttonLabelText: 'Sign in with Apple',
buttonColor: '#000',
buttonLabelColor: '#FFF',
},
},
);
Expand Down
2 changes: 0 additions & 2 deletions app/apple/server/index.js

This file was deleted.

File renamed without changes.
33 changes: 0 additions & 33 deletions app/apple/server/loginHandler.js

This file was deleted.

74 changes: 0 additions & 74 deletions app/apple/server/tokenHandler.js

This file was deleted.

3 changes: 2 additions & 1 deletion app/custom-oauth/client/custom_oauth_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export class CustomOAuth {
this.serverURL = options.serverURL;
this.authorizePath = options.authorizePath;
this.scope = options.scope;
this.responseType = options.responseType || 'code';

if (!isURL(this.authorizePath)) {
this.authorizePath = this.serverURL + this.authorizePath;
Expand Down Expand Up @@ -93,7 +94,7 @@ export class CustomOAuth {
const loginUrl =
`${this.authorizePath}${separator}client_id=${config.clientId}&redirect_uri=${encodeURIComponent(
OAuth._redirectUri(this.name, config),
)}&response_type=code` +
)}&response_type=${encodeURIComponent(this.responseType)}` +
`&state=${encodeURIComponent(OAuth._stateParam(loginStyle, credentialToken, options.redirectUrl))}&scope=${encodeURIComponent(
this.scope,
)}`;
Expand Down
5 changes: 5 additions & 0 deletions app/custom-oauth/server/custom_oauth_server.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export class CustomOAuth {
constructor(name: string, options: Record<string, any>);

getIdentity(accessToken: string, query: Record<string, any>): any;
}
2 changes: 1 addition & 1 deletion app/custom-oauth/server/custom_oauth_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ export class CustomOAuth {
const self = this;
OAuth.registerService(this.name, 2, null, (query) => {
const response = self.getAccessToken(query);
const identity = self.getIdentity(response.access_token);
const identity = self.getIdentity(response.access_token, query);

const serviceData = {
_OAuthCustom: true,
Expand Down
Loading

0 comments on commit 884b0d7

Please sign in to comment.