accounts-frontend/packages/app/services/authFlow/CompleteState.ts

141 lines
4.9 KiB
TypeScript
Raw Normal View History

import { OAuthState } from 'app/components/auth/reducer';
import { getActiveAccount } from 'app/components/accounts/reducer';
2016-03-02 02:06:14 +05:30
import AbstractState from './AbstractState';
import LoginState from './LoginState';
import PermissionsState from './PermissionsState';
2016-11-13 20:17:56 +05:30
import ChooseAccountState from './ChooseAccountState';
2016-03-02 02:06:14 +05:30
import ActivationState from './ActivationState';
2016-08-03 00:29:29 +05:30
import AcceptRulesState from './AcceptRulesState';
import FinishState from './FinishState';
2019-12-07 16:58:52 +05:30
import { AuthContext } from './AuthFlow';
2016-03-02 02:06:14 +05:30
const PROMPT_ACCOUNT_CHOOSE = 'select_account';
const PROMPT_PERMISSIONS = 'consent';
function hasPrompt(prompt: OAuthState['prompt'], needle: string): boolean {
if (Array.isArray(prompt)) {
return prompt.includes(needle);
}
return prompt === needle;
}
2016-03-02 02:06:14 +05:30
export default class CompleteState extends AbstractState {
isPermissionsAccepted?: boolean;
2020-05-24 04:38:24 +05:30
constructor(
options: {
accept?: boolean;
} = {},
) {
super();
2019-12-07 16:58:52 +05:30
2020-05-24 04:38:24 +05:30
this.isPermissionsAccepted = options.accept;
2019-12-07 16:58:52 +05:30
}
2020-05-24 04:38:24 +05:30
enter(context: AuthContext): Promise<void> | void {
const {
auth: { credentials, oauth },
2020-05-24 04:38:24 +05:30
user,
} = context.getState();
if (credentials.isActivationRequired) {
2020-05-24 04:38:24 +05:30
context.setState(new ActivationState());
} else if (user.isGuest) {
context.setState(new LoginState());
} else if (user.shouldAcceptRules && !user.isDeleted) {
2020-05-24 04:38:24 +05:30
context.setState(new AcceptRulesState());
} else if (oauth?.params) {
2020-05-24 04:38:24 +05:30
return this.processOAuth(context);
} else {
context.navigate('/');
}
}
2020-05-24 04:38:24 +05:30
processOAuth(context: AuthContext): Promise<void> | void {
const { auth, accounts, user } = context.getState();
2020-05-24 04:38:24 +05:30
let { isSwitcherEnabled } = auth;
const { oauth } = auth;
if (!oauth) {
throw new Error('Empty oauth state');
}
const { loginHint } = oauth;
if (loginHint) {
const account = accounts.available.find(
(item) => item.id === Number(loginHint) || item.email === loginHint || item.username === loginHint,
);
const activeAccount = getActiveAccount(context.getState());
if (account) {
// disable switching, because we are know the account, user must be authorized with
context.run('setAccountSwitcher', false);
isSwitcherEnabled = false;
if (!activeAccount || account.id !== activeAccount.id) {
// lets switch user to an account, that is needed for auth
return context.run('authenticate', account).then(() => context.setState(new CompleteState()));
}
}
}
if (
isSwitcherEnabled &&
(accounts.available.length > 1 ||
// we are always showing account switcher for deleted users
// so that they can see, that their account was deleted
// (this info is displayed on switcher)
user.isDeleted ||
hasPrompt(oauth.prompt, PROMPT_ACCOUNT_CHOOSE))
) {
return context.setState(new ChooseAccountState());
}
if (user.isDeleted) {
// you shall not pass
// if we are here, this means that user have already seen account
// switcher and now we should redirect him to his profile,
// because oauth is not available for deleted accounts
return context.navigate('/');
}
2020-05-24 04:38:24 +05:30
if (oauth.code) {
return context.setState(new FinishState());
}
2020-05-24 04:38:24 +05:30
const data: Record<string, any> = {};
if (typeof this.isPermissionsAccepted !== 'undefined') {
data.accept = this.isPermissionsAccepted;
} else if (oauth.acceptRequired || hasPrompt(oauth.prompt, PROMPT_PERMISSIONS)) {
context.setState(new PermissionsState());
2020-05-24 04:38:24 +05:30
return;
2020-05-24 04:38:24 +05:30
}
// TODO: it seems that oAuthComplete may be a separate state
return context
.run('oAuthComplete', data)
.then((resp: { redirectUri?: string }) => {
// TODO: пусть в стейт попадает флаг или тип авторизации
// вместо волшебства над редирект урлой
if (!resp.redirectUri || resp.redirectUri.includes('static_page')) {
context.setState(new FinishState());
} else {
return context.run('redirect', resp.redirectUri);
}
})
.catch((resp) => {
if (resp.unauthorized) {
context.setState(new LoginState());
} else if (resp.acceptRequired) {
context.setState(new PermissionsState());
}
});
2016-03-02 02:06:14 +05:30
}
}