Merge pull request #5578 from matrix-org/t3chguy/sso_fixes

Improve SSO auth flow
This commit is contained in:
Michael Telatynski 2021-01-29 13:28:53 +00:00 committed by GitHub
commit 62712159f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 228 additions and 63 deletions

View File

@ -34,7 +34,7 @@ limitations under the License.
h3 {
font-size: $font-14px;
font-weight: 600;
color: $authpage-primary-color;
color: $authpage-secondary-color;
}
h3.mx_AuthBody_centered {

View File

@ -28,8 +28,14 @@ limitations under the License.
.mx_SSOButton {
position: relative;
width: 100%;
padding-left: 32px;
padding-right: 32px;
padding: 7px 32px;
text-align: center;
border-radius: 8px;
display: inline-block;
font-size: $font-14px;
font-weight: $font-semi-bold;
border: 1px solid $input-border-color;
color: $primary-fg-color;
> img {
object-fit: contain;
@ -39,6 +45,16 @@ limitations under the License.
}
}
.mx_SSOButton_default {
color: $button-primary-bg-color;
background-color: $button-secondary-bg-color;
border-color: $button-primary-bg-color;
}
.mx_SSOButton_default.mx_SSOButton_primary {
color: $button-primary-fg-color;
background-color: $button-primary-bg-color;
}
.mx_SSOButton_mini {
box-sizing: border-box;
width: 50px; // 48px + 1px border on all sides
@ -56,3 +72,14 @@ limitations under the License.
}
}
}
.mx_SSOButton.mx_SSOButton_brand_facebook {
background-color: #3c5a99;
border-color: #3c5a99;
color: #ffffff;
}
.mx_SSOButton.mx_SSOButton_brand_twitter {
background-color: #47acdf;
border-color: #47acdf;
color: #ffffff;
}

View File

@ -0,0 +1,3 @@
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.9803 1.2796C17.0771 2.54383 16.6773 3.79601 15.8657 4.77022C15.0784 5.74949 13.8854 6.31354 12.629 6.3006C12.5491 5.07276 12.9605 3.86352 13.7727 2.93921C14.5952 2.00238 15.7404 1.40982 16.9803 1.2796ZM20.9539 8.70795C19.5086 9.59652 18.6192 11.1635 18.5974 12.86C18.5994 14.7794 19.7489 16.5115 21.5166 17.2592C21.1766 18.3636 20.6642 19.4073 19.9982 20.3517C19.1038 21.6896 18.1661 22.9967 16.6777 23.0208C15.9698 23.0372 15.492 22.8336 14.9941 22.6215C14.4747 22.4003 13.9335 22.1697 13.0867 22.1697C12.1885 22.1697 11.6231 22.4077 11.0778 22.6372C10.6065 22.8355 10.1503 23.0275 9.50727 23.0542C8.08982 23.1067 7.00654 21.6263 6.07964 20.3009C4.22703 17.5943 2.78444 12.6733 4.71844 9.32483C5.62662 7.69286 7.32468 6.65727 9.19136 6.59696C9.99528 6.58042 10.7667 6.89028 11.443 7.16193C11.9602 7.36969 12.4219 7.5551 12.7999 7.5551C13.1321 7.5551 13.5809 7.37701 14.1038 7.16946C14.9276 6.84251 15.9356 6.44246 16.9628 6.55027C18.5589 6.60021 20.038 7.39984 20.9539 8.70795Z" fill="#17191C"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,4 @@
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.9431 22.5022C22.5832 22.5022 23.1022 21.9832 23.1022 21.343V2.65915C23.1022 2.01886 22.5832 1.5 21.9431 1.5H3.25914C2.61884 1.5 2.09998 2.01886 2.09998 2.65915V21.343C2.09998 21.9831 2.61876 22.5022 3.25914 22.5022H21.9431Z" fill="white"/>
<path d="M16.5913 22.5022V14.369H19.3212L19.73 11.1993H16.5913V9.17572C16.5913 8.25803 16.8461 7.63266 18.1621 7.63266L19.8405 7.63192V4.79696C19.5502 4.75833 18.5539 4.67203 17.3947 4.67203C14.9748 4.67203 13.318 6.14917 13.318 8.86181V11.1993H10.5811V14.369H13.318V22.5022H16.5913Z" fill="#3C5A99"/>
</svg>

After

Width:  |  Height:  |  Size: 658 B

View File

@ -0,0 +1,3 @@
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.8421 7.10595C19.9703 5.6121 18.7876 4.42942 17.2939 3.55764C15.8 2.68581 14.169 2.25001 12.3999 2.25001C10.6311 2.25001 8.9996 2.68594 7.50597 3.55764C6.01212 4.42938 4.82953 5.6121 3.95765 7.10595C3.08592 8.59976 2.65002 10.231 2.65002 11.9997C2.65002 14.1242 3.26987 16.0346 4.50987 17.7315C5.74973 19.4284 7.35145 20.6027 9.3149 21.2543C9.54345 21.2967 9.71264 21.2669 9.82265 21.1656C9.9327 21.0641 9.98766 20.937 9.98766 20.7848C9.98766 20.7595 9.98548 20.531 9.98126 20.0993C9.9769 19.6676 9.97485 19.291 9.97485 18.9696L9.68285 19.0202C9.49667 19.0543 9.26181 19.0687 8.97826 19.0646C8.69484 19.0607 8.40061 19.031 8.09598 18.9757C7.79121 18.921 7.50775 18.7941 7.24536 18.5952C6.98311 18.3963 6.79693 18.1359 6.68688 17.8145L6.55993 17.5224C6.47531 17.3279 6.3421 17.1119 6.1601 16.875C5.97811 16.638 5.79406 16.4773 5.60789 16.3927L5.519 16.329C5.45978 16.2868 5.40482 16.2358 5.35399 16.1766C5.30321 16.1174 5.2652 16.0582 5.23981 15.9988C5.21437 15.9395 5.23545 15.8908 5.30326 15.8526C5.37107 15.8144 5.49361 15.7959 5.67143 15.7959L5.92524 15.8338C6.09451 15.8677 6.3039 15.9691 6.55366 16.1384C6.80329 16.3077 7.0085 16.5277 7.16933 16.7984C7.36408 17.1455 7.59873 17.4099 7.87392 17.5919C8.14889 17.7739 8.42613 17.8648 8.70537 17.8648C8.98461 17.8648 9.22579 17.8436 9.429 17.8015C9.63198 17.7592 9.82243 17.6955 10.0002 17.611C10.0764 17.0437 10.2838 16.6079 10.6222 16.3033C10.1399 16.2526 9.70619 16.1762 9.32099 16.0747C8.93601 15.9731 8.53818 15.8081 8.12777 15.5794C7.71714 15.3509 7.37649 15.0673 7.10574 14.7289C6.83495 14.3904 6.61271 13.9459 6.43934 13.3959C6.26588 12.8457 6.17913 12.211 6.17913 11.4916C6.17913 10.4674 6.51351 9.59578 7.18213 8.87633C6.86892 8.10629 6.89849 7.24304 7.27093 6.28668C7.51638 6.21043 7.88037 6.26765 8.36273 6.45801C8.84517 6.64845 9.1984 6.8116 9.42277 6.94686C9.64714 7.08208 9.82692 7.19666 9.96236 7.2896C10.7496 7.06963 11.562 6.95962 12.3998 6.95962C13.2377 6.95962 14.0503 7.06963 14.8376 7.2896L15.32 6.98505C15.6498 6.78185 16.0394 6.59563 16.4877 6.42635C16.9363 6.25716 17.2793 6.21056 17.5164 6.28682C17.8971 7.24322 17.931 8.10642 17.6177 8.87647C18.2863 9.59591 18.6208 10.4677 18.6208 11.4918C18.6208 12.2111 18.5337 12.8478 18.3605 13.4023C18.1871 13.9568 17.963 14.4008 17.688 14.7353C17.4127 15.0697 17.0699 15.3511 16.6595 15.5795C16.249 15.808 15.851 15.973 15.466 16.0747C15.0809 16.1763 14.6472 16.2527 14.1648 16.3035C14.6048 16.6842 14.8248 17.2851 14.8248 18.106V20.7845C14.8248 20.9367 14.8777 21.0637 14.9836 21.1652C15.0894 21.2665 15.2565 21.2964 15.485 21.2539C17.4487 20.6024 19.0505 19.4281 20.2903 17.7311C21.53 16.0343 22.15 14.1238 22.15 11.9993C22.1496 10.2309 21.7135 8.59976 20.8421 7.10595Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,9 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.0005 20.3296L15.3166 10.1293H8.68921L12.0005 20.3296Z" fill="#E24329"/>
<path d="M4.04348 10.1293L3.03364 13.2283C2.94226 13.5097 3.04095 13.8203 3.28214 13.9957L11.9996 20.3296L4.04348 10.1293Z" fill="#FCA326"/>
<path d="M4.04248 10.1289H8.68727L6.68828 3.98572C6.58597 3.67143 6.1401 3.67143 6.03411 3.98572L4.04248 10.1289Z" fill="#E24329"/>
<path d="M19.9602 10.1293L20.9664 13.2283C21.0577 13.5097 20.9591 13.8203 20.7179 13.9957L11.9991 20.3296L19.9602 10.1293Z" fill="#FCA326"/>
<path d="M19.9616 10.1289H15.3168L17.3121 3.98572C17.4144 3.67143 17.8603 3.67143 17.9663 3.98572L19.9616 10.1289Z" fill="#E24329"/>
<path d="M11.9991 20.3296L15.3153 10.1293H19.9601L11.9991 20.3296Z" fill="#FC6D26"/>
<path d="M11.9985 20.3296L4.04248 10.1293H8.68727L11.9985 20.3296Z" fill="#FC6D26"/>
</svg>

After

Width:  |  Height:  |  Size: 905 B

View File

@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M22.501 12.2333C22.501 11.37 22.4296 10.74 22.2748 10.0867H12.2153V13.9833H18.12C18.001 14.9517 17.3582 16.41 15.9296 17.3899L15.9096 17.5204L19.0902 19.9351L19.3106 19.9567C21.3343 18.125 22.501 15.43 22.501 12.2333Z" fill="#4285F4"/>
<path d="M12.2147 22.5001C15.1075 22.5001 17.5361 21.5667 19.3099 19.9567L15.929 17.39C15.0242 18.0083 13.8099 18.44 12.2147 18.44C9.38142 18.44 6.97669 16.6083 6.11947 14.0767L5.99382 14.0871L2.68656 16.5955L2.64331 16.7133C4.40519 20.1433 8.02423 22.5001 12.2147 22.5001Z" fill="#34A853"/>
<path d="M6.12022 14.0767C5.89403 13.4234 5.76313 12.7233 5.76313 12C5.76313 11.2767 5.89403 10.5767 6.10832 9.92337L6.10233 9.78423L2.75361 7.2356L2.64405 7.28667C1.91789 8.71002 1.50122 10.3084 1.50122 12C1.50122 13.6917 1.91789 15.29 2.64405 16.7133L6.12022 14.0767Z" fill="#FBBC05"/>
<path d="M12.2148 5.55997C14.2267 5.55997 15.5838 6.41163 16.3576 7.12335L19.3814 4.23C17.5243 2.53834 15.1076 1.5 12.2148 1.5C8.02426 1.5 4.4052 3.85665 2.64331 7.28662L6.10759 9.92332C6.97671 7.39166 9.38146 5.55997 12.2148 5.55997Z" fill="#EB4335"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,3 @@
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.04151 21C6.61526 21 4.35358 20.2943 2.44995 19.0767C4.0662 19.1813 6.9185 18.9308 8.69264 17.2386C6.02376 17.1161 4.82015 15.0692 4.66316 14.1945C4.88992 14.2819 5.97143 14.3869 6.58195 14.142C3.51187 13.3722 3.0409 10.678 3.14556 9.85573C3.7212 10.2581 4.69804 10.3981 5.0818 10.3631C2.22105 8.31618 3.25022 5.23707 3.75609 4.57226C5.80907 7.4165 8.88585 9.01393 12.6922 9.10278C12.6205 8.78802 12.5826 8.46032 12.5826 8.12373C12.5826 5.70819 14.535 3.75 16.9435 3.75C18.2019 3.75 19.3358 4.28457 20.1317 5.13963C20.9726 4.94258 22.2382 4.4813 22.8569 4.0824C22.545 5.20208 21.5742 6.13612 20.9869 6.48231C20.9918 6.49408 20.9821 6.47048 20.9869 6.48231C21.5027 6.40428 22.8985 6.13603 23.45 5.76192C23.1773 6.39094 22.1479 7.4368 21.3032 8.02232C21.4604 14.9535 16.1573 21 9.04151 21Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 918 B

View File

@ -366,7 +366,7 @@ async function abortLogin() {
// The plan is to gradually move the localStorage access done here into
// SessionStore to avoid bugs where the view becomes out-of-sync with
// localStorage (e.g. isGuest etc.)
async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }): Promise<boolean> {
export async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }): Promise<boolean> {
const ignoreGuest = opts?.ignoreGuest;
if (!localStorage) {

View File

@ -33,10 +33,20 @@ interface IPasswordFlow {
type: "m.login.password";
}
export enum IdentityProviderBrand {
Gitlab = "org.matrix.gitlab",
Github = "org.matrix.github",
Apple = "org.matrix.apple",
Google = "org.matrix.google",
Facebook = "org.matrix.facebook",
Twitter = "org.matrix.twitter",
}
export interface IIdentityProvider {
id: string;
name: string;
icon?: string;
brand?: IdentityProviderBrand | string;
}
export interface ISSOFlow {

View File

@ -177,7 +177,14 @@ export default class InteractiveAuthComponent extends React.Component {
stageState: stageState,
errorText: stageState.error,
}, () => {
if (oldStage != stageType) this._setFocus();
if (oldStage !== stageType) {
this._setFocus();
} else if (
!stageState.error && this._stageComponent.current &&
this._stageComponent.current.attemptFailed
) {
this._stageComponent.current.attemptFailed();
}
});
};

View File

@ -219,6 +219,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
private screenAfterLogin?: IScreen;
private windowWidth: number;
private pageChanging: boolean;
private tokenLogin?: boolean;
private accountPassword?: string;
private accountPasswordTimer?: NodeJS.Timeout;
private focusComposer: boolean;
@ -324,13 +325,20 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
Lifecycle.attemptTokenLogin(
this.props.realQueryParams,
this.props.defaultDeviceDisplayName,
).then((loggedIn) => {
if (loggedIn) {
).then(async (loggedIn) => {
if (this.props.realQueryParams?.loginToken) {
// remove the loginToken from the URL regardless
this.props.onTokenLoginCompleted();
}
// don't do anything else until the page reloads - just stay in
// the 'loading' state.
return;
if (loggedIn) {
this.tokenLogin = true;
// Create and start the client
await Lifecycle.restoreFromLocalStorage({
ignoreGuest: true,
});
return this.postLoginSetup();
}
// if the user has followed a login or register link, don't reanimate
@ -354,6 +362,42 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
CountlyAnalytics.instance.enable(/* anonymous = */ true);
}
private async postLoginSetup() {
const cli = MatrixClientPeg.get();
const cryptoEnabled = cli.isCryptoEnabled();
if (!cryptoEnabled) {
this.onLoggedIn();
}
const promisesList = [this.firstSyncPromise.promise];
if (cryptoEnabled) {
// wait for the client to finish downloading cross-signing keys for us so we
// know whether or not we have keys set up on this account
promisesList.push(cli.downloadKeys([cli.getUserId()]));
}
// Now update the state to say we're waiting for the first sync to complete rather
// than for the login to finish.
this.setState({ pendingInitialSync: true });
await Promise.all(promisesList);
if (!cryptoEnabled) {
this.setState({ pendingInitialSync: false });
return;
}
const crossSigningIsSetUp = cli.getStoredCrossSigningForUser(cli.getUserId());
if (crossSigningIsSetUp) {
this.setStateForNewView({ view: Views.COMPLETE_SECURITY });
} else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) {
this.setStateForNewView({ view: Views.E2E_SETUP });
} else {
this.onLoggedIn();
}
this.setState({ pendingInitialSync: false });
}
// TODO: [REACT-WARNING] Replace with appropriate lifecycle stage
// eslint-disable-next-line camelcase
UNSAFE_componentWillUpdate(props, state) {
@ -1839,40 +1883,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// Create and start the client
await Lifecycle.setLoggedIn(credentials);
const cli = MatrixClientPeg.get();
const cryptoEnabled = cli.isCryptoEnabled();
if (!cryptoEnabled) {
this.onLoggedIn();
}
const promisesList = [this.firstSyncPromise.promise];
if (cryptoEnabled) {
// wait for the client to finish downloading cross-signing keys for us so we
// know whether or not we have keys set up on this account
promisesList.push(cli.downloadKeys([cli.getUserId()]));
}
// Now update the state to say we're waiting for the first sync to complete rather
// than for the login to finish.
this.setState({ pendingInitialSync: true });
await Promise.all(promisesList);
if (!cryptoEnabled) {
this.setState({ pendingInitialSync: false });
return;
}
const crossSigningIsSetUp = cli.getStoredCrossSigningForUser(cli.getUserId());
if (crossSigningIsSetUp) {
this.setStateForNewView({ view: Views.COMPLETE_SECURITY });
} else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) {
this.setStateForNewView({ view: Views.E2E_SETUP });
} else {
this.onLoggedIn();
}
this.setState({ pendingInitialSync: false });
await this.postLoginSetup();
};
// complete security / e2e setup has finished
@ -1916,6 +1927,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
<E2eSetup
onFinished={this.onCompleteSecurityE2eSetupFinished}
accountPassword={this.accountPassword}
tokenLogin={!!this.tokenLogin}
/>
);
} else if (this.state.view === Views.LOGGED_IN) {

View File

@ -24,6 +24,7 @@ export default class E2eSetup extends React.Component {
static propTypes = {
onFinished: PropTypes.func.isRequired,
accountPassword: PropTypes.string,
tokenLogin: PropTypes.bool,
};
render() {
@ -33,6 +34,7 @@ export default class E2eSetup extends React.Component {
<CreateCrossSigningDialog
onFinished={this.props.onFinished}
accountPassword={this.props.accountPassword}
tokenLogin={this.props.tokenLogin}
/>
</CompleteSecurityBody>
</AuthPage>

View File

@ -609,8 +609,12 @@ export class SSOAuthEntry extends React.Component {
this.props.authSessionId,
);
this._popupWindow = null;
window.addEventListener("message", this._onReceiveMessage);
this.state = {
phase: SSOAuthEntry.PHASE_PREAUTH,
attemptFailed: false,
};
}
@ -618,12 +622,35 @@ export class SSOAuthEntry extends React.Component {
this.props.onPhaseChange(SSOAuthEntry.PHASE_PREAUTH);
}
componentWillUnmount() {
window.removeEventListener("message", this._onReceiveMessage);
if (this._popupWindow) {
this._popupWindow.close();
this._popupWindow = null;
}
}
attemptFailed = () => {
this.setState({
attemptFailed: true,
});
};
_onReceiveMessage = event => {
if (event.data === "authDone" && event.origin === this.props.matrixClient.getHomeserverUrl()) {
if (this._popupWindow) {
this._popupWindow.close();
this._popupWindow = null;
}
}
};
onStartAuthClick = () => {
// Note: We don't use PlatformPeg's startSsoAuth functions because we almost
// certainly will need to open the thing in a new tab to avoid losing application
// context.
window.open(this._ssoUrl, '_blank');
this._popupWindow = window.open(this._ssoUrl, "_blank");
this.setState({phase: SSOAuthEntry.PHASE_POSTAUTH});
this.props.onPhaseChange(SSOAuthEntry.PHASE_POSTAUTH);
};
@ -656,10 +683,28 @@ export class SSOAuthEntry extends React.Component {
);
}
return <div className='mx_InteractiveAuthEntryComponents_sso_buttons'>
{cancelButton}
{continueButton}
</div>;
let errorSection;
if (this.props.errorText) {
errorSection = (
<div className="error" role="alert">
{ this.props.errorText }
</div>
);
} else if (this.state.attemptFailed) {
errorSection = (
<div className="error" role="alert">
{ _t("Something went wrong in confirming your identity. Cancel and try again.") }
</div>
);
}
return <React.Fragment>
{ errorSection }
<div className="mx_InteractiveAuthEntryComponents_sso_buttons">
{cancelButton}
{continueButton}
</div>
</React.Fragment>;
}
}
@ -710,8 +755,7 @@ export class FallbackAuthEntry extends React.Component {
this.props.loginType,
this.props.authSessionId,
);
this._popupWindow = window.open(url);
this._popupWindow.opener = null;
this._popupWindow = window.open(url, "_blank");
};
_onReceiveMessage = event => {

View File

@ -34,6 +34,7 @@ import InteractiveAuthDialog from '../InteractiveAuthDialog';
export default class CreateCrossSigningDialog extends React.PureComponent {
static propTypes = {
accountPassword: PropTypes.string,
tokenLogin: PropTypes.bool,
};
constructor(props) {
@ -96,6 +97,9 @@ export default class CreateCrossSigningDialog extends React.PureComponent {
user: MatrixClientPeg.get().getUserId(),
password: this.state.accountPassword,
});
} else if (this.props.tokenLogin) {
// We are hoping the grace period is active
await makeRequest({});
} else {
const dialogAesthetics = {
[SSOAuthEntry.PHASE_PREAUTH]: {
@ -144,6 +148,12 @@ export default class CreateCrossSigningDialog extends React.PureComponent {
});
this.props.onFinished(true);
} catch (e) {
if (this.props.tokenLogin) {
// ignore any failures, we are relying on grace period here
this.props.onFinished();
return;
}
this.setState({ error: e });
console.error("Error bootstrapping cross-signing", e);
}

View File

@ -22,13 +22,33 @@ import {MatrixClient} from "matrix-js-sdk/src/client";
import PlatformPeg from "../../../PlatformPeg";
import AccessibleButton from "./AccessibleButton";
import {_t} from "../../../languageHandler";
import {IIdentityProvider, ISSOFlow} from "../../../Login";
import {IdentityProviderBrand, IIdentityProvider, ISSOFlow} from "../../../Login";
import AccessibleTooltipButton from "./AccessibleTooltipButton";
interface ISSOButtonProps extends Omit<IProps, "flow"> {
idp: IIdentityProvider;
mini?: boolean;
}
const getIcon = (brand: IdentityProviderBrand | string) => {
switch (brand) {
case IdentityProviderBrand.Apple:
return require(`../../../../res/img/element-icons/brands/apple.svg`);
case IdentityProviderBrand.Facebook:
return require(`../../../../res/img/element-icons/brands/facebook.svg`);
case IdentityProviderBrand.Github:
return require(`../../../../res/img/element-icons/brands/github.svg`);
case IdentityProviderBrand.Gitlab:
return require(`../../../../res/img/element-icons/brands/gitlab.svg`);
case IdentityProviderBrand.Google:
return require(`../../../../res/img/element-icons/brands/google.svg`);
case IdentityProviderBrand.Twitter:
return require(`../../../../res/img/element-icons/brands/twitter.svg`);
default:
return null;
}
}
const SSOButton: React.FC<ISSOButtonProps> = ({
matrixClient,
loginType,
@ -38,7 +58,6 @@ const SSOButton: React.FC<ISSOButtonProps> = ({
mini,
...props
}) => {
const kind = primary ? "primary" : "primary_outline";
const label = idp ? _t("Continue with %(provider)s", { provider: idp.name }) : _t("Sign in with single sign-on");
const onClick = () => {
@ -46,30 +65,35 @@ const SSOButton: React.FC<ISSOButtonProps> = ({
};
let icon;
if (typeof idp?.icon === "string" && (idp.icon.startsWith("mxc://") || idp.icon.startsWith("https://"))) {
icon = <img
src={matrixClient.mxcUrlToHttp(idp.icon, 24, 24, "crop", true)}
height="24"
width="24"
alt={label}
/>;
let brandClass;
const brandIcon = idp ? getIcon(idp.brand) : null;
if (brandIcon) {
const brandName = idp.brand.split(".").pop();
brandClass = `mx_SSOButton_brand_${brandName}`;
icon = <img src={brandIcon} height="24" width="24" alt={brandName} />;
} else if (typeof idp?.icon === "string" && idp.icon.startsWith("mxc://")) {
const src = matrixClient.mxcUrlToHttp(idp.icon, 24, 24, "crop", true);
icon = <img src={src} height="24" width="24" alt={idp.name} />;
}
const classes = classNames("mx_SSOButton", {
[brandClass]: brandClass,
mx_SSOButton_mini: mini,
mx_SSOButton_default: !idp,
mx_SSOButton_primary: primary,
});
if (mini) {
// TODO fallback icon
return (
<AccessibleButton {...props} className={classes} kind={kind} onClick={onClick}>
<AccessibleTooltipButton {...props} title={label} className={classes} onClick={onClick}>
{ icon }
</AccessibleButton>
</AccessibleTooltipButton>
);
}
return (
<AccessibleButton {...props} className={classes} kind={kind} onClick={onClick}>
<AccessibleButton {...props} className={classes} onClick={onClick}>
{ icon }
{ label }
</AccessibleButton>

View File

@ -2339,6 +2339,7 @@
"Please enter the code it contains:": "Please enter the code it contains:",
"Code": "Code",
"Submit": "Submit",
"Something went wrong in confirming your identity. Cancel and try again.": "Something went wrong in confirming your identity. Cancel and try again.",
"Start authentication": "Start authentication",
"Enter password": "Enter password",
"Nice, strong password!": "Nice, strong password!",