import axios from 'axios' import { defineStore } from 'pinia' import { computed, ref } from 'vue' import { components as steeringTypes } from '../steering-types' import { components as tankTypes } from '../tank-types' import { createUnpaginatedAPIStore, createSteeringURL, createTankURL } from '@/api' import { getUser, oidcManager } from '@/oidc' const log = globalThis.console export type CurrentUser = { name: string email: string oidcAccessToken: string tankSessionToken: string } export type SteeringUser = steeringTypes['schemas']['User'] export const useUserStore = createUnpaginatedAPIStore<SteeringUser>( 'steeringUser', createSteeringURL.prefix('users'), { getRequestDefaults() { const authStore = useAuthStore() return authStore.currentUser ? { headers: { Authorization: `Bearer ${authStore.currentUser.oidcAccessToken}` } } : {} }, }, ) async function createTankSession( accessToken: string, ): Promise<tankTypes['schemas']['auth.NewSessionResponse']> { const res = await fetch(`${import.meta.env.VUE_APP_TANK}/auth/session`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ backend: 'oidc', arguments: { access_token: accessToken, token_type: 'Bearer', }, }), }) if (res.ok) { return await res.json() } else { throw new Error('Could not initialize Tank session.') } } export const useAuthStore = defineStore('auth', () => { const currentUser = ref<CurrentUser>() const _steeringUser = ref<steeringTypes['schemas']['User']>() const steeringUser = computed(() => (currentUser.value ? _steeringUser.value : undefined)) const isSuperuser = computed(() => steeringUser.value?.isSuperuser === true) async function loadUser() { const oidcUser = await getUser() const userStore = useUserStore() const users = await userStore.list({ headers: { Authorization: `Bearer ${oidcUser.access_token}` }, }) const user = users.find((user: SteeringUser) => user.username === oidcUser.profile?.username) // now that we have a valid token, we can create a session with tank let tankSessionToken: string try { tankSessionToken = (await createTankSession(oidcUser.access_token))?.token ?? '' log.debug('Tank session token:', tankSessionToken) } catch (e) { log.error('Could not create tank session.', e) return } if (!tankSessionToken) { log.error('Tank session creation was successful, but no token has been assigned.') return } _steeringUser.value = user currentUser.value = { name: oidcUser.profile.nickname ?? '', email: oidcUser.profile.email ?? '', oidcAccessToken: oidcUser.access_token, tankSessionToken, } } async function init() { log.debug('Initializing oidc client') oidcManager.events.addUserSignedOut(async () => { log.debug('User has signed out. Resetting auth store.') currentUser.value = undefined }) oidcManager.events.addAccessTokenExpiring(async () => { log.debug('User token expiration imminent. Starting silent access token renewal.') try { const user = await oidcManager.signinSilent() if (currentUser.value) { log.debug('Successfully renewed user access token.') currentUser.value.oidcAccessToken = user.access_token } } catch (e) { log.error('Silent OIDC access token renewal has failed.', e) } }) oidcManager.events.addAccessTokenExpired(() => { log.debug('OIDC token has expired. Logging out...') currentUser.value = undefined }) try { await loadUser() } catch (e) { log.debug('Could not load user data.') currentUser.value = undefined _steeringUser.value = undefined } } return { currentUser, steeringUser, isSuperuser, init } }) axios.interceptors.request.use((config) => { const url = config?.url const authStore = useAuthStore() if (!url || !authStore.currentUser) return config if (url.startsWith(createSteeringURL())) { config.headers.set('Authorization', `Bearer ${authStore.currentUser.oidcAccessToken}`) } if (url.startsWith(createTankURL())) { config.headers.set('Authorization', `Bearer ${authStore.currentUser.tankSessionToken}`) } return config }) export const steeringAuthInit: { getRequestDefaults: () => RequestInit } = { getRequestDefaults() { const authStore = useAuthStore() return { headers: { Authorization: `Bearer ${authStore.currentUser?.oidcAccessToken}`, }, } }, } export const tankAuthInit: { getRequestDefaults: () => RequestInit } = { getRequestDefaults() { const authStore = useAuthStore() return { headers: { Authorization: `Bearer ${authStore.currentUser?.tankSessionToken}`, }, } }, }