mirror of
https://github.com/plankanban/planka.git
synced 2025-07-19 21:29:43 +02:00
ref: Refactoring
This commit is contained in:
parent
9011ee61da
commit
0fab6075bd
23 changed files with 96 additions and 91 deletions
|
@ -28,20 +28,20 @@ authenticate.failure = (error) => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const authenticateWithOidc = () => ({
|
const authenticateUsingOidc = () => ({
|
||||||
type: ActionTypes.WITH_OIDC_AUTHENTICATE,
|
type: ActionTypes.USING_OIDC_AUTHENTICATE,
|
||||||
payload: {},
|
payload: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
authenticateWithOidc.success = (accessToken) => ({
|
authenticateUsingOidc.success = (accessToken) => ({
|
||||||
type: ActionTypes.WITH_OIDC_AUTHENTICATE__SUCCESS,
|
type: ActionTypes.USING_OIDC_AUTHENTICATE__SUCCESS,
|
||||||
payload: {
|
payload: {
|
||||||
accessToken,
|
accessToken,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
authenticateWithOidc.failure = (error) => ({
|
authenticateUsingOidc.failure = (error) => ({
|
||||||
type: ActionTypes.WITH_OIDC_AUTHENTICATE__FAILURE,
|
type: ActionTypes.USING_OIDC_AUTHENTICATE__FAILURE,
|
||||||
payload: {
|
payload: {
|
||||||
error,
|
error,
|
||||||
},
|
},
|
||||||
|
@ -55,6 +55,6 @@ const clearAuthenticateError = () => ({
|
||||||
export default {
|
export default {
|
||||||
initializeLogin,
|
initializeLogin,
|
||||||
authenticate,
|
authenticate,
|
||||||
authenticateWithOidc,
|
authenticateUsingOidc,
|
||||||
clearAuthenticateError,
|
clearAuthenticateError,
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,14 +5,14 @@ import socket from './socket';
|
||||||
|
|
||||||
const createAccessToken = (data, headers) => http.post('/access-tokens', data, headers);
|
const createAccessToken = (data, headers) => http.post('/access-tokens', data, headers);
|
||||||
|
|
||||||
const exchangeToAccessToken = (data, headers) =>
|
const exchangeForAccessTokenUsingOidc = (data, headers) =>
|
||||||
http.post('/access-tokens/exchange', data, headers);
|
http.post('/access-tokens/exchange-using-oidc', data, headers);
|
||||||
|
|
||||||
const deleteCurrentAccessToken = (headers) =>
|
const deleteCurrentAccessToken = (headers) =>
|
||||||
socket.delete('/access-tokens/me', undefined, headers);
|
socket.delete('/access-tokens/me', undefined, headers);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
createAccessToken,
|
createAccessToken,
|
||||||
exchangeToAccessToken,
|
exchangeForAccessTokenUsingOidc,
|
||||||
deleteCurrentAccessToken,
|
deleteCurrentAccessToken,
|
||||||
};
|
};
|
||||||
|
|
|
@ -65,11 +65,11 @@ const Login = React.memo(
|
||||||
({
|
({
|
||||||
defaultData,
|
defaultData,
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
isSubmittingWithOidc,
|
isSubmittingUsingOidc,
|
||||||
error,
|
error,
|
||||||
withOidc,
|
withOidc,
|
||||||
onAuthenticate,
|
onAuthenticate,
|
||||||
onAuthenticateWithOidc,
|
onAuthenticateUsingOidc,
|
||||||
onMessageDismiss,
|
onMessageDismiss,
|
||||||
}) => {
|
}) => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
@ -192,15 +192,15 @@ const Login = React.memo(
|
||||||
content={t('action.logIn')}
|
content={t('action.logIn')}
|
||||||
floated="right"
|
floated="right"
|
||||||
loading={isSubmitting}
|
loading={isSubmitting}
|
||||||
disabled={isSubmitting || isSubmittingWithOidc}
|
disabled={isSubmitting || isSubmittingUsingOidc}
|
||||||
/>
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
{withOidc && (
|
{withOidc && (
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
loading={isSubmittingWithOidc}
|
loading={isSubmittingUsingOidc}
|
||||||
disabled={isSubmitting || isSubmittingWithOidc}
|
disabled={isSubmitting || isSubmittingUsingOidc}
|
||||||
onClick={onAuthenticateWithOidc}
|
onClick={onAuthenticateUsingOidc}
|
||||||
>
|
>
|
||||||
{t('action.logInWithSSO')}
|
{t('action.logInWithSSO')}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -239,11 +239,11 @@ Login.propTypes = {
|
||||||
defaultData: PropTypes.object.isRequired,
|
defaultData: PropTypes.object.isRequired,
|
||||||
/* eslint-enable react/forbid-prop-types */
|
/* eslint-enable react/forbid-prop-types */
|
||||||
isSubmitting: PropTypes.bool.isRequired,
|
isSubmitting: PropTypes.bool.isRequired,
|
||||||
isSubmittingWithOidc: PropTypes.bool.isRequired,
|
isSubmittingUsingOidc: PropTypes.bool.isRequired,
|
||||||
error: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
error: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||||
withOidc: PropTypes.bool.isRequired,
|
withOidc: PropTypes.bool.isRequired,
|
||||||
onAuthenticate: PropTypes.func.isRequired,
|
onAuthenticate: PropTypes.func.isRequired,
|
||||||
onAuthenticateWithOidc: PropTypes.func.isRequired,
|
onAuthenticateUsingOidc: PropTypes.func.isRequired,
|
||||||
onMessageDismiss: PropTypes.func.isRequired,
|
onMessageDismiss: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,9 @@ export default {
|
||||||
AUTHENTICATE: 'AUTHENTICATE',
|
AUTHENTICATE: 'AUTHENTICATE',
|
||||||
AUTHENTICATE__SUCCESS: 'AUTHENTICATE__SUCCESS',
|
AUTHENTICATE__SUCCESS: 'AUTHENTICATE__SUCCESS',
|
||||||
AUTHENTICATE__FAILURE: 'AUTHENTICATE__FAILURE',
|
AUTHENTICATE__FAILURE: 'AUTHENTICATE__FAILURE',
|
||||||
WITH_OIDC_AUTHENTICATE: 'WITH_OIDC_AUTHENTICATE',
|
USING_OIDC_AUTHENTICATE: 'USING_OIDC_AUTHENTICATE',
|
||||||
WITH_OIDC_AUTHENTICATE__SUCCESS: 'WITH_OIDC_AUTHENTICATE__SUCCESS',
|
USING_OIDC_AUTHENTICATE__SUCCESS: 'USING_OIDC_AUTHENTICATE__SUCCESS',
|
||||||
WITH_OIDC_AUTHENTICATE__FAILURE: 'WITH_OIDC_AUTHENTICATE__FAILURE',
|
USING_OIDC_AUTHENTICATE__FAILURE: 'USING_OIDC_AUTHENTICATE__FAILURE',
|
||||||
AUTHENTICATE_ERROR_CLEAR: 'AUTHENTICATE_ERROR_CLEAR',
|
AUTHENTICATE_ERROR_CLEAR: 'AUTHENTICATE_ERROR_CLEAR',
|
||||||
|
|
||||||
/* Core */
|
/* Core */
|
||||||
|
|
|
@ -11,7 +11,7 @@ export default {
|
||||||
/* Login */
|
/* Login */
|
||||||
|
|
||||||
AUTHENTICATE: `${PREFIX}/AUTHENTICATE`,
|
AUTHENTICATE: `${PREFIX}/AUTHENTICATE`,
|
||||||
WITH_OIDC_AUTHENTICATE: `${PREFIX}/WITH_OIDC_AUTHENTICATE`,
|
USING_OIDC_AUTHENTICATE: `${PREFIX}/USING_OIDC_AUTHENTICATE`,
|
||||||
AUTHENTICATE_ERROR_CLEAR: `${PREFIX}/AUTHENTICATE_ERROR_CLEAR`,
|
AUTHENTICATE_ERROR_CLEAR: `${PREFIX}/AUTHENTICATE_ERROR_CLEAR`,
|
||||||
|
|
||||||
/* Core */
|
/* Core */
|
||||||
|
|
|
@ -10,14 +10,14 @@ const mapStateToProps = (state) => {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
ui: {
|
ui: {
|
||||||
authenticateForm: { data: defaultData, isSubmitting, isSubmittingWithOidc, error },
|
authenticateForm: { data: defaultData, isSubmitting, isSubmittingUsingOidc, error },
|
||||||
},
|
},
|
||||||
} = state;
|
} = state;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
defaultData,
|
defaultData,
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
isSubmittingWithOidc,
|
isSubmittingUsingOidc,
|
||||||
error,
|
error,
|
||||||
withOidc: !!oidcConfig,
|
withOidc: !!oidcConfig,
|
||||||
};
|
};
|
||||||
|
@ -27,7 +27,7 @@ const mapDispatchToProps = (dispatch) =>
|
||||||
bindActionCreators(
|
bindActionCreators(
|
||||||
{
|
{
|
||||||
onAuthenticate: entryActions.authenticate,
|
onAuthenticate: entryActions.authenticate,
|
||||||
onAuthenticateWithOidc: entryActions.authenticateWithOidc,
|
onAuthenticateUsingOidc: entryActions.authenticateUsingOidc,
|
||||||
onMessageDismiss: entryActions.clearAuthenticateError,
|
onMessageDismiss: entryActions.clearAuthenticateError,
|
||||||
},
|
},
|
||||||
dispatch,
|
dispatch,
|
||||||
|
|
|
@ -7,8 +7,8 @@ const authenticate = (data) => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const authenticateWithOidc = () => ({
|
const authenticateUsingOidc = () => ({
|
||||||
type: EntryActionTypes.WITH_OIDC_AUTHENTICATE,
|
type: EntryActionTypes.USING_OIDC_AUTHENTICATE,
|
||||||
payload: {},
|
payload: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -19,6 +19,6 @@ const clearAuthenticateError = () => ({
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
authenticate,
|
authenticate,
|
||||||
authenticateWithOidc,
|
authenticateUsingOidc,
|
||||||
clearAuthenticateError,
|
clearAuthenticateError,
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,7 @@ const initialState = {
|
||||||
export default (state = initialState, { type, payload }) => {
|
export default (state = initialState, { type, payload }) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ActionTypes.AUTHENTICATE__SUCCESS:
|
case ActionTypes.AUTHENTICATE__SUCCESS:
|
||||||
case ActionTypes.WITH_OIDC_AUTHENTICATE__SUCCESS:
|
case ActionTypes.USING_OIDC_AUTHENTICATE__SUCCESS:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
accessToken: payload.accessToken,
|
accessToken: payload.accessToken,
|
||||||
|
|
|
@ -15,7 +15,7 @@ export default (state = initialState, { type, payload }) => {
|
||||||
config: payload.config,
|
config: payload.config,
|
||||||
};
|
};
|
||||||
case ActionTypes.AUTHENTICATE__SUCCESS:
|
case ActionTypes.AUTHENTICATE__SUCCESS:
|
||||||
case ActionTypes.WITH_OIDC_AUTHENTICATE__SUCCESS:
|
case ActionTypes.USING_OIDC_AUTHENTICATE__SUCCESS:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
isInitializing: true,
|
isInitializing: true,
|
||||||
|
|
|
@ -9,7 +9,7 @@ const initialState = {
|
||||||
password: '',
|
password: '',
|
||||||
},
|
},
|
||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
isSubmittingWithOidc: false,
|
isSubmittingUsingOidc: false,
|
||||||
error: null,
|
error: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ export default (state = initialState, { type, payload }) => {
|
||||||
if (payload.location.pathname === Paths.OIDC_CALLBACK) {
|
if (payload.location.pathname === Paths.OIDC_CALLBACK) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
isSubmittingWithOidc: true,
|
isSubmittingUsingOidc: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ export default (state = initialState, { type, payload }) => {
|
||||||
isSubmitting: true,
|
isSubmitting: true,
|
||||||
};
|
};
|
||||||
case ActionTypes.AUTHENTICATE__SUCCESS:
|
case ActionTypes.AUTHENTICATE__SUCCESS:
|
||||||
case ActionTypes.WITH_OIDC_AUTHENTICATE__SUCCESS:
|
case ActionTypes.USING_OIDC_AUTHENTICATE__SUCCESS:
|
||||||
return initialState;
|
return initialState;
|
||||||
case ActionTypes.AUTHENTICATE__FAILURE:
|
case ActionTypes.AUTHENTICATE__FAILURE:
|
||||||
return {
|
return {
|
||||||
|
@ -43,10 +43,10 @@ export default (state = initialState, { type, payload }) => {
|
||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
error: payload.error,
|
error: payload.error,
|
||||||
};
|
};
|
||||||
case ActionTypes.WITH_OIDC_AUTHENTICATE__FAILURE:
|
case ActionTypes.USING_OIDC_AUTHENTICATE__FAILURE:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
isSubmittingWithOidc: false,
|
isSubmittingUsingOidc: false,
|
||||||
error: payload.error,
|
error: payload.error,
|
||||||
};
|
};
|
||||||
case ActionTypes.AUTHENTICATE_ERROR_CLEAR:
|
case ActionTypes.AUTHENTICATE_ERROR_CLEAR:
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { all, apply, fork, take } from 'redux-saga/effects';
|
import { all, apply, fork, select, take } from 'redux-saga/effects';
|
||||||
|
|
||||||
import watchers from './watchers';
|
import watchers from './watchers';
|
||||||
import services from './services';
|
import services from './services';
|
||||||
|
import selectors from '../../selectors';
|
||||||
import { socket } from '../../api';
|
import { socket } from '../../api';
|
||||||
import ActionTypes from '../../constants/ActionTypes';
|
import ActionTypes from '../../constants/ActionTypes';
|
||||||
import Paths from '../../constants/Paths';
|
import Paths from '../../constants/Paths';
|
||||||
|
@ -14,5 +15,12 @@ export default function* coreSaga() {
|
||||||
|
|
||||||
yield take(ActionTypes.LOGOUT);
|
yield take(ActionTypes.LOGOUT);
|
||||||
|
|
||||||
window.location.href = Paths.LOGIN;
|
const oidcConfig = yield select(selectors.selectOidcConfig);
|
||||||
|
|
||||||
|
if (oidcConfig && oidcConfig.endSessionUrl !== null) {
|
||||||
|
// Redirect the user to the IDP to log out.
|
||||||
|
window.location.href = oidcConfig.endSessionUrl;
|
||||||
|
} else {
|
||||||
|
window.location.href = Paths.LOGIN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,15 +83,7 @@ export function* logout(invalidateAccessToken = true) {
|
||||||
} catch (error) {} // eslint-disable-line no-empty
|
} catch (error) {} // eslint-disable-line no-empty
|
||||||
}
|
}
|
||||||
|
|
||||||
const oidcConfig = yield select(selectors.selectOidcConfig);
|
|
||||||
|
|
||||||
yield put(actions.logout());
|
yield put(actions.logout());
|
||||||
|
|
||||||
if (oidcConfig && oidcConfig.endSessionUrl !== null) {
|
|
||||||
// Redirect the user to the IDP to log out.
|
|
||||||
window.location.replace(oidcConfig.endSessionUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
yield take();
|
yield take();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ export default function* loginSaga() {
|
||||||
|
|
||||||
yield fork(services.initializeLogin);
|
yield fork(services.initializeLogin);
|
||||||
|
|
||||||
yield take([ActionTypes.AUTHENTICATE__SUCCESS, ActionTypes.WITH_OIDC_AUTHENTICATE__SUCCESS]);
|
yield take([ActionTypes.AUTHENTICATE__SUCCESS, ActionTypes.USING_OIDC_AUTHENTICATE__SUCCESS]);
|
||||||
|
|
||||||
yield cancel(watcherTasks);
|
yield cancel(watcherTasks);
|
||||||
yield call(services.goToRoot);
|
yield call(services.goToRoot);
|
||||||
|
|
|
@ -29,19 +29,22 @@ export function* authenticate(data) {
|
||||||
yield put(actions.authenticate.success(accessToken));
|
yield put(actions.authenticate.success(accessToken));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* authenticateWithOidc() {
|
export function* authenticateUsingOidc() {
|
||||||
const oidcConfig = yield select(selectors.selectOidcConfig);
|
const oidcConfig = yield select(selectors.selectOidcConfig);
|
||||||
|
|
||||||
const nonce = nanoid();
|
const nonce = nanoid();
|
||||||
window.sessionStorage.setItem('oidc-nonce', nonce);
|
window.sessionStorage.setItem('oidc-nonce', nonce);
|
||||||
window.location.replace(`${oidcConfig.authorizationUrl}&nonce=${encodeURIComponent(nonce)}`);
|
window.location.href = `${oidcConfig.authorizationUrl}&nonce=${encodeURIComponent(nonce)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* authenticateWithOidcCallback() {
|
export function* authenticateUsingOidcCallback() {
|
||||||
const params = new URLSearchParams(window.location.hash.substring(1));
|
const params = new URLSearchParams(window.location.hash.substring(1));
|
||||||
|
|
||||||
|
yield put(replace(Paths.LOGIN));
|
||||||
|
|
||||||
if (params.get('error') !== null) {
|
if (params.get('error') !== null) {
|
||||||
yield put(
|
yield put(
|
||||||
actions.authenticateWithOidc.failure(
|
actions.authenticateUsingOidc.failure(
|
||||||
new Error(
|
new Error(
|
||||||
`OIDC Authorization error: ${params.get('error')}: ${params.get('error_description')}`,
|
`OIDC Authorization error: ${params.get('error')}: ${params.get('error_description')}`,
|
||||||
),
|
),
|
||||||
|
@ -53,7 +56,7 @@ export function* authenticateWithOidcCallback() {
|
||||||
const nonce = window.sessionStorage.getItem('oidc-nonce');
|
const nonce = window.sessionStorage.getItem('oidc-nonce');
|
||||||
if (nonce === null) {
|
if (nonce === null) {
|
||||||
yield put(
|
yield put(
|
||||||
actions.authenticateWithOidc.failure(
|
actions.authenticateUsingOidc.failure(
|
||||||
new Error('Unable to process OIDC response: no nonce issued'),
|
new Error('Unable to process OIDC response: no nonce issued'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -63,29 +66,27 @@ export function* authenticateWithOidcCallback() {
|
||||||
const code = params.get('code');
|
const code = params.get('code');
|
||||||
if (code === null) {
|
if (code === null) {
|
||||||
yield put(
|
yield put(
|
||||||
actions.authenticateWithOidc.failure(new Error('Invalid OIDC response: no code parameter')),
|
actions.authenticateUsingOidc.failure(new Error('Invalid OIDC response: no code parameter')),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.sessionStorage.removeItem('oidc-nonce');
|
window.sessionStorage.removeItem('oidc-nonce');
|
||||||
|
|
||||||
yield put(replace(Paths.LOGIN));
|
|
||||||
|
|
||||||
if (code !== null) {
|
if (code !== null) {
|
||||||
let accessToken;
|
let accessToken;
|
||||||
try {
|
try {
|
||||||
({ item: accessToken } = yield call(api.exchangeToAccessToken, {
|
({ item: accessToken } = yield call(api.exchangeForAccessTokenUsingOidc, {
|
||||||
code,
|
code,
|
||||||
nonce,
|
nonce,
|
||||||
}));
|
}));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
yield put(actions.authenticateWithOidc.failure(error));
|
yield put(actions.authenticateUsingOidc.failure(error));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
yield call(setAccessToken, accessToken);
|
yield call(setAccessToken, accessToken);
|
||||||
yield put(actions.authenticateWithOidc.success(accessToken));
|
yield put(actions.authenticateUsingOidc.success(accessToken));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +97,7 @@ export function* clearAuthenticateError() {
|
||||||
export default {
|
export default {
|
||||||
initializeLogin,
|
initializeLogin,
|
||||||
authenticate,
|
authenticate,
|
||||||
authenticateWithOidc,
|
authenticateUsingOidc,
|
||||||
authenticateWithOidcCallback,
|
authenticateUsingOidcCallback,
|
||||||
clearAuthenticateError,
|
clearAuthenticateError,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { call, put, select, take } from 'redux-saga/effects';
|
import { call, put, select, take } from 'redux-saga/effects';
|
||||||
import { push } from '../../../lib/redux-router';
|
import { push } from '../../../lib/redux-router';
|
||||||
|
|
||||||
import { authenticateWithOidcCallback } from './login';
|
import { authenticateUsingOidcCallback } from './login';
|
||||||
import selectors from '../../../selectors';
|
import selectors from '../../../selectors';
|
||||||
import ActionTypes from '../../../constants/ActionTypes';
|
import ActionTypes from '../../../constants/ActionTypes';
|
||||||
import Paths from '../../../constants/Paths';
|
import Paths from '../../../constants/Paths';
|
||||||
|
@ -36,9 +36,7 @@ export function* handleLocationChange() {
|
||||||
yield take(ActionTypes.LOGIN_INITIALIZE);
|
yield take(ActionTypes.LOGIN_INITIALIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check if OIDC is enabled
|
yield call(authenticateUsingOidcCallback);
|
||||||
|
|
||||||
yield call(authenticateWithOidcCallback);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ export default function* loginWatchers() {
|
||||||
takeEvery(EntryActionTypes.AUTHENTICATE, ({ payload: { data } }) =>
|
takeEvery(EntryActionTypes.AUTHENTICATE, ({ payload: { data } }) =>
|
||||||
services.authenticate(data),
|
services.authenticate(data),
|
||||||
),
|
),
|
||||||
takeEvery(EntryActionTypes.WITH_OIDC_AUTHENTICATE, () => services.authenticateWithOidc()),
|
takeEvery(EntryActionTypes.USING_OIDC_AUTHENTICATE, () => services.authenticateUsingOidc()),
|
||||||
takeEvery(EntryActionTypes.AUTHENTICATE_ERROR_CLEAR, () => services.clearAuthenticateError()),
|
takeEvery(EntryActionTypes.AUTHENTICATE_ERROR_CLEAR, () => services.clearAuthenticateError()),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
const { getRemoteAddress } = require('../../../utils/remoteAddress');
|
const { getRemoteAddress } = require('../../../utils/remoteAddress');
|
||||||
|
|
||||||
const Errors = {
|
const Errors = {
|
||||||
INVALID_TOKEN: {
|
INVALID_CODE_OR_NONCE: {
|
||||||
invalidToken: 'Invalid token',
|
invalidCodeOrNonce: 'Invalid code or nonce',
|
||||||
},
|
},
|
||||||
EMAIL_ALREADY_IN_USE: {
|
EMAIL_ALREADY_IN_USE: {
|
||||||
emailAlreadyInUse: 'Email already in use',
|
emailAlreadyInUse: 'Email already in use',
|
||||||
|
@ -28,7 +28,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
exits: {
|
exits: {
|
||||||
invalidToken: {
|
invalidCodeOrNonce: {
|
||||||
responseType: 'unauthorized',
|
responseType: 'unauthorized',
|
||||||
},
|
},
|
||||||
emailAlreadyInUse: {
|
emailAlreadyInUse: {
|
||||||
|
@ -46,10 +46,10 @@ module.exports = {
|
||||||
const remoteAddress = getRemoteAddress(this.req);
|
const remoteAddress = getRemoteAddress(this.req);
|
||||||
|
|
||||||
const user = await sails.helpers.users
|
const user = await sails.helpers.users
|
||||||
.getOrCreateOneByOidcToken(inputs.code, inputs.nonce)
|
.getOrCreateOneUsingOidc(inputs.code, inputs.nonce)
|
||||||
.intercept('invalidToken', () => {
|
.intercept('invalidCodeOrNonce', () => {
|
||||||
sails.log.warn(`Invalid token! (IP: ${remoteAddress})`);
|
sails.log.warn(`Invalid code or nonce! (IP: ${remoteAddress})`);
|
||||||
return Errors.INVALID_TOKEN;
|
return Errors.INVALID_CODE_OR_NONCE;
|
||||||
})
|
})
|
||||||
.intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE)
|
.intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE)
|
||||||
.intercept('usernameAlreadyInUse', () => Errors.USERNAME_ALREADY_IN_USE)
|
.intercept('usernameAlreadyInUse', () => Errors.USERNAME_ALREADY_IN_USE)
|
|
@ -1,20 +1,21 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
fn() {
|
fn() {
|
||||||
const oidcClient = sails.hooks.oidc.isActive() ? sails.hooks.oidc.getClient() : null;
|
let oidc = null;
|
||||||
|
if (sails.hooks.oidc.isActive()) {
|
||||||
|
const oidcClient = sails.hooks.oidc.getClient();
|
||||||
|
|
||||||
|
oidc = {
|
||||||
|
authorizationUrl: oidcClient.authorizationUrl({
|
||||||
|
scope: sails.config.custom.oidcScopes,
|
||||||
|
response_mode: 'fragment',
|
||||||
|
}),
|
||||||
|
endSessionUrl: oidcClient.issuer.end_session_endpoint ? oidcClient.endSessionUrl({}) : null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
item: {
|
item: {
|
||||||
oidc:
|
oidc,
|
||||||
sails.config.custom.oidcIssuer !== ''
|
|
||||||
? {
|
|
||||||
authorizationUrl: oidcClient.authorizationUrl({
|
|
||||||
scope: sails.config.custom.oidcScopes,
|
|
||||||
response_mode: 'fragment',
|
|
||||||
}),
|
|
||||||
endSessionUrl: oidcClient.issuer.end_session_endpoint
|
|
||||||
? oidcClient.endSessionUrl({})
|
|
||||||
: null,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,7 +11,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
exits: {
|
exits: {
|
||||||
invalidToken: {},
|
invalidCodeOrNonce: {},
|
||||||
missingValues: {},
|
missingValues: {},
|
||||||
emailAlreadyInUse: {},
|
emailAlreadyInUse: {},
|
||||||
usernameAlreadyInUse: {},
|
usernameAlreadyInUse: {},
|
||||||
|
@ -23,14 +23,14 @@ module.exports = {
|
||||||
let userInfo;
|
let userInfo;
|
||||||
try {
|
try {
|
||||||
const tokenSet = await client.callback(
|
const tokenSet = await client.callback(
|
||||||
`${sails.config.custom.baseUrl}/oidc-callback`,
|
sails.config.custom.oidcRedirectUri,
|
||||||
{ code: inputs.code },
|
{ code: inputs.code },
|
||||||
{ nonce: inputs.nonce },
|
{ nonce: inputs.nonce },
|
||||||
);
|
);
|
||||||
userInfo = await client.userinfo(tokenSet);
|
userInfo = await client.userinfo(tokenSet);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
sails.log.warn(`Error while exchanging OIDC code: ${e}`);
|
sails.log.warn(`Error while exchanging OIDC code: ${e}`);
|
||||||
throw 'invalidToken';
|
throw 'invalidCodeOrNonce';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!userInfo.email || !userInfo.name) {
|
if (!userInfo.email || !userInfo.name) {
|
||||||
|
@ -73,7 +73,7 @@ module.exports = {
|
||||||
} else {
|
} else {
|
||||||
// If no IDP/User mapping exists, search for the user by email.
|
// If no IDP/User mapping exists, search for the user by email.
|
||||||
user = await sails.helpers.users.getOne({
|
user = await sails.helpers.users.getOne({
|
||||||
email: values.email,
|
email: values.email.toLowerCase(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Otherwise, create a new user.
|
// Otherwise, create a new user.
|
|
@ -15,7 +15,7 @@ module.exports = function oidcServiceHook(sails) {
|
||||||
client = new issuer.Client({
|
client = new issuer.Client({
|
||||||
client_id: sails.config.custom.oidcClientId,
|
client_id: sails.config.custom.oidcClientId,
|
||||||
client_secret: sails.config.custom.oidcClientSecret,
|
client_secret: sails.config.custom.oidcClientSecret,
|
||||||
redirect_uris: [`${sails.config.custom.baseUrl}/oidc-callback`],
|
redirect_uris: [sails.config.custom.oidcRedirectUri],
|
||||||
response_types: ['code'],
|
response_types: ['code'],
|
||||||
});
|
});
|
||||||
sails.log.info('OIDC hook has been loaded successfully');
|
sails.log.info('OIDC hook has been loaded successfully');
|
||||||
|
|
|
@ -39,4 +39,9 @@ module.exports.custom = {
|
||||||
oidcScopes: process.env.OIDC_SCOPES || 'openid email profile',
|
oidcScopes: process.env.OIDC_SCOPES || 'openid email profile',
|
||||||
oidcAdminRoles: process.env.OIDC_ADMIN_ROLES ? process.env.OIDC_ADMIN_ROLES.split(',') : [],
|
oidcAdminRoles: process.env.OIDC_ADMIN_ROLES ? process.env.OIDC_ADMIN_ROLES.split(',') : [],
|
||||||
oidcRolesAttribute: process.env.OIDC_ROLES_ATTRIBUTE || 'groups',
|
oidcRolesAttribute: process.env.OIDC_ROLES_ATTRIBUTE || 'groups',
|
||||||
|
|
||||||
|
// TODO: move client base url to environment variable?
|
||||||
|
oidcRedirectUri: `${
|
||||||
|
sails.config.environment === 'production' ? process.env.BASE_URL : 'http://localhost:3000'
|
||||||
|
}/oidc-callback`,
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,5 +25,5 @@ module.exports.policies = {
|
||||||
|
|
||||||
'show-config': true,
|
'show-config': true,
|
||||||
'access-tokens/create': true,
|
'access-tokens/create': true,
|
||||||
'access-tokens/exchange': true,
|
'access-tokens/exchange-using-oidc': true,
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,7 +12,7 @@ module.exports.routes = {
|
||||||
'GET /api/config': 'show-config',
|
'GET /api/config': 'show-config',
|
||||||
|
|
||||||
'POST /api/access-tokens': 'access-tokens/create',
|
'POST /api/access-tokens': 'access-tokens/create',
|
||||||
'POST /api/access-tokens/exchange': 'access-tokens/exchange',
|
'POST /api/access-tokens/exchange-using-oidc': 'access-tokens/exchange-using-oidc',
|
||||||
'DELETE /api/access-tokens/me': 'access-tokens/delete',
|
'DELETE /api/access-tokens/me': 'access-tokens/delete',
|
||||||
|
|
||||||
'GET /api/users': 'users/index',
|
'GET /api/users': 'users/index',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue