From 05f723af0fc94f619e276aeea6c5ebb23c870d20 Mon Sep 17 00:00:00 2001 From: AlitaBernachot Date: Tue, 22 Oct 2024 10:23:33 +0200 Subject: [PATCH 1/4] feat: auto auth with cookies and adapt for v3 --- .babelrc | 6 ++++++ .env | 1 + .env.development | 7 ++++--- .env.e2e | 1 + .env.staging | 7 ++++--- README.md | 30 +++++++++++++++++++++++++++++- package.json | 2 +- src/assets/main.css | 2 +- src/bundle/lib.ts | 4 ++++ src/components/auth/auth-form.vue | 13 ++++++++++--- src/services/auth/auth.service.ts | 7 ++++++- vite-dist.config.ts | 1 + 12 files changed, 68 insertions(+), 13 deletions(-) diff --git a/.babelrc b/.babelrc index dacfb573..b622e6f8 100644 --- a/.babelrc +++ b/.babelrc @@ -8,5 +8,11 @@ } ] ], + "env": { + "dev": { + "compact": false, + "minified": false + } + }, "compact": true } diff --git a/.env b/.env index b938ec88..09bcd978 100644 --- a/.env +++ b/.env @@ -40,6 +40,7 @@ VITE_ARROW_MODEL_URL="/static-ngeo/models/arrow5.glb" VITE_ELEVATION_URL="/raster" # Auth +VITE_CREDENTIALS_ORIGIN="same-origin" VITE_LOGIN_URL="/login" VITE_LOGOUT_URL="/logout" VITE_USERINFO_URL="/getuserinfo" diff --git a/.env.development b/.env.development index 92779622..987acab9 100644 --- a/.env.development +++ b/.env.development @@ -40,9 +40,10 @@ VITE_ARROW_MODEL_URL="https://migration.geoportail.lu/static-ngeo/models/arrow5. VITE_ELEVATION_URL="https://migration.geoportail.lu/raster" # Auth -VITE_LOGIN_URL="https://migration.geoportail.lu/login" -VITE_LOGOUT_URL="https://migration.geoportail.lu/logout" -VITE_USERINFO_URL="https://migration.geoportail.lu/getuserinfo" +VITE_CREDENTIALS_ORIGIN="include" +VITE_LOGIN_URL="http://localhost:8080/login" +VITE_LOGOUT_URL="http://localhost:8080/logout" +VITE_USERINFO_URL="http://localhost:8080/getuserinfo" VITE_MYACCOUNT_URL="https://myaccount.geoportail.lu" VITE_MYACCOUNT_RECOVER_URL="https://myaccount.geoportail.lu/recover-password" VITE_MYACCOUNT_NEW_URL="https://myaccount.geoportail.lu/new-user" diff --git a/.env.e2e b/.env.e2e index 7418e2ff..1a25ee8f 100644 --- a/.env.e2e +++ b/.env.e2e @@ -40,6 +40,7 @@ VITE_ARROW_MODEL_URL="https://migration.geoportail.lu/static-ngeo/models/arrow5. VITE_ELEVATION_URL="https://migration.geoportail.lu/raster" # Auth +VITE_CREDENTIALS_ORIGIN="same-origin" VITE_LOGIN_URL="https://migration.geoportail.lu/login" VITE_LOGOUT_URL="https://migration.geoportail.lu/logout" VITE_USERINFO_URL="https://migration.geoportail.lu/getuserinfo" diff --git a/.env.staging b/.env.staging index 2fc0526d..d11cc700 100644 --- a/.env.staging +++ b/.env.staging @@ -40,9 +40,10 @@ VITE_ARROW_MODEL_URL="https://migration.geoportail.lu/static-ngeo/models/arrow5. VITE_ELEVATION_URL="https://migration.geoportail.lu/raster" # Auth -VITE_LOGIN_URL="https://migration.geoportail.lu/login" -VITE_LOGOUT_URL="https://migration.geoportail.lu/logout" -VITE_USERINFO_URL="https://migration.geoportail.lu/getuserinfo" +VITE_CREDENTIALS_ORIGIN="same-origin" +VITE_LOGIN_URL="/login" +VITE_LOGOUT_URL="/logout" +VITE_USERINFO_URL="/getuserinfo" VITE_MYACCOUNT_URL="https://myaccount.geoportail.lu" VITE_MYACCOUNT_RECOVER_URL="https://myaccount.geoportail.lu/recover-password" VITE_MYACCOUNT_NEW_URL="https://myaccount.geoportail.lu/new-user" diff --git a/README.md b/README.md index 9045d200..681a49fe 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ You can include the built lib multiple ways in the `package.json`: } ``` -### Develop on lib within geoportailv3 environnement +### Develop on lib within geoportailv3 environment A simple way to develop on the lib and test it directly from within the geoportailv3 context is to map your `luxembourg-geoportal` repository as a volume to webpack_dev_server service of the docker composition: @@ -267,3 +267,31 @@ To create custom components in the application using the lib, adapt the followin const LayerPanelElement = createElementInstance(LayerPanel, app) customElements.define('layer-panel', LayerPanelElement) ``` + +## 🔒 Authenticate user + +To authenticate inside the v4 standalone app, you will need the v3 composition to be running at the same time and make some adjustement on both sides: + +- in v4, update `VITE_LOGIN_URL`, `VITE_LOGOUT_URL` and `VITE_USERINFO_URL` to point to your local v3 composition + +```bash +# file: luxembourg-geoportail/.env.development +VITE_LOGIN_URL="http://localhost:8080/login" +VITE_LOGOUT_URL="http://localhost:8080/logout" +VITE_USERINFO_URL="http://localhost:8080/getuserinfo" +``` + +- in v4, activate cross origin for GET/POST requests with a custom `VITE_CREDENTIALS_ORIGIN`. + +```bash +# file: luxembourg-geoportail/.env.development +VITE_CREDENTIALS_ORIGIN="include" +# ⚠️ WARNING: don't use `"include"` value in production (but use `"same-origin"` instead). +``` + +- in v3, add a new env variable in `docker-compose.yaml`: `ALLOW_CORS` and set it to value = `1` in the `.env.project` file to allow cors and cross origin requests. + +```bash +# file: geoportailv3/.env.project +ALLOW_CORS=1 # ⚠️WARNING: don't use this value in production +``` diff --git a/package.json b/package.json index 06fe496b..cb2fe066 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "build": "run-p type-check build-only", "build-only": "vite build", "build:lib:prod": "npx vite build --mode prod --config vite-dist.config.ts --minify=esbuild --debug && npx babel bundle/lux.dist.mjs --out-file bundle/lux.dist.js", - "build:lib:dev": "npx vite build --mode staging --config vite-dist.config.ts --minify=false --base=/dev/main.html/ --debug && cp bundle/lux.dist.mjs bundle/lux.dist.js", + "build:lib:dev": "npx vite build --mode staging --config vite-dist.config.ts --minify=false --base=/dev/main.html/ --debug && BABEL_ENV=dev npx babel bundle/lux.dist.mjs --out-file bundle/lux.dist.js", "preview": "vite preview", "test": "npm run test:unit", "test:unit": "vitest --environment jsdom --root .", diff --git a/src/assets/main.css b/src/assets/main.css index 3f7de88d..9f0a0085 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -404,7 +404,7 @@ } .lux-account-tab { - @apply ml-1 bg-primary text-white after:content-['\E02E'] after:font-icons after:text-3xl after:ml-4 w-20 px-2 pt-1; + @apply ml-1 bg-primary text-white after:content-['\E02E'] after:font-icons after:text-3xl after:ml-4 w-20 px-2 pt-1 mb-0 border-none; } .lux-account-content { diff --git a/src/bundle/lib.ts b/src/bundle/lib.ts index 7db6daf0..18533bab 100644 --- a/src/bundle/lib.ts +++ b/src/bundle/lib.ts @@ -11,6 +11,7 @@ import './lib.css' // Tell Vite to build the css import '../assets/main.css' // Tell Vite to build the css import AlertNotifications from '@/components/alert-notifications/alert-notifications.vue' +import AuthForm from '@/components/auth/auth-form.vue' import DropdownList from '@/components/common/dropdown-list.vue' import MapContainer from '@/components/map/map-container.vue' import BackgroundSelector from '@/components/background-selector/background-selector.vue' @@ -33,6 +34,7 @@ import { useAppStore } from '@/stores/app.store' import { useMapStore } from '@/stores/map.store' import { useStyleStore } from '@/stores/style.store' import { useThemeStore } from '@/stores/config.store' +import { useUserManagerStore } from '@/stores/user-manager.store' import { statePersistorBgLayerService } from '@/services/state-persistor/state-persistor-layer-background.service' import { statePersistorLayersService } from '@/services/state-persistor/state-persistor-layers.service' import { statePersistorThemeService } from '@/services/state-persistor/state-persistor-theme.service' @@ -118,6 +120,7 @@ export { VueDOMPurifyHTML, I18NextVue, AlertNotifications, + AuthForm, DropdownList, MapContainer, BackgroundSelector, @@ -142,6 +145,7 @@ export { useMapStore, useStyleStore, useThemeStore, + useUserManagerStore, statePersistorBgLayerService, statePersistorLayersService, statePersistorThemeService, diff --git a/src/components/auth/auth-form.vue b/src/components/auth/auth-form.vue index 7e113ae1..b4c3c4a8 100644 --- a/src/components/auth/auth-form.vue +++ b/src/components/auth/auth-form.vue @@ -20,18 +20,22 @@ const { lang, isApp } = storeToRefs(useAppStore()) const userManagerStore = useUserManagerStore() const { setCurrentUser, clearUser } = userManagerStore const { authenticated, currentUser } = storeToRefs(userManagerStore) +const autoAuthenticated = ref(false) // Will be set to true if user is authenticated via cookie on first call AuthService.getUserInfo() const userName = ref('') const userPassword = ref('') watch(authenticated, authenticated => { - if (authenticated) { + if (!autoAuthenticated.value && authenticated) { addNotification(t('Vous êtes maintenant correctement connecté.')) } }) onMounted(() => { AuthService.getUserInfo() - .then(onAuthenticateSuccess) + .then(user => { + autoAuthenticated.value = true + onAuthenticateSuccess(user) + }) .catch(() => { // do nothing, don't display errors }) @@ -51,7 +55,10 @@ function logout() { function submit() { AuthService.authenticate(userName.value, userPassword.value, isApp.value) - .then(onAuthenticateSuccess) + .then(user => { + autoAuthenticated.value = false + onAuthenticateSuccess(user) + }) .catch(onAuthenticateFailure) resetAuthForm() } diff --git a/src/services/auth/auth.service.ts b/src/services/auth/auth.service.ts index dfeaf5e3..7610af3f 100644 --- a/src/services/auth/auth.service.ts +++ b/src/services/auth/auth.service.ts @@ -1,5 +1,6 @@ import { User, UserApi } from '@/stores/user-manager.store.model' +const CREDENTIALS_ORIGIN = import.meta.env.VITE_CREDENTIALS_ORIGIN const LOGIN_URL = import.meta.env.VITE_LOGIN_URL const LOGOUT_URL = import.meta.env.VITE_LOGOUT_URL const USERINFO_URL = import.meta.env.VITE_USERINFO_URL @@ -23,6 +24,7 @@ export async function authenticate( }) const response = await fetch(LOGIN_URL, { method: 'POST', + credentials: CREDENTIALS_ORIGIN, headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, @@ -43,7 +45,9 @@ export async function authenticate( * @returns The api returns true if succeeded */ export async function logout() { - const response = await fetch(LOGOUT_URL) + const response = await fetch(LOGOUT_URL, { + credentials: CREDENTIALS_ORIGIN, + }) if (!response.ok) { throw new Error('Error while trying to logout user') @@ -62,6 +66,7 @@ export async function getUserInfo() { const payload = new URLSearchParams({}) const response = await fetch(USERINFO_URL, { method: 'POST', + credentials: CREDENTIALS_ORIGIN, headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, diff --git a/vite-dist.config.ts b/vite-dist.config.ts index 8e283661..d1377893 100644 --- a/vite-dist.config.ts +++ b/vite-dist.config.ts @@ -41,6 +41,7 @@ export default defineConfig(({ mode }) => { entry: resolve(__dirname, 'src/bundle/lib.ts'), name: 'luxembourg-geoportail', fileName: 'lux.dist', + formats: ['es'], // outputs only bundle/lux.dist.mjs }, commonjsOptions: { exclude: ['ol', 'mapbox-gl'], From 872eaff0fc485c3cbd206d382698212c9e900ec9 Mon Sep 17 00:00:00 2001 From: AlitaBernachot Date: Wed, 23 Oct 2024 14:20:10 +0200 Subject: [PATCH 2/4] doc: update doc for ALLOW_CORS --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 681a49fe..ab2bee40 100644 --- a/README.md +++ b/README.md @@ -289,7 +289,22 @@ VITE_CREDENTIALS_ORIGIN="include" # ⚠️ WARNING: don't use `"include"` value in production (but use `"same-origin"` instead). ``` -- in v3, add a new env variable in `docker-compose.yaml`: `ALLOW_CORS` and set it to value = `1` in the `.env.project` file to allow cors and cross origin requests. +- in v3, add a new env variable in `docker-compose.yaml`: `ALLOW_CORS` + +```yaml +# file: geoportailv3/docker-compose.yaml +geoportal: + extends: ... + volumes_from: ... + volumes: ... + environment: ... + - VECTORTILESURL + - ALLOW_CORS # <=== Add new var here! + ports: + - 8080:8080 +``` + +- and set it to value = `1` in the `.env.project` file to allow cors and cross origin requests. ```bash # file: geoportailv3/.env.project From 373ad2f87f54328e9a5decf6e9380bbc10892bbe Mon Sep 17 00:00:00 2001 From: AlitaBernachot Date: Wed, 23 Oct 2024 14:32:29 +0200 Subject: [PATCH 3/4] doc: add doc for CORS plugin --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index ab2bee40..569a1b4b 100644 --- a/README.md +++ b/README.md @@ -310,3 +310,11 @@ geoportal: # file: geoportailv3/.env.project ALLOW_CORS=1 # ⚠️WARNING: don't use this value in production ``` + +## 🛡️ By pass CORS in dev mode + +Because v4 is a standalone app with no backend, it uses sometimes local v3's backend and sometimes migration platform https://migration.geoportail.lu to perform api calls (see dedicated `.env` files to check urls). + +To ignore CORS errors when performing these calls, it is mandatory to use a plugin in your web browser (such as Use Allow CORS plugin for Chrome: https://mybrowseraddon.com/access-control-allow-origin.html). Without the plugin functionnalities such as MyMaps, authentication, MySymbols, ... won't work. + +💡 NB. For e2e testing, CORS securities have been deactivated with the Cypress option: `chromeWebSecurity: false` (only avaialable for Chrome browser). From 0c1d712e8d241cb6f0e5c3aa631d4ecb467f662a Mon Sep 17 00:00:00 2001 From: AlitaBernachot Date: Wed, 23 Oct 2024 15:19:18 +0200 Subject: [PATCH 4/4] refactor: switch to class for auth service --- src/components/auth/auth-form.spec.ts | 6 +- src/components/auth/auth-form.vue | 11 +- src/services/auth/auth.service.spec.ts | 18 +-- src/services/auth/auth.service.ts | 178 ++++++++++++------------- 4 files changed, 108 insertions(+), 105 deletions(-) diff --git a/src/components/auth/auth-form.spec.ts b/src/components/auth/auth-form.spec.ts index 98fbca06..bfc9cb95 100644 --- a/src/components/auth/auth-form.spec.ts +++ b/src/components/auth/auth-form.spec.ts @@ -2,7 +2,7 @@ import { shallowMount, VueWrapper } from '@vue/test-utils' import { createTestingPinia } from '@pinia/testing' import { useUserManagerStore } from '@/stores/user-manager.store' -import * as AuthService from '@/services/auth/auth.service' +import { authService } from '@/services/auth/auth.service' import AuthForm from './auth-form.vue' describe('AuthForm', () => { @@ -44,7 +44,7 @@ describe('AuthForm', () => { }) it('should call AuthService.authenticate on submit', async () => { - const authenticateMock = vi.spyOn(AuthService, 'authenticate') + const authenticateMock = vi.spyOn(authService, 'authenticate') const userNameInput = wrapper.find('input[name="userName"]') const userPasswordInput = wrapper.find('input[name="userPassword"]') @@ -83,7 +83,7 @@ describe('AuthForm', () => { }) it('should call AuthService.logout on logout', async () => { - const logoutMock = vi.spyOn(AuthService, 'logout') + const logoutMock = vi.spyOn(authService, 'logout') await wrapper.find('button').trigger('click') diff --git a/src/components/auth/auth-form.vue b/src/components/auth/auth-form.vue index b4c3c4a8..ba08fc35 100644 --- a/src/components/auth/auth-form.vue +++ b/src/components/auth/auth-form.vue @@ -3,7 +3,7 @@ import { onMounted, ref, watch } from 'vue' import { useTranslation } from 'i18next-vue' import { storeToRefs } from 'pinia' -import * as AuthService from '@/services/auth/auth.service' +import { authService } from '@/services/auth/auth.service' import { useAlertNotificationsStore } from '@/stores/alert-notifications.store' import { AlertNotificationType } from '@/stores/alert-notifications.store.model' import { useAppStore } from '@/stores/app.store' @@ -31,7 +31,8 @@ watch(authenticated, authenticated => { }) onMounted(() => { - AuthService.getUserInfo() + authService + .getUserInfo() .then(user => { autoAuthenticated.value = true onAuthenticateSuccess(user) @@ -42,7 +43,8 @@ onMounted(() => { }) function logout() { - AuthService.logout() + authService + .logout() .then(() => clearUser()) .catch(() => addNotification( @@ -54,7 +56,8 @@ function logout() { } function submit() { - AuthService.authenticate(userName.value, userPassword.value, isApp.value) + authService + .authenticate(userName.value, userPassword.value, isApp.value) .then(user => { autoAuthenticated.value = false onAuthenticateSuccess(user) diff --git a/src/services/auth/auth.service.spec.ts b/src/services/auth/auth.service.spec.ts index cbb1ac63..637f8513 100644 --- a/src/services/auth/auth.service.spec.ts +++ b/src/services/auth/auth.service.spec.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, MockedFunction, vi } from 'vitest' import { User, UserApi } from '@/stores/user-manager.store.model' -import { authenticate, logout, getUserInfo } from './auth.service' +import { authService } from './auth.service' global.fetch = vi.fn() @@ -51,16 +51,16 @@ describe('Auth service', () => { it('should authenticate user successfully and return user info', async () => { mockFetchUserApiSuccess() - const result = await authenticate('the_user', 'the_password') + const result = await authService.authenticate('the_user', 'the_password') expect(result).toStrictEqual(resultUserInfo) }) it('should throw error when authentication fails', async () => { mockFetchError() - await expect(authenticate('the_user', 'the_password')).rejects.toThrow( - 'Error while trying to authenticate user' - ) + await expect( + authService.authenticate('the_user', 'the_password') + ).rejects.toThrow('Error while trying to authenticate user') }) }) @@ -71,14 +71,14 @@ describe('Auth service', () => { text: vi.fn().mockResolvedValue('success'), }) - const result = await logout() + const result = await authService.logout() expect(result).toBe('success') }) it('should throw error when logout fails', async () => { mockFetchError() - await expect(logout()).rejects.toThrow( + await expect(authService.logout()).rejects.toThrow( 'Error while trying to logout user' ) }) @@ -88,14 +88,14 @@ describe('Auth service', () => { it('should get the user info', async () => { mockFetchUserApiSuccess() - const result = await getUserInfo() + const result = await authService.getUserInfo() expect(result).toStrictEqual(resultUserInfo) }) it('should throw error when getUserInfo fails', async () => { mockFetchError() - await expect(getUserInfo()).rejects.toThrow( + await expect(authService.getUserInfo()).rejects.toThrow( 'Error while trying to get user info' ) }) diff --git a/src/services/auth/auth.service.ts b/src/services/auth/auth.service.ts index 7610af3f..1e8e18d6 100644 --- a/src/services/auth/auth.service.ts +++ b/src/services/auth/auth.service.ts @@ -5,107 +5,107 @@ const LOGIN_URL = import.meta.env.VITE_LOGIN_URL const LOGOUT_URL = import.meta.env.VITE_LOGOUT_URL const USERINFO_URL = import.meta.env.VITE_USERINFO_URL -/** - * Calls "/login" url to authenticate user - * @param userName The user's name, mandatory - * @param userPassword The user's password, mandatory - * @param isApp If the app is mobile app mode, false by default - * @returns The api returns user's info is succeeded - */ -export async function authenticate( - userName: string, - userPassword: string, - isApp = false -) { - const payload = new URLSearchParams({ - login: userName, - password: userPassword, - app: isApp ? 'true' : 'false', - }) - const response = await fetch(LOGIN_URL, { - method: 'POST', - credentials: CREDENTIALS_ORIGIN, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - body: payload, - }) +export class AuthService { + /** + * Calls "/login" url to authenticate user + * @param userName The user's name, mandatory + * @param userPassword The user's password, mandatory + * @param isApp If the app is mobile app mode, false by default + * @returns The api returns user's info is succeeded + */ + async authenticate(userName: string, userPassword: string, isApp = false) { + const payload = new URLSearchParams({ + login: userName, + password: userPassword, + app: isApp ? 'true' : 'false', + }) + const response = await fetch(LOGIN_URL, { + method: 'POST', + credentials: CREDENTIALS_ORIGIN, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: payload, + }) - if (!response.ok) { - throw new Error('Error while trying to authenticate user') + if (!response.ok) { + throw new Error('Error while trying to authenticate user') + } + + const data = await response.json() + + return this.mapUserApiToUser(data) } - const data = await response.json() + /** + * Calls "/logout" url to log out the user + * @returns The api returns true if succeeded + */ + async logout() { + const response = await fetch(LOGOUT_URL, { + credentials: CREDENTIALS_ORIGIN, + }) - return mapUserApiToUser(data) -} + if (!response.ok) { + throw new Error('Error while trying to logout user') + } -/** - * Calls "/logout" url to log out the user - * @returns The api returns true if succeeded - */ -export async function logout() { - const response = await fetch(LOGOUT_URL, { - credentials: CREDENTIALS_ORIGIN, - }) + const data = await response.text() - if (!response.ok) { - throw new Error('Error while trying to logout user') + return data } - const data = await response.text() - - return data -} + /** + * Calls "/getuserinfo" url to get user's info, the user needs to be authenticated first + * @returns The api returns user's info is succeeded + */ + async getUserInfo() { + const payload = new URLSearchParams({}) + const response = await fetch(USERINFO_URL, { + method: 'POST', + credentials: CREDENTIALS_ORIGIN, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: payload, + }) -/** - * Calls "/getuserinfo" url to get user's info, the user needs to be authenticated first - * @returns The api returns user's info is succeeded - */ -export async function getUserInfo() { - const payload = new URLSearchParams({}) - const response = await fetch(USERINFO_URL, { - method: 'POST', - credentials: CREDENTIALS_ORIGIN, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - body: payload, - }) + const data = response.ok && (await response.json()) - const data = response.ok && (await response.json()) + if (!data.login) { + throw new Error('Error while trying to get user info') + } - if (!data.login) { - throw new Error('Error while trying to get user info') + return this.mapUserApiToUser(data) } - return mapUserApiToUser(data) -} - -/** - * Transform User info returned by api to User model - * @param userApi The user info returned by the api - */ -function mapUserApiToUser(userApi: UserApi) { - const { - is_admin, - login, - mail, - mymaps_role, - role, - role_id, - sn, - typeUtilisateur, - } = userApi - return { - login, - mail, - isAdmin: is_admin, - mymapsRole: mymaps_role, - name: sn, - role, - roleId: role_id, - sn, - typeUtilisateur, + /** + * Transform User info returned by api to User model + * @param userApi The user info returned by the api + */ + private mapUserApiToUser(userApi: UserApi) { + const { + is_admin, + login, + mail, + mymaps_role, + role, + role_id, + sn, + typeUtilisateur, + } = userApi + return { + login, + mail, + isAdmin: is_admin, + mymapsRole: mymaps_role, + name: sn, + role, + roleId: role_id, + sn, + typeUtilisateur, + } } } + +export const authService = new AuthService()