'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }

var universalUserAgent = require('universal-user-agent');
var request = require('@octokit/request');
var authOauthDevice = require('@octokit/auth-oauth-device');
var oauthMethods = require('@octokit/oauth-methods');
var btoa = _interopDefault(require('btoa-lite'));

const VERSION = "2.0.0";

// @ts-nocheck there is only place for one of us in this file. And it's not you, TS
async function getAuthentication(state) {
  // handle code exchange form OAuth Web Flow
  if ("code" in state.strategyOptions) {
    const {
      authentication
    } = await oauthMethods.exchangeWebFlowCode({
      clientId: state.clientId,
      clientSecret: state.clientSecret,
      clientType: state.clientType,
      ...state.strategyOptions,
      request: state.request
    });
    return {
      type: "token",
      tokenType: "oauth",
      ...authentication
    };
  } // handle OAuth device flow


  if ("onVerification" in state.strategyOptions) {
    const deviceAuth = authOauthDevice.createOAuthDeviceAuth({
      clientType: state.clientType,
      clientId: state.clientId,
      ...state.strategyOptions,
      request: state.request
    });
    const authentication = await deviceAuth({
      type: "oauth"
    });
    return {
      clientSecret: state.clientSecret,
      ...authentication
    };
  } // use existing authentication


  if ("token" in state.strategyOptions) {
    return {
      type: "token",
      tokenType: "oauth",
      clientId: state.clientId,
      clientSecret: state.clientSecret,
      clientType: state.clientType,
      ...state.strategyOptions
    };
  }

  throw new Error("[@octokit/auth-oauth-user] Invalid strategy options");
}

async function auth(state, options = {}) {
  if (!state.authentication) {
    // This is what TS makes us do ¯\_(ツ)_/¯
    state.authentication = state.clientType === "oauth-app" ? await getAuthentication(state) : await getAuthentication(state);
  }

  if (state.authentication.invalid) {
    throw new Error("[@octokit/auth-oauth-user] Token is invalid");
  }

  const currentAuthentication = state.authentication; // (auto) refresh for user-to-server tokens

  if ("expiresAt" in currentAuthentication) {
    if (options.type === "refresh" || new Date(currentAuthentication.expiresAt) < new Date()) {
      const {
        authentication
      } = await oauthMethods.refreshToken({
        clientType: "github-app",
        clientId: state.clientId,
        clientSecret: state.clientSecret,
        refreshToken: currentAuthentication.refreshToken,
        request: state.request
      });
      state.authentication = {
        tokenType: "oauth",
        type: "token",
        ...authentication
      };
    }
  } // throw error for invalid refresh call


  if (options.type === "refresh") {
    if (state.clientType === "oauth-app") {
      throw new Error("[@octokit/auth-oauth-user] OAuth Apps do not support expiring tokens");
    }

    if (!currentAuthentication.hasOwnProperty("expiresAt")) {
      throw new Error("[@octokit/auth-oauth-user] Refresh token missing");
    }
  } // check or reset token


  if (options.type === "check" || options.type === "reset") {
    const method = options.type === "check" ? oauthMethods.checkToken : oauthMethods.resetToken;

    try {
      const {
        authentication
      } = await method({
        // @ts-expect-error making TS happy would require unnecessary code so no
        clientType: state.clientType,
        clientId: state.clientId,
        clientSecret: state.clientSecret,
        token: state.authentication.token,
        request: state.request
      });
      state.authentication = {
        tokenType: "oauth",
        type: "token",
        // @ts-expect-error TBD
        ...authentication
      };
      return state.authentication;
    } catch (error) {
      // istanbul ignore else
      if (error.status === 404) {
        error.message = "[@octokit/auth-oauth-user] Token is invalid"; // @ts-expect-error TBD

        state.authentication.invalid = true;
      }

      throw error;
    }
  } // invalidate


  if (options.type === "delete" || options.type === "deleteAuthorization") {
    const method = options.type === "delete" ? oauthMethods.deleteToken : oauthMethods.deleteAuthorization;

    try {
      await method({
        // @ts-expect-error making TS happy would require unnecessary code so no
        clientType: state.clientType,
        clientId: state.clientId,
        clientSecret: state.clientSecret,
        token: state.authentication.token,
        request: state.request
      });
    } catch (error) {
      // istanbul ignore if
      if (error.status !== 404) throw error;
    }

    state.authentication.invalid = true;
    return state.authentication;
  }

  return state.authentication;
}

/**
 * The following endpoints require an OAuth App to authenticate using its client_id and client_secret.
 *
 * - [`POST /applications/{client_id}/token`](https://docs.github.com/en/rest/reference/apps#check-a-token) - Check a token
 * - [`PATCH /applications/{client_id}/token`](https://docs.github.com/en/rest/reference/apps#reset-a-token) - Reset a token
 * - [`POST /applications/{client_id}/token/scoped`](https://docs.github.com/en/rest/reference/apps#create-a-scoped-access-token) - Create a scoped access token
 * - [`DELETE /applications/{client_id}/token`](https://docs.github.com/en/rest/reference/apps#delete-an-app-token) - Delete an app token
 * - [`DELETE /applications/{client_id}/grant`](https://docs.github.com/en/rest/reference/apps#delete-an-app-authorization) - Delete an app authorization
 *
 * deprecated:
 *
 * - [`GET /applications/{client_id}/tokens/{access_token}`](https://docs.github.com/en/rest/reference/apps#check-an-authorization) - Check an authorization
 * - [`POST /applications/{client_id}/tokens/{access_token}`](https://docs.github.com/en/rest/reference/apps#reset-an-authorization) - Reset an authorization
 * - [`DELETE /applications/{client_id}/tokens/{access_token}`](https://docs.github.com/en/rest/reference/apps#revoke-an-authorization-for-an-application) - Revoke an authorization for an application
 * - [`DELETE /applications/{client_id}/grants/{access_token}`](https://docs.github.com/en/rest/reference/apps#revoke-a-grant-for-an-application) - Revoke a grant for an application
 */
const ROUTES_REQUIRING_BASIC_AUTH = /\/applications\/[^/]+\/(token|grant)s?/;
function requiresBasicAuth(url) {
  return url && ROUTES_REQUIRING_BASIC_AUTH.test(url);
}

async function hook(state, request, route, parameters = {}) {
  const endpoint = request.endpoint.merge(route, parameters); // Do not intercept OAuth Web/Device flow request

  if (/\/login\/(oauth\/access_token|device\/code)$/.test(endpoint.url)) {
    return request(endpoint);
  }

  if (requiresBasicAuth(endpoint.url)) {
    const credentials = btoa(`${state.clientId}:${state.clientSecret}`);
    endpoint.headers.authorization = `basic ${credentials}`;
    return request(endpoint);
  } // TS makes us do this ¯\_(ツ)_/¯


  const {
    token
  } = state.clientType === "oauth-app" ? await auth({ ...state,
    request
  }) : await auth({ ...state,
    request
  });
  endpoint.headers.authorization = "token " + token;
  return request(endpoint);
}

function createOAuthUserAuth({
  clientId,
  clientSecret,
  clientType = "oauth-app",
  request: request$1 = request.request.defaults({
    headers: {
      "user-agent": `octokit-auth-oauth-app.js/${VERSION} ${universalUserAgent.getUserAgent()}`
    }
  }),
  ...strategyOptions
}) {
  const state = Object.assign({
    clientType,
    clientId,
    clientSecret,
    strategyOptions,
    request: request$1
  }); // @ts-expect-error not worth the extra code needed to appease TS

  return Object.assign(auth.bind(null, state), {
    // @ts-expect-error not worth the extra code needed to appease TS
    hook: hook.bind(null, state)
  });
}
createOAuthUserAuth.VERSION = VERSION;

exports.createOAuthUserAuth = createOAuthUserAuth;
exports.requiresBasicAuth = requiresBasicAuth;
//# sourceMappingURL=index.js.map
