Skip to content

Commit

Permalink
feat: oauth landing page [SQSERVICES-1775] (#14793)
Browse files Browse the repository at this point in the history
* feat: basic UI structure

* chore: add some validation for the params

* chore: remove errors

* chore: more structure added

* chore: update redux to add getTeam action

* fix: get redux working correctly with auth

* feat: improve routing

* feat: display team image, improved styling

* chore: get application name from BE

* chore: add oauth calls

* chore: update api client

* chore: add redirect for 2manyClients case

* chore: update copy for 2manyclients case

* chore: add back initial redirect

* feat: pass redirect url

* chore: fix scope parsing

* chore: update for loading icon

* chore: remove client device name

* runfix: issue with missing team icon

* runfix: issue with client page

* feat: improve styling

* chore: styling

* chore: add translations

* chore: update core

* chore: resolve comments

* fix: other instances of formatted message cleanup

* chore: move styles to individual file

* styles: fixing styling issues

* chore: switch to URLSearchParams

* chore: handle logout for error cases
  • Loading branch information
tlebon committed Mar 30, 2023
1 parent d07f10a commit 314696a
Show file tree
Hide file tree
Showing 19 changed files with 658 additions and 25 deletions.
15 changes: 14 additions & 1 deletion src/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@
"clientManager.headline": "Remove a device",
"clientManager.logout": "Cancel process",
"clientManager.subhead": "Remove one of your other devices to start using {brandName} on this one.",
"clientManager.oauth": "You are adding a new device. Only 7 devices can be active. Remove one of your devices to start using Wire on this one ({device}).",
"collectionSectionAudio": "Audio",
"collectionSectionFiles": "Files",
"collectionSectionImages": "Images",
Expand Down Expand Up @@ -956,6 +957,18 @@
"notificationTitleGroup": "{{user}} in {{conversation}}",
"notificationVoiceChannelActivate": "Calling",
"notificationVoiceChannelDeactivate": "Called",
"oauth.headline": "Permissions",
"oauth.logout": "Switch account",
"oauth.cancel": "Don't Allow",
"oauth.allow": "Allow",
"oauth.subhead": "{app} requires your permission to:",
"oauth.learnMore": "<learnMore>Learn more</learnMore> about these permissions in the settings.",
"oauth.details": "If you allow the permissions listed, Wire™ will be able to connect to your calendar. It won’t see the content of your calendar, just the ones happening with Wire. If you don’t grant the permissions, you can’t use this add-in.",
"oauth.privacypolicy": "Wire's <privacypolicy>Privacy Policy</privacypolicy>",
"oauth.scope.write_conversations": "Create conversations",
"oauth.scope.write_conversations_code": "Create conversation guest links",
"oauth.scope.read_self": "Access user information",
"oauth.scope.read_feature_configs": "Access team feature configurations",
"ongoingAudioCall": "Ongoing audio call with {{conversationName}}.",
"ongoingGroupAudioCall": "Ongoing conference call with {{conversationName}}.",
"ongoingGroupVideoCall": "Ongoing video conference call with {{conversationName}}, your camera is {{cameraStatus}}.",
Expand Down Expand Up @@ -1309,4 +1322,4 @@
"wireMacos": "{{brandName}} for macOS",
"wireWindows": "{{brandName}} for Windows",
"wire_for_web": "{{brandName}} for Web"
}
}
3 changes: 1 addition & 2 deletions src/script/auth/component/AcceptNewsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ const AcceptNewsModal = ({onConfirm, onDecline}: Props) => {
<FormattedMessage
{...acceptNewsModalStrings.privacyDescription}
values={{
// eslint-disable-next-line react/display-name
strong: ((...chunks: any[]) => <strong>{chunks}</strong>) as any,
strong: (...chunks: any[]) => <strong>{chunks}</strong>,
}}
/>
</Text>
Expand Down
15 changes: 6 additions & 9 deletions src/script/auth/component/AccountForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,7 @@ const AccountFormComponent = ({account, ...props}: Props & ConnectedProps & Disp
<FormattedMessage
{...accountFormStrings.termsAndPrivacyPolicy}
values={{
// eslint-disable-next-line react/display-name
privacypolicy: ((...chunks: string[] | React.ReactNode[]) => (
privacypolicy: (...chunks: string[] | React.ReactNode[]) => (
<a
target="_blank"
rel="noopener noreferrer"
Expand All @@ -274,9 +273,8 @@ const AccountFormComponent = ({account, ...props}: Props & ConnectedProps & Disp
>
{chunks}
</a>
)) as any,
// eslint-disable-next-line react/display-name
terms: ((...chunks: string[] | React.ReactNode[]) => (
),
terms: (...chunks: string[] | React.ReactNode[]) => (
<a
target="_blank"
rel="noopener noreferrer"
Expand All @@ -289,15 +287,14 @@ const AccountFormComponent = ({account, ...props}: Props & ConnectedProps & Disp
>
{chunks}
</a>
)) as any,
),
}}
/>
) : (
<FormattedMessage
{...accountFormStrings.terms}
values={{
// eslint-disable-next-line react/display-name
terms: ((...chunks: string[] | React.ReactNode[]) => (
terms: (...chunks: string[] | React.ReactNode[]) => (
<a
target="_blank"
rel="noopener noreferrer"
Expand All @@ -310,7 +307,7 @@ const AccountFormComponent = ({account, ...props}: Props & ConnectedProps & Disp
>
{chunks}
</a>
)) as any,
),
}}
/>
)}
Expand Down
7 changes: 7 additions & 0 deletions src/script/auth/component/ClientList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {connect} from 'react-redux';
import {useNavigate} from 'react-router-dom';
import {AnyAction, Dispatch} from 'redux';

import {UrlUtil} from '@wireapp/commons';
import {ContainerXS, Loading} from '@wireapp/react-ui-kit';

import {getLogger} from 'Util/Logger';
Expand Down Expand Up @@ -56,6 +57,7 @@ const ClientListComponent = ({
}: Props & ConnectedProps & DispatchProps) => {
const navigate = useNavigate();
const [showLoading, setShowLoading] = React.useState(false);
const isOauth = UrlUtil.hasURLParameter(QUERY_KEY.SCOPE);
const [currentlySelectedClient, setCurrentlySelectedClient] = React.useState<string | null>(null);

const setSelectedClient = (clientId: string) => {
Expand All @@ -76,6 +78,11 @@ const ClientListComponent = ({
await doInitializeClient(persist ? ClientType.PERMANENT : ClientType.TEMPORARY, password, SFAcode, entropy);
removeLocalStorage(QUERY_KEY.CONVERSATION_CODE);
removeLocalStorage(QUERY_KEY.JOIN_EXPIRES);

if (isOauth) {
return navigate(ROUTE.AUTHORIZE);
}

return navigate(ROUTE.HISTORY_INFO);
} catch (error) {
logger.error(error);
Expand Down
1 change: 0 additions & 1 deletion src/script/auth/component/UnsupportedBrowser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ export const UnsupportedBrowserComponent = ({
) : (
<H3 css={{marginBottom: 10}} data-uie-name="element-unsupported-general">
{_(unsupportedStrings.subheadBrowser, {
// eslint-disable-next-line react/display-name
strong: (...chunks: any[]) => <strong style={{fontWeight: 800}}>{chunks}</strong>,
})}
</H3>
Expand Down
3 changes: 1 addition & 2 deletions src/script/auth/component/WirelessContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ const WirelessContainer: React.FC<Props> = ({showCookiePolicyBanner, onCookiePol
{...cookiePolicyStrings.bannerText}
values={{
newline: <br />,
// eslint-disable-next-line react/display-name
strong: ((...chunks: any[]) => <strong>{chunks}</strong>) as any,
strong: (...chunks: any[]) => <strong>{chunks}</strong>,
}}
/>
</Link>
Expand Down
11 changes: 5 additions & 6 deletions src/script/auth/module/action/AuthAction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/

import {ClientType} from '@wireapp/api-client/lib/client/';
import {RecursivePartial} from '@wireapp/commons/lib/util/TypeUtil';
import {StatusCodes as HTTP_STATUS} from 'http-status-codes';

import type {APIClient} from '@wireapp/api-client';
Expand All @@ -28,7 +29,7 @@ import {AuthActionCreator} from './creator/';

import {mockStoreFactory} from '../../util/test/mockStoreFactory';

import {actionRoot} from './';
import {ActionRoot, actionRoot} from './';

describe('AuthAction', () => {
it('authenticates a user successfully', async () => {
Expand Down Expand Up @@ -129,14 +130,12 @@ describe('AuthAction', () => {
backendError.code = HTTP_STATUS.FORBIDDEN;
backendError.label = 'invalid-credentials';
backendError.message = 'Authentication failed.';
const spies = {
generateClientPayload: jasmine.createSpy().and.returnValue({}),
};

const mockedActions = {
clientAction: {
generateClientPayload: spies.generateClientPayload,
generateClientPayload: jest.fn().mockReturnValue({}),
},
};
} as RecursivePartial<ActionRoot>;
const mockedCore = {
login: () => Promise.reject(backendError),
};
Expand Down
45 changes: 45 additions & 0 deletions src/script/auth/module/action/AuthAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import type {DomainData} from '@wireapp/api-client/lib/account/DomainData';
import type {LoginData, RegisterData, SendLoginCode} from '@wireapp/api-client/lib/auth/';
import {VerificationActionType} from '@wireapp/api-client/lib/auth/VerificationActionType';
import {ClientType} from '@wireapp/api-client/lib/client/';
import {OAuthBody} from '@wireapp/api-client/lib/oauth/OAuthBody';
import {OAuthClient} from '@wireapp/api-client/lib/oauth/OAuthClient';
import type {TeamData} from '@wireapp/api-client/lib/team/';
import {LowDiskSpaceError} from '@wireapp/store-engine/lib/engine/error';
import {StatusCodes as HTTP_STATUS, StatusCodes} from 'http-status-codes';

Expand Down Expand Up @@ -145,6 +148,20 @@ export class AuthAction {
};
};

doPostOAuthCode = (oauthBody: OAuthBody): ThunkAction<Promise<string>> => {
return async (dispatch, getState, {apiClient}) => {
dispatch(AuthActionCreator.startSendOAuthCode());
try {
const url = await apiClient.api.oauth.postOAuthCode(oauthBody);
dispatch(AuthActionCreator.successfulSendOAuthCode());
return url;
} catch (error) {
dispatch(AuthActionCreator.failedSendOAuthCode(error));
throw error;
}
};
};

doSendTwoFactorLoginCode = (email: string): ThunkAction => {
return async (dispatch, getState, {apiClient}) => {
dispatch(AuthActionCreator.startSendTwoFactorCode());
Expand Down Expand Up @@ -185,6 +202,34 @@ export class AuthAction {
};
};

doGetTeamData = (teamId: string): ThunkAction<Promise<TeamData>> => {
return async (dispatch, getState, {apiClient}) => {
dispatch(AuthActionCreator.startFetchTeam());
try {
const teamData = await apiClient.api.teams.team.getTeam(teamId);
dispatch(AuthActionCreator.successfulFetchTeam(teamData));
return teamData;
} catch (error) {
dispatch(AuthActionCreator.failedFetchTeam(error));
throw error;
}
};
};

doGetOAuthApplication = (applicationId: string): ThunkAction<Promise<OAuthClient>> => {
return async (dispatch, getState, {apiClient}) => {
dispatch(AuthActionCreator.startFetchOAuth());
try {
const application = await apiClient.api.oauth.getClient(applicationId);
dispatch(AuthActionCreator.successfulFetchOAuth(application));
return application;
} catch (error) {
dispatch(AuthActionCreator.failedFetchOAuth(error));
throw error;
}
};
};

validateSSOCode = (code: string): ThunkAction => {
return async (dispatch, getState, {apiClient}) => {
const mapError = (error: any) => {
Expand Down
2 changes: 1 addition & 1 deletion src/script/auth/module/action/ClientAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export class ClientAction {
};
};

generateClientPayload = (clientType: ClientType): ClientInfo | undefined => {
private generateClientPayload = (clientType: ClientType): ClientInfo | undefined => {
if (clientType === ClientType.NONE) {
return undefined;
}
Expand Down

0 comments on commit 314696a

Please sign in to comment.