2023-10-19 14:39:21 +02:00
|
|
|
import { nanoid } from 'nanoid';
|
|
|
|
import { call, put, select } from 'redux-saga/effects';
|
2023-10-17 19:18:19 +02:00
|
|
|
import { replace } from '../../../lib/redux-router';
|
2019-08-31 04:07:25 +05:00
|
|
|
|
2023-10-17 19:18:19 +02:00
|
|
|
import selectors from '../../../selectors';
|
2022-08-04 13:31:14 +02:00
|
|
|
import actions from '../../../actions';
|
2021-06-24 01:05:22 +05:00
|
|
|
import api from '../../../api';
|
2022-09-07 18:39:33 +05:00
|
|
|
import { setAccessToken } from '../../../utils/access-token-storage';
|
2023-10-17 19:18:19 +02:00
|
|
|
import Paths from '../../../constants/Paths';
|
|
|
|
|
|
|
|
export function* initializeLogin() {
|
|
|
|
const { item: config } = yield call(api.getConfig); // TODO: handle error
|
|
|
|
|
|
|
|
yield put(actions.initializeLogin(config));
|
|
|
|
}
|
2019-08-31 04:07:25 +05:00
|
|
|
|
2022-08-04 13:31:14 +02:00
|
|
|
export function* authenticate(data) {
|
|
|
|
yield put(actions.authenticate(data));
|
2021-06-24 01:05:22 +05:00
|
|
|
|
2023-10-17 19:18:19 +02:00
|
|
|
let accessToken;
|
2021-06-24 01:05:22 +05:00
|
|
|
try {
|
2023-10-17 19:18:19 +02:00
|
|
|
({ item: accessToken } = yield call(api.createAccessToken, data));
|
2021-06-24 01:05:22 +05:00
|
|
|
} catch (error) {
|
2022-08-04 13:31:14 +02:00
|
|
|
yield put(actions.authenticate.failure(error));
|
2021-06-24 01:05:22 +05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-09-07 18:39:33 +05:00
|
|
|
yield call(setAccessToken, accessToken);
|
2022-08-04 13:31:14 +02:00
|
|
|
yield put(actions.authenticate.success(accessToken));
|
2019-08-31 04:07:25 +05:00
|
|
|
}
|
|
|
|
|
2023-10-19 16:05:34 +02:00
|
|
|
export function* authenticateUsingOidc() {
|
2023-10-17 19:18:19 +02:00
|
|
|
const oidcConfig = yield select(selectors.selectOidcConfig);
|
|
|
|
|
2023-12-09 17:06:45 +01:00
|
|
|
const state = nanoid();
|
|
|
|
window.sessionStorage.setItem('oidc-state', state);
|
|
|
|
|
2023-10-19 14:39:21 +02:00
|
|
|
const nonce = nanoid();
|
|
|
|
window.sessionStorage.setItem('oidc-nonce', nonce);
|
2023-12-09 17:06:45 +01:00
|
|
|
|
|
|
|
let redirectUrl = `${oidcConfig.authorizationUrl}`;
|
|
|
|
redirectUrl += `&state=${encodeURIComponent(state)}`;
|
|
|
|
redirectUrl += `&nonce=${encodeURIComponent(nonce)}`;
|
|
|
|
|
|
|
|
window.location.href = redirectUrl;
|
2023-10-17 19:18:19 +02:00
|
|
|
}
|
|
|
|
|
2023-10-19 16:05:34 +02:00
|
|
|
export function* authenticateUsingOidcCallback() {
|
2023-10-19 23:06:37 +02:00
|
|
|
// https://github.com/plankanban/planka/issues/511#issuecomment-1771385639
|
|
|
|
const params = new URLSearchParams(window.location.hash.substring(1) || window.location.search);
|
2023-10-19 16:05:34 +02:00
|
|
|
|
2023-12-09 17:06:45 +01:00
|
|
|
const state = window.sessionStorage.getItem('oidc-state');
|
|
|
|
window.sessionStorage.removeItem('oidc-state');
|
|
|
|
|
|
|
|
const nonce = window.sessionStorage.getItem('oidc-nonce');
|
|
|
|
window.sessionStorage.removeItem('oidc-nonce');
|
|
|
|
|
2023-10-19 16:05:34 +02:00
|
|
|
yield put(replace(Paths.LOGIN));
|
|
|
|
|
2023-10-19 14:39:21 +02:00
|
|
|
if (params.get('error') !== null) {
|
|
|
|
yield put(
|
2023-10-19 16:05:34 +02:00
|
|
|
actions.authenticateUsingOidc.failure(
|
2023-10-19 14:39:21 +02:00
|
|
|
new Error(
|
|
|
|
`OIDC Authorization error: ${params.get('error')}: ${params.get('error_description')}`,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
2023-10-17 19:18:19 +02:00
|
|
|
|
2023-12-09 17:06:45 +01:00
|
|
|
const code = params.get('code');
|
|
|
|
if (code === null) {
|
|
|
|
yield put(
|
|
|
|
actions.authenticateUsingOidc.failure(new Error('Invalid OIDC response: no code parameter')),
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (params.get('state') !== state) {
|
2023-10-19 14:39:21 +02:00
|
|
|
yield put(
|
2023-10-19 16:05:34 +02:00
|
|
|
actions.authenticateUsingOidc.failure(
|
2023-12-09 17:06:45 +01:00
|
|
|
new Error('Unable to process OIDC response: state mismatch'),
|
2023-10-19 14:39:21 +02:00
|
|
|
),
|
|
|
|
);
|
|
|
|
return;
|
2023-10-17 19:18:19 +02:00
|
|
|
}
|
|
|
|
|
2023-12-09 17:06:45 +01:00
|
|
|
if (nonce === null) {
|
2023-10-19 14:39:21 +02:00
|
|
|
yield put(
|
2023-12-09 17:06:45 +01:00
|
|
|
actions.authenticateUsingOidc.failure(
|
|
|
|
new Error('Unable to process OIDC response: no nonce issued'),
|
|
|
|
),
|
2023-10-19 14:39:21 +02:00
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-12-09 17:06:45 +01:00
|
|
|
let accessToken;
|
|
|
|
try {
|
|
|
|
({ item: accessToken } = yield call(api.exchangeForAccessTokenUsingOidc, {
|
|
|
|
code,
|
|
|
|
nonce,
|
|
|
|
}));
|
|
|
|
} catch (error) {
|
|
|
|
yield put(actions.authenticateUsingOidc.failure(error));
|
|
|
|
return;
|
2023-10-17 19:18:19 +02:00
|
|
|
}
|
2023-12-09 17:06:45 +01:00
|
|
|
|
|
|
|
yield call(setAccessToken, accessToken);
|
|
|
|
yield put(actions.authenticateUsingOidc.success(accessToken));
|
2023-10-17 19:18:19 +02:00
|
|
|
}
|
|
|
|
|
2022-08-04 13:31:14 +02:00
|
|
|
export function* clearAuthenticateError() {
|
|
|
|
yield put(actions.clearAuthenticateError());
|
2019-08-31 04:07:25 +05:00
|
|
|
}
|
2022-08-04 13:31:14 +02:00
|
|
|
|
|
|
|
export default {
|
2023-10-17 19:18:19 +02:00
|
|
|
initializeLogin,
|
2022-08-04 13:31:14 +02:00
|
|
|
authenticate,
|
2023-10-19 16:05:34 +02:00
|
|
|
authenticateUsingOidc,
|
|
|
|
authenticateUsingOidcCallback,
|
2022-08-04 13:31:14 +02:00
|
|
|
clearAuthenticateError,
|
|
|
|
};
|