Skip to content

Commit 6a46e57

Browse files
authored
Merge branch 'develop' into update-meteor-2.5.2
2 parents 4644f91 + 1441feb commit 6a46e57

File tree

16 files changed

+224
-53
lines changed

16 files changed

+224
-53
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { KJUR } from 'jsrsasign';
2+
import { ServiceConfiguration } from 'meteor/service-configuration';
3+
4+
import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server';
5+
import { settings, settingsRegistry } from '../../settings/server';
6+
7+
const config = {
8+
serverURL: 'https://appleid.apple.com',
9+
tokenPath: '/auth/token',
10+
scope: 'name email',
11+
mergeUsers: true,
12+
accessTokenParam: 'access_token',
13+
loginStyle: 'popup',
14+
};
15+
16+
new CustomOAuth('apple', config);
17+
18+
settingsRegistry.addGroup('OAuth', function () {
19+
this.section('Apple', function () {
20+
this.add('Accounts_OAuth_Apple', false, { type: 'boolean', public: true });
21+
22+
this.add('Accounts_OAuth_Apple_id', '', { type: 'string', public: true });
23+
this.add('Accounts_OAuth_Apple_secretKey', '', { type: 'string', multiline: true });
24+
25+
this.add('Accounts_OAuth_Apple_iss', '', { type: 'string' });
26+
this.add('Accounts_OAuth_Apple_kid', '', { type: 'string' });
27+
});
28+
});
29+
30+
settings.watchMultiple(
31+
[
32+
'Accounts_OAuth_Apple',
33+
'Accounts_OAuth_Apple_id',
34+
'Accounts_OAuth_Apple_secretKey',
35+
'Accounts_OAuth_Apple_iss',
36+
'Accounts_OAuth_Apple_kid',
37+
],
38+
([enabled, clientId, serverSecret, iss, kid]) => {
39+
if (!enabled) {
40+
return ServiceConfiguration.configurations.remove({
41+
service: 'apple',
42+
});
43+
}
44+
45+
const HEADER = {
46+
kid,
47+
alg: 'ES256',
48+
};
49+
50+
const tokenPayload = {
51+
iss,
52+
iat: Math.floor(Date.now() / 1000),
53+
exp: Math.floor(Date.now() / 1000) + 300,
54+
aud: 'https://appleid.apple.com',
55+
sub: clientId,
56+
};
57+
58+
const secret = KJUR.jws.JWS.sign(null, HEADER, tokenPayload, serverSecret as string);
59+
60+
ServiceConfiguration.configurations.upsert(
61+
{
62+
service: 'apple',
63+
},
64+
{
65+
$set: {
66+
// We'll hide this button on Web Client
67+
showButton: false,
68+
secret,
69+
enabled: settings.get('Accounts_OAuth_Apple'),
70+
loginStyle: 'popup',
71+
clientId,
72+
},
73+
},
74+
);
75+
},
76+
);

app/apple/server/startup.ts

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1 @@
1-
import { ServiceConfiguration } from 'meteor/service-configuration';
2-
3-
import { settings, settingsRegistry } from '../../settings/server';
4-
5-
settingsRegistry.addGroup('OAuth', function () {
6-
this.section('Apple', function () {
7-
this.add('Accounts_OAuth_Apple', false, { type: 'boolean', public: true });
8-
});
9-
});
10-
11-
settings.watch('Accounts_OAuth_Apple', (enabled) => {
12-
if (!enabled) {
13-
return ServiceConfiguration.configurations.remove({
14-
service: 'apple',
15-
});
16-
}
17-
18-
ServiceConfiguration.configurations.upsert(
19-
{
20-
service: 'apple',
21-
},
22-
{
23-
$set: {
24-
// We'll hide this button on Web Client
25-
showButton: false,
26-
enabled: settings.get('Accounts_OAuth_Apple'),
27-
},
28-
},
29-
);
30-
});
1+
import './appleOauthRegisterService';

app/apple/server/tokenHandler.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const isValidAppleJWT = (identityToken, header) => {
2525
}
2626
};
2727

28-
export const handleIdentityToken = ({ identityToken, fullName, email }) => {
28+
export const handleIdentityToken = ({ identityToken, fullName = {}, email }) => {
2929
check(identityToken, String);
3030
check(fullName, Match.Maybe(Object));
3131
check(email, Match.Maybe(String));

app/cors/server/cors.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ WebApp.rawConnectHandlers.use(function (req, res, next) {
3535
]
3636
.filter(Boolean)
3737
.join(' ');
38-
38+
const external = [settings.get('Accounts_OAuth_Apple') && 'https://appleid.cdn-apple.com'].filter(Boolean).join(' ');
3939
res.setHeader(
4040
'Content-Security-Policy',
4141
[
@@ -45,7 +45,7 @@ WebApp.rawConnectHandlers.use(function (req, res, next) {
4545
'frame-src *',
4646
'img-src * data: blob:',
4747
'media-src * data:',
48-
`script-src 'self' 'unsafe-eval' ${inlineHashes} ${cdn_prefixes}`,
48+
`script-src 'self' 'unsafe-eval' ${inlineHashes} ${cdn_prefixes} ${external}`,
4949
`style-src 'self' 'unsafe-inline' ${cdn_prefixes}`,
5050
].join('; '),
5151
);
Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1-
<template name='loginServices'>
1+
<template name="loginServices">
22
{{#if loginService.length}}
3-
<div class="oauth-login buttons-group">
4-
{{#each loginService}}
5-
<button type="button" class="rc-button rc-button--secondary external-login {{service.service}}" title="{{displayName}}" style="{{#if service.buttonColor}}background-color:{{service.buttonColor}};{{/if}}{{#if service.buttonLabelColor}}color:{{service.buttonLabelColor}};{{/if}}"><i class="icon-{{icon}} service-icon"></i><i class="icon-spin animate-spin loading-icon hidden"></i><span>{{service.buttonLabelText}}</span></button>
6-
{{/each}}
7-
</div>
8-
{{/if}}
3+
<div class="oauth-login buttons-group">
4+
{{#each loginService}}
5+
<button
6+
type="button"
7+
class="rc-button rc-button--secondary external-login {{service.service}}"
8+
title="{{displayName}}"
9+
style="{{#if service.buttonColor}}background-color:{{service.buttonColor}};{{/if}}{{#if service.buttonLabelColor}}color:{{service.buttonLabelColor}};{{/if}}"
10+
>
11+
<i class="icon-{{icon}} service-icon"></i
12+
><i class="icon-spin animate-spin loading-icon hidden"></i
13+
><span>{{service.buttonLabelText}}</span>
14+
</button>
15+
{{/each}}
16+
</div>
17+
{{/if}} {{> AppleOauthButton}}
918
</template>

client/components/UserCard/UserCard.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,9 @@ const UserCard = forwardRef(function UserCard(
6868
</Box>
6969
)}
7070
</Box>
71-
{customStatus && <Info>{typeof customStatus === 'string' ? <MarkdownText content={customStatus} /> : customStatus}</Info>}
71+
{customStatus && (
72+
<Info>{typeof customStatus === 'string' ? <MarkdownText content={customStatus} parseEmoji={true} /> : customStatus}</Info>
73+
)}
7274
<Roles>{roles}</Roles>
7375
<Info>{localTime}</Info>
7476
{bio && (

client/lib/utils/createAnchor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
export const createAnchor = (id: string): HTMLDivElement => {
1+
export const createAnchor = (id: string, target = document.body): HTMLDivElement => {
22
const div = document.createElement('div');
33
div.id = id;
4-
document.body.appendChild(div);
4+
target.appendChild(div);
55
return div;
66
};

client/sidebar/header/UserDropdown.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,12 @@ const UserDropdown = ({ user, onClose }: UserDropdownProps): ReactElement => {
135135
</Margins>
136136
</Box>
137137
<Box color='hint'>
138-
<MarkdownText withTruncatedText content={statusText || t(status || 'offline')} variant='inlineWithoutBreaks' />
138+
<MarkdownText
139+
withTruncatedText
140+
parseEmoji={true}
141+
content={statusText || t(status || 'offline')}
142+
variant='inlineWithoutBreaks'
143+
/>
139144
</Box>
140145
</Box>
141146
</Box>
@@ -160,7 +165,9 @@ const UserDropdown = ({ user, onClose }: UserDropdownProps): ReactElement => {
160165
<Option.Column>
161166
<UserStatus status={modifier} />
162167
</Option.Column>
163-
<Option.Content>{name}</Option.Content>
168+
<Option.Content>
169+
<MarkdownText content={name} parseEmoji={true} variant='inline' />
170+
</Option.Content>
164171
</Option>
165172
);
166173
})}

client/templates.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ createTemplateForComponent('SortList', () => import('./components/SortList'));
160160

161161
createTemplateForComponent('CreateRoomList', () => import('./sidebar/header/actions/CreateRoomList'));
162162

163+
createTemplateForComponent('AppleOauthButton', () => import('./views/login/AppleOauth/AppleOauthButton'), {
164+
renderContainerView: () => HTML.DIV({ style: 'display: flex; justify-content: center;' }),
165+
});
166+
163167
createTemplateForComponent('UserDropdown', () => import('./sidebar/header/UserDropdown'));
164168

165169
createTemplateForComponent('sidebarFooter', () => import('./sidebar/footer'));
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { Accounts } from 'meteor/accounts-base';
2+
import React, { FC, useCallback, useEffect, useLayoutEffect, useRef } from 'react';
3+
4+
import { useAbsoluteUrl } from '../../../contexts/ServerContext';
5+
import { useSetting } from '../../../contexts/SettingsContext';
6+
7+
export const AppleOauthButton: FC = () => {
8+
const enabled = useSetting('Accounts_OAuth_Apple');
9+
const absoluteUrl = useAbsoluteUrl();
10+
const appleClientID = useSetting('Accounts_OAuth_Apple_id');
11+
12+
const redirectURI = absoluteUrl('_oauth/apple');
13+
14+
useEffect(() => {
15+
const success = (data: any): void => {
16+
const { authorization, user } = data.detail;
17+
18+
Accounts.callLoginMethod({
19+
methodArguments: [
20+
{
21+
serviceName: 'apple',
22+
identityToken: authorization.id_token,
23+
...(user && {
24+
fullName: {
25+
givenName: user.name.firstName,
26+
familyName: user.name.lastName,
27+
},
28+
email: user.email,
29+
}),
30+
},
31+
],
32+
userCallback: console.log,
33+
});
34+
};
35+
36+
const error = (error: any): void => {
37+
// handle error.
38+
console.error(error);
39+
};
40+
document.addEventListener('AppleIDSignInOnSuccess', success);
41+
// Listen for authorization failures
42+
document.addEventListener('AppleIDSignInOnFailure', error);
43+
44+
return (): void => {
45+
document.removeEventListener('AppleIDSignInOnSuccess', success);
46+
document.removeEventListener('AppleIDSignInOnFailure', error);
47+
};
48+
}, []);
49+
50+
const scriptLoadedHandler = useCallback(() => {
51+
if (!enabled) {
52+
return;
53+
}
54+
(window as any).AppleID.auth.init({
55+
clientId: appleClientID,
56+
scope: 'name email',
57+
redirectURI,
58+
usePopup: true,
59+
});
60+
}, [enabled, appleClientID, redirectURI]);
61+
62+
const ref = useRef<HTMLScriptElement>();
63+
64+
useEffect(() => {
65+
if ((window as any).AppleID) {
66+
scriptLoadedHandler();
67+
return;
68+
}
69+
if (!ref.current) {
70+
return;
71+
}
72+
ref.current.onload = scriptLoadedHandler;
73+
}, [scriptLoadedHandler]);
74+
75+
useLayoutEffect(() => {
76+
const script = document.createElement('script');
77+
script.src = 'https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js';
78+
script.async = true;
79+
ref.current = script;
80+
document.body.appendChild(script);
81+
return (): void => {
82+
document.body.removeChild(script);
83+
};
84+
}, []);
85+
86+
if (!enabled) {
87+
return null;
88+
}
89+
90+
return <div id='appleid-signin' data-height='40px'></div>;
91+
};
92+
93+
export default AppleOauthButton;

0 commit comments

Comments
 (0)