From b4cbd32bf272b23b0db866d446e8a0b32e9f17cf Mon Sep 17 00:00:00 2001 From: Samuel Date: Thu, 6 Nov 2025 20:56:48 +0100 Subject: [PATCH] feat: Add API key authentication (#1254) Closes #945 --- client/src/actions/users.js | 55 ++++++ client/src/api/users.js | 4 + .../BoardMemberships/ActionsStep.jsx | 2 +- .../common/AdministrationModal/SmtpPane.jsx | 10 +- .../UsersPane/ActionsStep.jsx | 72 +++++--- .../UsersPane/ApiKeyStep.jsx | 169 ++++++++++++++++++ .../UsersPane/ApiKeyStep.module.scss | 65 +++++++ .../AdministrationModal/UsersPane/Item.jsx | 59 +++++- .../UsersPane/Item.module.scss | 35 +++- .../UsersPane/UsersPane.jsx | 17 +- .../EditUserEmailStep/EditUserEmailStep.jsx | 9 +- .../EditUserPasswordStep.jsx | 15 +- .../EditUserUsernameStep.jsx | 9 +- .../AccountPane/AccountPane.jsx | 6 +- client/src/constants/ActionTypes.js | 7 + client/src/constants/EntryActionTypes.js | 3 + client/src/entry-actions/users.js | 24 +++ client/src/locales/ar-YE/core.js | 17 ++ client/src/locales/bg-BG/core.js | 17 ++ client/src/locales/cs-CZ/core.js | 17 ++ client/src/locales/da-DK/core.js | 17 ++ client/src/locales/de-DE/core.js | 19 ++ client/src/locales/el-GR/core.js | 18 ++ client/src/locales/en-GB/core.js | 17 ++ client/src/locales/en-US/core.js | 17 ++ client/src/locales/es-ES/core.js | 17 ++ client/src/locales/et-EE/core.js | 17 ++ client/src/locales/fa-IR/core.js | 17 ++ client/src/locales/fi-FI/core.js | 17 ++ client/src/locales/fr-FR/core.js | 17 ++ client/src/locales/hu-HU/core.js | 17 ++ client/src/locales/id-ID/core.js | 17 ++ client/src/locales/it-IT/core.js | 17 ++ client/src/locales/ja-JP/core.js | 17 ++ client/src/locales/ko-KR/core.js | 17 ++ client/src/locales/nl-NL/core.js | 18 ++ client/src/locales/pl-PL/core.js | 17 ++ client/src/locales/pt-BR/core.js | 17 ++ client/src/locales/pt-PT/core.js | 17 ++ client/src/locales/ro-RO/core.js | 17 ++ client/src/locales/ru-RU/core.js | 17 ++ client/src/locales/sk-SK/core.js | 17 ++ client/src/locales/sr-Cyrl-RS/core.js | 17 ++ client/src/locales/sr-Latn-RS/core.js | 18 ++ client/src/locales/sv-SE/core.js | 17 ++ client/src/locales/tr-TR/core.js | 17 ++ client/src/locales/uk-UA/core.js | 17 ++ client/src/locales/uz-UZ/core.js | 17 ++ client/src/locales/zh-CN/core.js | 15 ++ client/src/locales/zh-TW/core.js | 15 ++ client/src/models/User.js | 57 ++++++ client/src/reducers/ui/index.js | 4 +- .../ui/{smtp-test.js => smtp-test-state.js} | 0 client/src/sagas/core/services/users.js | 42 +++++ client/src/sagas/core/watchers/users.js | 9 + client/src/selectors/common.js | 4 +- client/src/selectors/users.js | 13 +- .../api/controllers/access-tokens/delete.js | 2 + .../api/controllers/users/create-api-key.js | 103 +++++++++++ .../api/controllers/users/update-password.js | 2 +- server/api/controllers/users/update.js | 14 ++ server/api/helpers/users/present-one.js | 2 + server/api/helpers/users/update-one.js | 24 ++- server/api/helpers/utils/generate-api-key.js | 19 ++ .../helpers/utils/generate-random-string.js | 30 ++++ server/api/helpers/utils/hash.js | 21 +++ server/api/hooks/current-user/index.js | 47 ++++- server/api/hooks/query-methods/models/User.js | 7 + server/api/models/User.js | 24 ++- server/api/policies/is-authenticated.js | 1 + server/api/policies/is-session.js | 12 ++ server/config/policies.js | 3 + server/config/routes.js | 1 + server/config/swagger.js | 8 + ...251105104948_add_api_key_authentication.js | 23 +++ 75 files changed, 1501 insertions(+), 94 deletions(-) create mode 100644 client/src/components/common/AdministrationModal/UsersPane/ApiKeyStep.jsx create mode 100644 client/src/components/common/AdministrationModal/UsersPane/ApiKeyStep.module.scss rename client/src/reducers/ui/{smtp-test.js => smtp-test-state.js} (100%) create mode 100644 server/api/controllers/users/create-api-key.js create mode 100644 server/api/helpers/utils/generate-api-key.js create mode 100644 server/api/helpers/utils/generate-random-string.js create mode 100644 server/api/helpers/utils/hash.js create mode 100755 server/api/policies/is-session.js create mode 100644 server/db/migrations/20251105104948_add_api_key_authentication.js diff --git a/client/src/actions/users.js b/client/src/actions/users.js index 1b2ece4a..1adc0e2b 100644 --- a/client/src/actions/users.js +++ b/client/src/actions/users.js @@ -235,6 +235,58 @@ updateUserAvatar.failure = (id, error) => ({ }, }); +const createUserApiKey = (id) => ({ + type: ActionTypes.USER_API_KEY_CREATE, + payload: { + id, + }, +}); + +createUserApiKey.success = (user, apiKey) => ({ + type: ActionTypes.USER_API_KEY_CREATE__SUCCESS, + payload: { + user, + apiKey, + }, +}); + +createUserApiKey.failure = (id, error) => ({ + type: ActionTypes.USER_API_KEY_CREATE__FAILURE, + payload: { + id, + error, + }, +}); + +const deleteUserApiKey = (id) => ({ + type: ActionTypes.USER_API_KEY_DELETE, + payload: { + id, + }, +}); + +deleteUserApiKey.success = (user) => ({ + type: ActionTypes.USER_API_KEY_DELETE__SUCCESS, + payload: { + user, + }, +}); + +deleteUserApiKey.failure = (id, error) => ({ + type: ActionTypes.USER_API_KEY_DELETE__FAILURE, + payload: { + id, + error, + }, +}); + +const clearUserApiKeyValue = (id) => ({ + type: ActionTypes.USER_API_KEY_VALUE_CLEAR, + payload: { + id, + }, +}); + const deleteUser = (id) => ({ type: ActionTypes.USER_DELETE, payload: { @@ -359,6 +411,9 @@ export default { updateUserUsername, clearUserUsernameUpdateError, updateUserAvatar, + createUserApiKey, + deleteUserApiKey, + clearUserApiKeyValue, deleteUser, handleUserDelete, addUserToCard, diff --git a/client/src/api/users.js b/client/src/api/users.js index 0685f247..4038a560 100755 --- a/client/src/api/users.js +++ b/client/src/api/users.js @@ -33,6 +33,9 @@ const updateUserUsername = (id, data, headers) => const updateUserAvatar = (id, data, headers) => http.post(`/users/${id}/avatar`, data, headers); +const createUserApiKey = (userId, headers) => + socket.post(`/users/${userId}/api-key`, undefined, headers); + const deleteUser = (id, headers) => socket.delete(`/users/${id}`, undefined, headers); export default { @@ -45,5 +48,6 @@ export default { updateUserPassword, updateUserUsername, updateUserAvatar, + createUserApiKey, deleteUser, }; diff --git a/client/src/components/board-memberships/BoardMemberships/ActionsStep.jsx b/client/src/components/board-memberships/BoardMemberships/ActionsStep.jsx index 204a66d3..3ac638ff 100644 --- a/client/src/components/board-memberships/BoardMemberships/ActionsStep.jsx +++ b/client/src/components/board-memberships/BoardMemberships/ActionsStep.jsx @@ -121,7 +121,7 @@ const ActionsStep = React.memo(({ boardMembershipId, title, onBack, onClose }) = )} {user.organization && (
- + {user.organization}
)} diff --git a/client/src/components/common/AdministrationModal/SmtpPane.jsx b/client/src/components/common/AdministrationModal/SmtpPane.jsx index 80d8955a..9db6649f 100644 --- a/client/src/components/common/AdministrationModal/SmtpPane.jsx +++ b/client/src/components/common/AdministrationModal/SmtpPane.jsx @@ -20,7 +20,7 @@ import styles from './SmtpPane.module.scss'; const SmtpPane = React.memo(() => { const config = useSelector(selectors.selectConfig); - const smtpTest = useSelector(selectors.selectSmtpTest); + const smtpTestState = useSelector(selectors.selectSmtpTestState); const dispatch = useDispatch(); const [t] = useTranslation(); @@ -196,14 +196,14 @@ const SmtpPane = React.memo(() => {