From 4b09fdd04f475483ad09d627ad6abc5c0f18b5f7 Mon Sep 17 00:00:00 2001 From: Thibaud Dauce Date: Wed, 25 Feb 2026 14:48:16 +0100 Subject: [PATCH 1/3] feat: new API keys --- components/User/AdminUserProfilePage.vue | 107 +++-------------------- components/User/ApiTokensSection.vue | 102 +++++++++++++++++++++ components/User/CreateApiTokenModal.vue | 92 +++++++++++++++++++ datagouv-components/src/types/users.ts | 1 - types/api-tokens.ts | 15 ++++ utils/auth.ts | 1 - 6 files changed, 220 insertions(+), 98 deletions(-) create mode 100644 components/User/ApiTokensSection.vue create mode 100644 components/User/CreateApiTokenModal.vue create mode 100644 types/api-tokens.ts diff --git a/components/User/AdminUserProfilePage.vue b/components/User/AdminUserProfilePage.vue index 69cae6497..9383183c8 100644 --- a/components/User/AdminUserProfilePage.vue +++ b/components/User/AdminUserProfilePage.vue @@ -75,73 +75,6 @@ {{ $t('Sauvegarder') }} -
- -
-
-
- -
- -
-
-
-
-
- - {{ $t('Regénérer') }} - {{ $t('Générer') }} - -
-
- - {{ $t('Supprimer') }} - -
-
-
-
+
diff --git a/components/User/ApiTokensSection.vue b/components/User/ApiTokensSection.vue new file mode 100644 index 000000000..175601bb4 --- /dev/null +++ b/components/User/ApiTokensSection.vue @@ -0,0 +1,102 @@ + + + diff --git a/components/User/CreateApiTokenModal.vue b/components/User/CreateApiTokenModal.vue new file mode 100644 index 000000000..ef6eeb25d --- /dev/null +++ b/components/User/CreateApiTokenModal.vue @@ -0,0 +1,92 @@ + + + diff --git a/datagouv-components/src/types/users.ts b/datagouv-components/src/types/users.ts index 0664c7760..4b9e74a6b 100644 --- a/datagouv-components/src/types/users.ts +++ b/datagouv-components/src/types/users.ts @@ -21,7 +21,6 @@ export type User = { page: UserReference['page'] avatar: UserReference['avatar'] avatar_thumbnail: UserReference['avatar_thumbnail'] - apikey?: string email?: string about: string website?: string diff --git a/types/api-tokens.ts b/types/api-tokens.ts new file mode 100644 index 000000000..73193410b --- /dev/null +++ b/types/api-tokens.ts @@ -0,0 +1,15 @@ +export type ApiToken = { + id: string + token_prefix: string + name: string | null + scope: 'admin' + kind: 'api_key' + created_at: string + last_used_at: string | null + user_agents: string[] + expires_at: string | null +} + +export type ApiTokenCreated = ApiToken & { + token: string +} diff --git a/utils/auth.ts b/utils/auth.ts index 554321dfc..e6b8b8de9 100644 --- a/utils/auth.ts +++ b/utils/auth.ts @@ -4,7 +4,6 @@ import { usePostApiWithCsrf } from './api' export type Me = User & { about: string | null active: boolean - apikey: string | null email: string metrics: { datasets: number From 2531242fef29ca3d0dd99c2a0d51917f504dc03f Mon Sep 17 00:00:00 2001 From: Thibaud Dauce Date: Wed, 25 Feb 2026 14:54:03 +0100 Subject: [PATCH 2/3] add user agents --- components/User/ApiTokensSection.vue | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/components/User/ApiTokensSection.vue b/components/User/ApiTokensSection.vue index 175601bb4..802e74d28 100644 --- a/components/User/ApiTokensSection.vue +++ b/components/User/ApiTokensSection.vue @@ -42,6 +42,19 @@ {{ $t('Créé {date}', { date: formatRelativeIfRecentDate(token.created_at) }) }} · {{ $t('Utilisé {date}', { date: formatRelativeIfRecentDate(token.last_used_at) }) }} · {{ $t('Jamais utilisé') }} + + + + +
    +
  • + {{ ua }} +
  • +
+
@@ -66,6 +96,7 @@ import { BrandedButton, CopyButton, SimpleBanner, toast, useFormatDate } from '@ import { RiDeleteBin6Line } from '@remixicon/vue' import type { ApiToken, ApiTokenCreated } from '~/types/api-tokens' import CreateApiTokenModal from './CreateApiTokenModal.vue' +import ModalClient from '~/components/Modal/Modal.client.vue' const { t } = useTranslation() const { $api } = useNuxtApp() @@ -74,6 +105,7 @@ const { formatRelativeIfRecentDate } = useFormatDate() const tokens = ref([]) const newlyCreatedToken = ref(null) const revoking = ref(null) +const userAgentsModalList = ref(null) async function fetchTokens() { tokens.value = await $api('/api/1/me/tokens/') From 1d9e70c2899d5cb88bf3d14fef3aef0ba8b3dbf9 Mon Sep 17 00:00:00 2001 From: Thibaud Dauce Date: Wed, 25 Feb 2026 15:02:32 +0100 Subject: [PATCH 3/3] add confirmation modal before deleting token --- components/User/ApiTokensSection.vue | 35 +++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/components/User/ApiTokensSection.vue b/components/User/ApiTokensSection.vue index 802e74d28..ad343a93c 100644 --- a/components/User/ApiTokensSection.vue +++ b/components/User/ApiTokensSection.vue @@ -28,7 +28,7 @@
@@ -61,10 +61,9 @@ color="danger" size="xs" :icon="RiDeleteBin6Line" - :loading="revoking === token.id" :disabled="!!revoking" icon-only - @click="revokeToken(token)" + @click="tokenToRevoke = token" />
@@ -72,6 +71,34 @@
+ + {{ $t('Le token {name} sera définitivement révoqué. Les applications qui l\'utilisent ne pourront plus accéder à l\'API.', { name: tokenToRevoke?.name || `${tokenToRevoke?.token_prefix}…` }) }} + + + ([]) const newlyCreatedToken = ref(null) const revoking = ref(null) +const tokenToRevoke = ref(null) const userAgentsModalList = ref(null) async function fetchTokens() { @@ -123,6 +151,7 @@ async function revokeToken(token: ApiToken) { method: 'DELETE', }) toast.success(t('Token révoqué.')) + tokenToRevoke.value = null await fetchTokens() } finally {