{"size": 2367, "ext": "js", "max_stars_repo_path": "backend/lib/security/jose.js", "max_stars_repo_name": "nbnco-public/dashboard", "max_stars_repo_stars_event_min_datetime": null, "max_stars_repo_stars_event_max_datetime": null, "max_issues_repo_path": "backend/lib/security/jose.js", "max_issues_repo_name": "nbnco-public/dashboard", "max_issues_repo_issues_event_min_datetime": null, "max_issues_repo_issues_event_max_datetime": null, "max_forks_repo_path": "backend/lib/security/jose.js", "max_forks_repo_name": "nbnco-public/dashboard", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": null, "max_issues_count": null, "max_forks_count": null, "avg_line_length": 26.595505618, "max_line_length": 92, "alphanum_fraction": 0.6692015209}
//
// SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors
//
// SPDX-License-Identifier: Apache-2.0
//

'use strict'

const { isPlainObject } = require('lodash')
const { promisify } = require('util')
const jwt = require('jsonwebtoken')
const crypto = require('crypto')
const jose = require('jose')
const uuid = require('uuid')
const base64url = require('base64url')

const jwtSign = promisify(jwt.sign)
const jwtVerify = promisify(jwt.verify)

function importSymmetricKey (sessionSecret) {
  const decodedSessionSecret = decodeSecret(sessionSecret)
  return crypto.createSecretKey(decodedSessionSecret)
}

function decodeSecret (input) {
  let value = base64url.decode(input)
  if (base64url(value) === input) {
    return value
  }
  value = Buffer.from(input, 'base64')
  if (value.toString('base64') === input) {
    return value
  }
  return Buffer.from(input)
}

function encodeState (data = {}) {
  return base64url.encode(JSON.stringify(data))
}

function decodeState (state) {
  try {
    return JSON.parse(base64url.decode(state))
  } catch (err) {
    return {}
  }
}

module.exports = sessionSecret => {
  const symetricKey = importSymmetricKey(sessionSecret)
  const encoder = new TextEncoder()
  const decoder = new TextDecoder()
  return {
    encodeState,
    decodeState,
    sign (payload, secretOrPrivateKey, options) {
      if (isPlainObject(secretOrPrivateKey)) {
        options = secretOrPrivateKey
        secretOrPrivateKey = undefined
      }
      if (!secretOrPrivateKey) {
        secretOrPrivateKey = sessionSecret
      }
      const { expiresIn = '1d', jwtid = uuid.v1(), ...rest } = options || {}
      return jwtSign(payload, secretOrPrivateKey, { expiresIn, jwtid, ...rest })
    },
    verify (token, options) {
      return jwtVerify(token, sessionSecret, options)
    },
    decode (token) {
      return jwt.decode(token) || {}
    },
    encrypt (text) {
      const encodedText = encoder.encode(text)
      const protectedHeader = {
        enc: 'A128CBC-HS256',
        alg: 'PBES2-HS256+A128KW'
      }
      return new jose.CompactEncrypt(encodedText)
        .setProtectedHeader(protectedHeader)
        .encrypt(symetricKey)
    },
    async decrypt (data) {
      const { plaintext } = await jose.compactDecrypt(data, symetricKey)
      return decoder.decode(plaintext)
    }
  }
}
