feat: Remove OIDC and SSO support
Existing SSO accounts have no local password, so the migration deactivates them before dropping is_sso_user and the identity_provider_user table.
This commit is contained in:
@@ -3,12 +3,11 @@
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { apply, fork, select, take } from 'redux-saga/effects';
|
||||
import { apply, fork, take } from 'redux-saga/effects';
|
||||
|
||||
import watchers from './watchers';
|
||||
import services from './services';
|
||||
import runWatchers from '../run-watchers';
|
||||
import selectors from '../../selectors';
|
||||
import { socket } from '../../api';
|
||||
import ActionTypes from '../../constants/ActionTypes';
|
||||
import Paths from '../../constants/Paths';
|
||||
@@ -21,17 +20,5 @@ export default function* coreSaga() {
|
||||
|
||||
yield take(ActionTypes.LOGOUT);
|
||||
|
||||
const oidcBootstrap = yield select(selectors.selectOidcBootstrap);
|
||||
|
||||
if (oidcBootstrap && oidcBootstrap.endSessionUrl !== null) {
|
||||
const currentUser = yield select(selectors.selectCurrentUser);
|
||||
|
||||
if (!currentUser || currentUser.isSsoUser) {
|
||||
// Redirect the user to the IDP to log out.
|
||||
window.location.href = oidcBootstrap.endSessionUrl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
window.location.href = Paths.LOGIN;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,6 @@ export function* handleLocationChange() {
|
||||
|
||||
switch (pathsMatch.pattern.path) {
|
||||
case Paths.LOGIN:
|
||||
case Paths.OIDC_CALLBACK:
|
||||
yield call(goToRoot);
|
||||
|
||||
break;
|
||||
|
||||
@@ -15,11 +15,7 @@ export default function* loginSaga() {
|
||||
|
||||
yield fork(services.initializeLogin);
|
||||
|
||||
yield take([
|
||||
ActionTypes.AUTHENTICATE__SUCCESS,
|
||||
ActionTypes.WITH_OIDC_AUTHENTICATE__SUCCESS,
|
||||
ActionTypes.TERMS_ACCEPT__SUCCESS,
|
||||
]);
|
||||
yield take([ActionTypes.AUTHENTICATE__SUCCESS, ActionTypes.TERMS_ACCEPT__SUCCESS]);
|
||||
|
||||
yield cancel(watcherTasks);
|
||||
yield call(services.goToRoot);
|
||||
|
||||
@@ -3,16 +3,13 @@
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { nanoid } from 'nanoid';
|
||||
import { call, put, select } from 'redux-saga/effects';
|
||||
import { replace } from '../../../lib/redux-router';
|
||||
|
||||
import selectors from '../../../selectors';
|
||||
import actions from '../../../actions';
|
||||
import api from '../../../api';
|
||||
import i18n from '../../../i18n';
|
||||
import { setAccessToken } from '../../../utils/access-token-storage';
|
||||
import Paths from '../../../constants/Paths';
|
||||
import AccessTokenSteps from '../../../constants/AccessTokenSteps';
|
||||
|
||||
export function* initializeLogin() {
|
||||
@@ -41,105 +38,6 @@ export function* authenticate(data) {
|
||||
yield put(actions.authenticate.success(accessToken));
|
||||
}
|
||||
|
||||
export function* authenticateWithOidc() {
|
||||
const oidcBootstrap = yield select(selectors.selectOidcBootstrap);
|
||||
|
||||
const state = nanoid();
|
||||
window.localStorage.setItem('oidc-state', state);
|
||||
|
||||
const nonce = nanoid();
|
||||
window.localStorage.setItem('oidc-nonce', nonce);
|
||||
|
||||
let redirectUrl = `${oidcBootstrap.authorizationUrl}`;
|
||||
redirectUrl += `&state=${encodeURIComponent(state)}`;
|
||||
redirectUrl += `&nonce=${encodeURIComponent(nonce)}`;
|
||||
|
||||
window.location.href = redirectUrl;
|
||||
}
|
||||
|
||||
export function* authenticateWithOidcCallback() {
|
||||
// https://github.com/plankanban/planka/issues/511#issuecomment-1771385639
|
||||
const params = new URLSearchParams(window.location.hash.substring(1) || window.location.search);
|
||||
|
||||
const state = window.localStorage.getItem('oidc-state');
|
||||
window.localStorage.removeItem('oidc-state');
|
||||
|
||||
const nonce = window.localStorage.getItem('oidc-nonce');
|
||||
window.localStorage.removeItem('oidc-nonce');
|
||||
|
||||
yield put(replace(Paths.LOGIN));
|
||||
|
||||
if (params.get('error') !== null) {
|
||||
yield put(
|
||||
actions.authenticateWithOidc.failure(
|
||||
new Error(
|
||||
`OIDC Authorization error: ${params.get('error')}: ${params.get('error_description')}`,
|
||||
),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const code = params.get('code');
|
||||
if (code === null) {
|
||||
yield put(
|
||||
actions.authenticateWithOidc.failure(new Error('Invalid OIDC response: no code parameter')),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.get('state') !== state) {
|
||||
yield put(
|
||||
actions.authenticateWithOidc.failure(
|
||||
new Error('Unable to process OIDC response: state mismatch'),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nonce === null) {
|
||||
yield put(
|
||||
actions.authenticateWithOidc.failure(
|
||||
new Error('Unable to process OIDC response: no nonce issued'),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const oidcBootstrap = yield select(selectors.selectOidcBootstrap);
|
||||
|
||||
if (oidcBootstrap?.debug) {
|
||||
const {
|
||||
included: { logs },
|
||||
} = yield call(api.debugOidc, {
|
||||
code,
|
||||
nonce,
|
||||
});
|
||||
|
||||
yield put(actions.authenticateWithOidc.debug(logs));
|
||||
return;
|
||||
}
|
||||
|
||||
let accessToken;
|
||||
try {
|
||||
({ item: accessToken } = yield call(api.exchangeForAccessTokenWithOidc, {
|
||||
code,
|
||||
nonce,
|
||||
}));
|
||||
} catch (error) {
|
||||
let terms;
|
||||
if (error.step === AccessTokenSteps.ACCEPT_TERMS) {
|
||||
({ item: terms } = yield call(api.getTerms, i18n.resolvedLanguage));
|
||||
}
|
||||
|
||||
yield put(actions.authenticateWithOidc.failure(error, terms));
|
||||
return;
|
||||
}
|
||||
|
||||
yield call(setAccessToken, accessToken);
|
||||
yield put(actions.authenticateWithOidc.success(accessToken));
|
||||
}
|
||||
|
||||
export function* clearAuthenticateError() {
|
||||
yield put(actions.clearAuthenticateError());
|
||||
}
|
||||
@@ -199,8 +97,6 @@ export function* updateTermsLanguage(value) {
|
||||
export default {
|
||||
initializeLogin,
|
||||
authenticate,
|
||||
authenticateWithOidc,
|
||||
authenticateWithOidcCallback,
|
||||
clearAuthenticateError,
|
||||
acceptTerms,
|
||||
cancelTerms,
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import { call, put, select, take } from 'redux-saga/effects';
|
||||
import { push } from '../../../lib/redux-router';
|
||||
|
||||
import { authenticateWithOidc, authenticateWithOidcCallback } from './login';
|
||||
import selectors from '../../../selectors';
|
||||
import ActionTypes from '../../../constants/ActionTypes';
|
||||
import Paths from '../../../constants/Paths';
|
||||
@@ -48,23 +47,6 @@ export function* handleLocationChange() {
|
||||
}
|
||||
|
||||
switch (pathsMatch.pattern.path) {
|
||||
case Paths.LOGIN: {
|
||||
const oidcBootstrap = yield select(selectors.selectOidcBootstrap);
|
||||
|
||||
if (oidcBootstrap) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
|
||||
if (params.has('authenticateWithOidc')) {
|
||||
yield call(authenticateWithOidc);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case Paths.OIDC_CALLBACK:
|
||||
yield call(authenticateWithOidcCallback);
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ export default function* loginWatchers() {
|
||||
takeEvery(EntryActionTypes.AUTHENTICATE, ({ payload: { data } }) =>
|
||||
services.authenticate(data),
|
||||
),
|
||||
takeEvery(EntryActionTypes.WITH_OIDC_AUTHENTICATE, () => services.authenticateWithOidc()),
|
||||
takeEvery(EntryActionTypes.AUTHENTICATE_ERROR_CLEAR, () => services.clearAuthenticateError()),
|
||||
takeEvery(EntryActionTypes.TERMS_ACCEPT, ({ payload: { signature } }) =>
|
||||
services.acceptTerms(signature),
|
||||
|
||||
Reference in New Issue
Block a user