Source: api/auth.js

import {Config} from "../config.js"
import {AccessLevel, UserClaims} from "../types/types.js"
import {http} from "../utils/http/index.js"

let _userClaims = new Map()
let _apiKey = undefined
let _getIdToken = async () => {
	throw new Error("not implemented")
}

/**
 * @module auth
 */


/**
 * setting Api key
 * @param {object} [param0={}]
 * @param {Number} [param0.apiKey] - optional variable defining user's API key
 * @param {Number} [param0.getIdToken] - optional async function to get user's idToken
 */
export function configure({apiKey, getIdToken}) {
	if (apiKey)
		_apiKey = apiKey
	if (getIdToken)
		_getIdToken = getIdToken
	_userClaims.clear()
}

/**
 * Manually setting userToken
 * @param {string} token
 * @param {AccessLevel} accessLevel
 */
export function setUserToken(token, accessLevel = AccessLevel.USER) {
	if (!token)
		return
	let c = new UserClaims(token)
	if (!c.isExpired())
		_userClaims.set(accessLevel, c)
}

/**
 *
 * @returns {UserClaims}
 */
export async function getUserClaims() {
	return getUserToken()
		.then(_token => {
			return _userClaims.get(AccessLevel.USER)
		})
}

/**
 *
 * @async
 * @param {object} [param0={}]
 * @param {Number} [param0.minExpiry] - optional variable defining minimum expiry time in seconds
 * @param {AccessLevel} [param0.accessLevel=AccessLevel.USER]
 * @returns {string} - user Token.
 */
export async function getUserToken({minExpiry, accessLevel} = {}) {
	minExpiry = minExpiry || (30 * 60)
	accessLevel = accessLevel || AccessLevel.USER
	let claim = _userClaims.get(accessLevel)
	let refresh = false

	let exp = claim ? claim.getSecondsUntilExpiry() : 0

	//at least 1 minute expiry time
	if (exp < 60) {
		claim = undefined
		refresh = true
	} else if (exp < minExpiry)
		refresh = true

	if (refresh) {
		return _refreshUserToken(accessLevel)
			.then(token => {
				setUserToken(token, accessLevel)
				return token
			})
			.catch(_err => {
				if (claim)
					return claim.token
				else
					throw new Error("unable to create user token")
			})
	} else {
		if (claim)
			return claim.token
		else
			throw new Error("could not create user token")
	}
}

/**
 * clear user.
 */
export function logout() {
	_apiKey = undefined
	_userClaims.clear()
}

/**
 *
 * Generate a new API Key, old API KEY will be discareded
 * It require a userToken to be set.
 * @returns {string} api key
 */
export async function generateAPIKey() {
	let headers = {
		"Content-Type": "application/json",
		Accept: "application/json",
		"x-jibb-user-jwt": await getUserToken(),
	}
	let response = await http.get(`${Config.apiBaseURL}/v1/auth/apikey`, headers)
	return response.data.apiKey
}

/**
 *
 * Generating a one time password.
 * It require a IdToken to be set.
 * @async
 * @returns  {string} - Custom Password.
 */
export async function generateCustomAuthPassword() {
	let headers = {
		"Content-Type": "application/json",
		Accept: "application/json",
		"x-jibb-id-jwt": await _getIdToken(),
	}

	let response = await http.get(`${Config.apiBaseURL}/v1/auth/custom`, headers)
	return response.data.password
}

async function _getUserTokenFromApiKey(accessLevel) {
	let headers = {
		"Content-Type": "application/json",
		Accept: "application/json",
	}
	let body = {
		api_key: _apiKey,
	}
	let url

	switch (accessLevel) {
		case AccessLevel.ADMIN:
			url = `${Config.apiBaseURL}/v1/admin/auth/token`
			break
		case AccessLevel.SUPERADMIN:
			url = `${Config.apiBaseURL}/v1/superadmin/auth/token`
			break
		default:
			url = `${Config.apiBaseURL}/v1/auth/token`
			break
	}

	let response = await http.post(url, body, headers)
	return response.data.token
}


async function _getUserTokenFromIDToken(accessLevel) {
	let headers = {
		"Content-Type": "application/json",
		Accept: "application/json",
		"x-jibb-id-jwt": await _getIdToken(),
	}
	let body = {}
	let url

	switch (accessLevel) {
		case AccessLevel.ADMIN:
			url = `${Config.apiBaseURL}/v1/admin/auth/token`
			break
		case AccessLevel.SUPERADMIN:
			url = `${Config.apiBaseURL}/v1/superadmin/auth/token`
			break
		default:
			url = `${Config.apiBaseURL}/v1/auth/token`
			break
	}

	let response = await http.post(url, body, headers)
	return response.data.token
}


async function _refreshUserToken(accessLevel) {
	if (_apiKey) {
		return _getUserTokenFromApiKey(accessLevel)
	}

	return _getUserTokenFromIDToken(accessLevel)
}