diff --git a/docker-compose.yml b/docker-compose.yml index 4fffbf9e7e8162e93d1dbecc3ab5c01056244e8c..ea9260df449a4a37fa2d999955ad313a0af156cd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,7 +32,7 @@ networks: services: fda-metadata-db: - restart: on-failure + restart: no container_name: dbrepo-metadata-db hostname: metadata-db build: ./fda-metadata-db @@ -50,7 +50,7 @@ services: driver: json-file fda-discovery-service: - restart: on-failure + restart: no container_name: dbrepo-discovery-service hostname: discovery-service build: ./fda-discovery-service @@ -65,7 +65,7 @@ services: driver: json-file fda-gateway-service: - restart: on-failure + restart: no container_name: dbrepo-gateway-service hostname: gateway-service build: ./fda-gateway-service @@ -83,7 +83,7 @@ services: driver: json-file fda-database-service: - restart: on-failure + restart: no container_name: dbrepo-database-service hostname: database-service build: ./fda-database-service @@ -108,7 +108,7 @@ services: driver: json-file fda-container-service: - restart: on-failure + restart: no container_name: dbrepo-container-service hostname: container-service build: ./fda-container-service @@ -128,7 +128,7 @@ services: driver: json-file fda-authentication-service: - restart: on-failure + restart: no container_name: dbrepo-authentication-service hostname: authentication-service image: fda-authentication-service @@ -152,7 +152,7 @@ services: driver: json-file fda-query-service: - restart: on-failure + restart: no container_name: dbrepo-query-service hostname: query-service build: ./fda-query-service @@ -175,7 +175,7 @@ services: driver: json-file fda-table-service: - restart: on-failure + restart: no container_name: dbrepo-table-service hostname: table-service build: ./fda-table-service @@ -201,7 +201,7 @@ services: driver: json-file fda-identifier-service: - restart: on-failure + restart: no container_name: dbrepo-identifier-service hostname: identifier-service build: ./fda-identifier-service @@ -223,7 +223,7 @@ services: driver: json-file fda-metadata-service: - restart: on-failure + restart: no container_name: dbrepo-metadata-service hostname: metadata-service build: ./fda-metadata-service @@ -243,7 +243,7 @@ services: driver: json-file fda-analyse-service: - restart: on-failure + restart: no container_name: dbrepo-analyse-service hostname: analyse-service build: ./fda-analyse-service @@ -265,7 +265,7 @@ services: driver: json-file fda-user-service: - restart: on-failure + restart: no container_name: dbrepo-user-service hostname: user-service build: ./fda-user-service @@ -285,7 +285,7 @@ services: driver: json-file fda-semantics-service: - restart: on-failure + restart: no container_name: dbrepo-semantics-service hostname: semantics-service build: ./fda-semantics-service @@ -310,7 +310,7 @@ services: driver: json-file fda-broker-service: - restart: on-failure + restart: no container_name: dbrepo-broker-service hostname: broker-service build: ./fda-broker-service @@ -354,7 +354,7 @@ services: driver: json-file fda-ui: - restart: on-failure + restart: no container_name: dbrepo-ui hostname: ui build: ./fda-ui diff --git a/fda-ui/api/authentication.service.js b/fda-ui/api/authentication.service.js index 78bacea4b2d6232f9aebd92101374dc56bcc2b56..cf36d2cc469fa275e7a96734488074db6c9ced99 100644 --- a/fda-ui/api/authentication.service.js +++ b/fda-ui/api/authentication.service.js @@ -1,26 +1,66 @@ import Vue from 'vue' import qs from 'qs' -import api from '@/api/api' -import { api as endpoint, clientSecret } from '@/config' +import { setRefreshToken, setToken } from '@/server-middleware/store' +import axios from 'axios' +import { clientSecret } from '@/config' +/** + * Service class for interaction with Authentication Service in the back end. + * + * @author Martin Weise + * @description This service needs **important** its own axios instance for calls to the back end, otherwise it creates + * an infinite loop with the interceptors. + */ class AuthenticationService { - authenticate (username, password) { + /** + * Authenticates a user in the back end with their username and password credential. + * @param username The username. + * @param password The password credential. + */ + authenticatePlain (username, password) { const payload = { client_id: 'dbrepo-client', username, password, grant_type: 'password', client_secret: clientSecret, - scope: 'openid profile roles' + scope: 'openid profile roles attributes' } - api.post(`${endpoint.replace('http:', 'https:')}/api/auth/realms/dbrepo/protocol/openid-connect/token`, qs.stringify(payload), { - headers: { ContentType: 'application/form-data' } + return this._authenticate(payload) + } + + authenticateToken (refreshToken) { + const payload = { + client_id: 'dbrepo-client', + grant_type: 'refresh_token', + client_secret: clientSecret, + refresh_token: refreshToken + } + return this._authenticate(payload) + } + + _authenticate (payload) { + axios.post('/api/auth/realms/dbrepo/protocol/openid-connect/token', qs.stringify(payload), { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } }).then((response) => { - console.info('====>', response) + const authentication = response.data + // eslint-disable-next-line camelcase + const { access_token, refresh_token } = authentication + console.debug('response authenticate', authentication) + setToken(access_token) + setRefreshToken(refresh_token) + return authentication }).catch((error) => { console.error('Failed to authenticate', error) - const { code, message } = error - Vue.$toast.error(`[${code}] Failed to authenticate: ${message}`) + const { code, message, response } = error + const { status } = response + if (status === 401) { + Vue.$toast.error('Invalid username-password combination.') + } else { + Vue.$toast.error(`[${code}] Failed to authenticate: ${message}`) + } }) } } diff --git a/fda-ui/api/container.service.js b/fda-ui/api/container.service.js new file mode 100644 index 0000000000000000000000000000000000000000..2a6a3297bfae6a550b41eaf2e29fa399d874a041 --- /dev/null +++ b/fda-ui/api/container.service.js @@ -0,0 +1,62 @@ +import Vue from 'vue' +import api from '@/api' + +class ContainerService { + findAll (limit = 100) { + return new Promise((resolve, reject) => { + api.get(`/api/container?limit=${limit}`, { headers: { Accept: 'application/json' } }) + .then((response) => { + const containers = response.data + console.debug('response containers', containers) + resolve(containers) + }) + .catch((error) => { + const { code, message } = error + console.error('Failed to load containers', error) + Vue.$toast.error(`[${code}] Failed to load containers: ${message}`) + reject(error) + }) + }) + } + + findOne (id) { + api.get(`/api/container/${id}`, { headers: { Accept: 'application/json' } }) + .then((response) => { + const container = response.data + console.debug('response container', container) + return container + }).catch((error) => { + const { code, message } = error + console.error('Failed to load container', error) + Vue.$toast.error(`[${code}] Failed to load container: ${message}`) + }) + } + + create (data) { + api.post('/api/container', data, { headers: { Accept: 'application/json' } }) + .then((response) => { + const container = response.data + console.debug('response container', container) + return container + }).catch((error) => { + const { code, message } = error + console.error('Failed to create container', error) + Vue.$toast.error(`[${code}] Failed to create container: ${message}`) + }) + } + + modify (id, action) { + api.put(`/api/container/${id}`, { action }, { headers: { Accept: 'application/json' } }) + .then((response) => { + const container = response.data + console.debug('response container', container) + return container + }).catch((error) => { + const { code, message } = error + console.error('Failed to modify container', error) + Vue.$toast.error(`[${code}] Failed to modify container: ${message}`) + }) + } +} + +export default new ContainerService() diff --git a/fda-ui/api/container/index.js b/fda-ui/api/container/index.js deleted file mode 100644 index 594657500b1fa953ce3592333fe1a01940da9f47..0000000000000000000000000000000000000000 --- a/fda-ui/api/container/index.js +++ /dev/null @@ -1,28 +0,0 @@ -const axios = require('axios/dist/browser/axios.cjs') - -export function listContainers (limit) { - return axios.get(`/api/container?limit=${limit}`) -} - -export function findContainer (containerId) { - return axios.get(`/api/container${containerId}`) -} - -export function createContainer (token, payload) { - return axios.post('/api/container/', payload, { - headers: { - Authorization: `Bearer ${token}` - } - }) -} - -export function startContainer (token, containerId) { - const payload = { - action: 'start' - } - return axios.put(`/api/container/${containerId}`, payload, { - headers: { - Authorization: `Bearer ${token}` - } - }) -} diff --git a/fda-ui/api/api.js b/fda-ui/api/index.js similarity index 83% rename from fda-ui/api/api.js rename to fda-ui/api/index.js index 8c08db991372adb36630bbbde5e12148ca00209b..d26eeaab7be40a4f0b7450a251ba12f46fdb6e50 100644 --- a/fda-ui/api/api.js +++ b/fda-ui/api/index.js @@ -1,11 +1,9 @@ import https from 'https' import axios from 'axios' -import api from '@/config' const httpsAgent = new https.Agent({ rejectUnauthorized: false }) const instance = axios.create({ - baseURL: api, timeout: 10000, params: {}, httpsAgent diff --git a/fda-ui/api/user/index.js b/fda-ui/api/user/index.js index dd32580f3386a0d1c50fc3cd9d42b414fa9cc8a8..b313891ff34081f2c91e94d52e26ff1112c1325d 100644 --- a/fda-ui/api/user/index.js +++ b/fda-ui/api/user/index.js @@ -1,6 +1,6 @@ // eslint-disable-next-line camelcase import jwt_decode from 'jwt-decode' -import api from '../api' +import api from '../index' const qs = require('qs') export function updateUser (token, userId, data) { diff --git a/fda-ui/components/DatabaseList.vue b/fda-ui/components/DatabaseList.vue index 2259d7e86ce9a834347e0e3bc9a152efa0c640a2..01d50e8cef1cf3d407cffffee21fc2ee79d2d424 100644 --- a/fda-ui/components/DatabaseList.vue +++ b/fda-ui/components/DatabaseList.vue @@ -51,8 +51,8 @@ <script> import { formatCreators, formatUser, formatYearUTC, isResearcher } from '@/utils' -import { startContainer } from '@/api/container' import { createDatabase } from '@/api/database' +import ContainerService from '@/api/container.service' export default { data () { @@ -138,36 +138,19 @@ export default { } return container.database.identifier.description }, - async loadContainers () { + loadContainers () { this.createDbDialog = false - try { - this.loadingContainers = true - const res = await this.$axios.get(`/api/container?limit=${this.limit}`) - this.containers = res.data - console.debug('containers', this.containers) - this.error = false - } catch (error) { - this.error = true - console.error('Failed to retrieve containers', error) - const { statusText } = error.response - this.$toast.error(`Failed to retrieve containers: ${statusText}`) - } + this.loadingContainers = true + ContainerService.findAll(this.limit) + .then((containers) => { + this.containers = containers + console.info('Found', this.containers.length, 'container(s)') + }) this.loadingContainers = false }, async startContainer (container) { - try { - container.loading = true - const res = await startContainer(this.token, container.id) - console.debug('started container', res.data) - this.error = false - } catch (error) { - console.error('start container', error) - const { status } = error.response - if (status !== 409) { - this.error = true - this.$toast.error('Failed to start container') - } - } + container.loading = true + await ContainerService.modify(container.id, 'start') container.loading = false }, async createDatabase (container) { diff --git a/fda-ui/components/dialogs/CreateDB.vue b/fda-ui/components/dialogs/CreateDB.vue index 9e9a6ceaf8a846e79360bb2f112706b4069cef6e..44adbbdb217cd94bf2784108a7d053f678614954 100644 --- a/fda-ui/components/dialogs/CreateDB.vue +++ b/fda-ui/components/dialogs/CreateDB.vue @@ -54,9 +54,9 @@ </template> <script> -const { notEmpty } = require('@/utils') -const { createContainer, startContainer } = require('@/api/container') -const { createDatabase } = require('@/api/database') +import { notEmpty } from '@/utils' +import ContainerService from '@/api/container.service' +import { createDatabase } from '@/api/database' export default { data () { @@ -142,32 +142,14 @@ export default { async createContainer () { this.createContainerDto.repository = this.engine.repository this.createContainerDto.tag = this.engine.tag - try { - this.loading = true - const res = await createContainer(this.token, this.createContainerDto) - this.container = res.data - console.debug('created container', this.container) - this.error = false - } catch (error) { - console.error('create container', error) - this.error = true - this.$toast.error('Failed to create container') - } + this.loading = true + this.container = await ContainerService.create(this.createContainerDto) + this.error = false this.loading = false }, async startContainer (container) { - try { - this.loading = true - const res = await startContainer(this.token, container.id) - console.debug('started container', res.data) - this.error = false - } catch (error) { - const { status } = error.response - if (status !== 409) { - this.error = true - this.$toast.error('Failed to start container') - } - } + this.loading = true + await ContainerService.modify(container.id, 'start') this.loading = false }, async createDatabase (container) { diff --git a/fda-ui/config.js b/fda-ui/config.js index b2ea4ef41e7c38bb63c62df110c71fbfd78cb82d..bff062a685fa2d01f6692136b798ae069b467dd3 100644 --- a/fda-ui/config.js +++ b/fda-ui/config.js @@ -1,6 +1,6 @@ const config = {} -config.api = process.env.API || 'http://localhost:9095' +config.api = process.env.API || 'http://localhost:3000/api' config.search = process.env.SEARCH || 'http://localhost:9200' config.sandbox = process.env.SANDBOX || false config.title = process.env.TITLE || 'Database Repository' @@ -14,7 +14,7 @@ config.mailVerify = process.env.MAIL_VERIFY || false config.tokenMax = process.env.TOKEN_MAX || 5 config.elasticPassword = process.env.ELASTIC_PASSWORD || 'elastic' config.elasticPassword = process.env.ELASTIC_PASSWORD || 'elastic' -config.clientSecret = process.env.DBREPO_CLIENT_SECRET +config.clientSecret = process.env.DBREPO_CLIENT_SECRET || 'MUwRc7yfXSJwX8AdRMWaQC3Nep1VjwgG' config.defaultPublisher = process.env.DEFAULT_PID_PUBLISHER module.exports = config diff --git a/fda-ui/nuxt.config.js b/fda-ui/nuxt.config.js index 0f3697cc50e764aca13fbe3375fd23f69ea80767..0800379cf2485b00f0efa323a78bb227cb17dddb 100644 --- a/fda-ui/nuxt.config.js +++ b/fda-ui/nuxt.config.js @@ -32,7 +32,6 @@ export default { plugins: [ { src: '@/plugins/axios', ssr: false }, - { src: '@/plugins/keycloak', ssr: false }, { src: '@/plugins/toast', ssr: false }, { src: '@/plugins/vendors', ssr: false }, { src: '@/plugins/axios', ssr: false }, diff --git a/fda-ui/package.json b/fda-ui/package.json index 8e4584db51a1e5c88a1ce1d879d02cde96bb4990..ff23e62ae94a9fff5f6ad2d24cc4092fa5e861d5 100644 --- a/fda-ui/package.json +++ b/fda-ui/package.json @@ -37,7 +37,6 @@ "is-docker": "^2.2.1", "jose": "^4.9.2", "jwt-decode": "^3.1.2", - "keycloak-js": "^21.0.2", "knex": "^0.95.6", "lodash": "^4.17.21", "moment": "^2.29.1", diff --git a/fda-ui/pages/container/_container_id/database/_database_id/table/import.vue b/fda-ui/pages/container/_container_id/database/_database_id/table/import.vue index 9a97285bd69bcbaa431980add330782916521c19..37d0ad3816f0492cb211387bae0be5a123c40d51 100644 --- a/fda-ui/pages/container/_container_id/database/_database_id/table/import.vue +++ b/fda-ui/pages/container/_container_id/database/_database_id/table/import.vue @@ -187,7 +187,7 @@ <script> import TableSchema from '@/components/TableSchema' import { notEmpty, isNonNegativeInteger, isResearcher } from '@/utils' -import { findContainer } from '@/api/container' +import ContainerService from '@/api/container.service' import { listTables, createTable, dataImport } from '@/api/table' import { determineDataTypes } from '@/api/analyse' @@ -381,17 +381,9 @@ export default { column.unique = true }, async loadDateFormats () { - let getResult - try { - this.loading = true - getResult = await findContainer(this.$route.params.container_id) - this.dateFormats = getResult.data.image.date_formats - console.debug('retrieve image date formats', this.dateFormats) - this.loading = false - } catch (err) { - this.loading = false - console.error('retrieve image date formats failed', err) - } + this.loading = true + this.dateFormats = await ContainerService.findOne(this.$route.params.container_id).image.date_formats + this.loading = true }, async createTable () { /* make enum values to array */ diff --git a/fda-ui/pages/login.vue b/fda-ui/pages/login.vue index a1c04ad0c3c809f4e9a806bb78eb1fba202f850c..68e9b08d8221c8983580e78e9f61db9fa9e7f732 100644 --- a/fda-ui/pages/login.vue +++ b/fda-ui/pages/login.vue @@ -59,7 +59,7 @@ <script> import { getThemeDark, findUser } from '@/api/user' -import authenticationService from '@/api/authentication.service' +import AuthenticationService from '@/api/authentication.service' export default { data () { return { @@ -108,21 +108,11 @@ export default { submit () { this.$refs.form.validate() }, - login () { + async login () { this.loading = true - authenticationService.authenticate(this.username, this.password) - // // eslint-disable-next-line camelcase - // const { access_token } = res.data - // this.$store.commit('SET_TOKEN', access_token) - // const roles = tokenToRoles(access_token) - // this.$store.commit('SET_ROLES', roles) - // await this.setTheme() - // await this.$router.push({ path: this.$route.query.redirect ? this.$route.query.redirect : '/container' }) - // } catch (error) { - // console.error('Failed to login', error) - // const { statusText } = error.response - // this.$toast.error(`Failed to login: ${statusText}`) - // this.loading = false + AuthenticationService.authenticatePlain(this.username, this.password) + await this.$router.push({ path: this.$route.query.redirect ? this.$route.query.redirect : '/container' }) + this.loading = false }, async setTheme () { try { diff --git a/fda-ui/plugins/axios.js b/fda-ui/plugins/axios.js index 384459494898185ed76aaea5bc58b5d4e042236b..909161f0818ce506593095f55129a4cf88a2be01 100644 --- a/fda-ui/plugins/axios.js +++ b/fda-ui/plugins/axios.js @@ -1,20 +1,30 @@ import Vue from 'vue' -import api from '@/api/api' +import api from '@/api' +import AuthenticationService from '@/api/authentication.service' +import { getRefreshToken, getToken, setRefreshToken, setToken } from '@/server-middleware/store' +import jwtDecode from 'jwt-decode' api.interceptors.request.use((config) => { - console.debug('loading token', Vue.$keycloak.token, Vue.$keycloak.refreshToken) - Vue.$keycloak.updateToken(70) - .then(() => { - const token = String(Vue.$keycloak.token) - config.headers.common.Authorization = `Bearer ${token}` - return config - }) - .catch((error) => { - console.error('Failed to update token', error) - }) -}, function (error) { - // Do something with request error - return Promise.reject(error) + const token = getToken() + if (!token) { + return config + } + const { exp } = jwtDecode(token) + if (new Date(exp) <= new Date()) { + /* token expired */ + const refreshToken = getRefreshToken() + const { exp2 } = jwtDecode(refreshToken) + if (new Date(exp2) <= new Date()) { + /* refresh token expired */ + setToken(null) + setRefreshToken(null) + } + AuthenticationService.authenticateToken(refreshToken) + return config + } + console.debug('interceptor inject authorization header', exp) + config.headers.Authorization = `Bearer ${token}` + return config }) Vue.use(api) diff --git a/fda-ui/plugins/keycloak.js b/fda-ui/plugins/keycloak.js deleted file mode 100644 index c6656cae9df56e68236ecf51dafefe852084bca8..0000000000000000000000000000000000000000 --- a/fda-ui/plugins/keycloak.js +++ /dev/null @@ -1,31 +0,0 @@ -import Vue from 'vue' -import Keycloak from 'keycloak-js' -import api from '../config' - -const initOptions = { - url: api + '/api/auth', - realm: 'dbrepo', - clientId: 'dbrepo-client' -} - -const _keycloak = new Keycloak(initOptions) - -const Plugin = { - install: (Vue) => { - Vue.$keycloak = _keycloak - } -} -Plugin.install = (Vue) => { - Vue.$keycloak = _keycloak - Object.defineProperties(Vue.prototype, { - $keycloak: { - get () { - return _keycloak - } - } - }) -} - -Vue.use(Plugin) - -export default Plugin diff --git a/fda-ui/server-middleware/store.js b/fda-ui/server-middleware/store.js new file mode 100644 index 0000000000000000000000000000000000000000..8a2a1e0dace8c3a55eb2dc8c8b908f77128f0659 --- /dev/null +++ b/fda-ui/server-middleware/store.js @@ -0,0 +1,23 @@ +export function setToken (value) { + localStorage.setItem('token', JSON.stringify(value)) +} + +export function getToken () { + return JSON.parse(localStorage.getItem('token')) +} + +export function setRefreshToken (value) { + localStorage.setItem('refresh_token', JSON.stringify(value)) +} + +export function getRefreshToken () { + return JSON.parse(localStorage.getItem('refresh_token')) +} + +export function setUser (user) { + localStorage.setItem('user', JSON.stringify(user)) +} + +export function getUser () { + return JSON.parse(localStorage.getItem('user')) +} diff --git a/fda-ui/yarn.lock b/fda-ui/yarn.lock index 5fbbde314c9d3ac7290605df747fc9407b6535e5..bff2f60529e64d54be07f685b751033d20e69b24 100644 --- a/fda-ui/yarn.lock +++ b/fda-ui/yarn.lock @@ -2928,7 +2928,7 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.0.2, base64-js@^1.3.1, base64-js@^1.5.1: +base64-js@^1.0.2, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -5728,15 +5728,6 @@ fromentries@^1.2.0: resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.3.2.tgz#e4bca6808816bf8f93b52750f1127f5a6fd86e3a" integrity sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg== -fs-extra@^11.1.1: - version "11.1.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" - integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -7501,11 +7492,6 @@ js-cookie@^3.0.0: resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.1.tgz#9e39b4c6c2f56563708d7d31f6f5f21873a92414" integrity sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw== -js-sha256@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" - integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== - js-string-escape@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" @@ -7645,14 +7631,6 @@ jwt-decode@^3.1.2: resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== -keycloak-js@^21.0.2: - version "21.0.2" - resolved "https://registry.yarnpkg.com/keycloak-js/-/keycloak-js-21.0.2.tgz#1d3c2079d3c23850df4f253a868926861c51c488" - integrity sha512-i05i3VBPhQ867EgjA+OYPlf8YUPiUwtrU2zv4j8tvZIdRvhJY8f+mp1ZvRJl/GMRb+XhJs9BDknyBMrIspwDkw== - dependencies: - base64-js "^1.5.1" - js-sha256 "^0.9.0" - keyv@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"