text: // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
  // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
  
  import { Log } from './Log.js';
  import { OidcClientSettings } from './OidcClientSettings.js';
  import { ErrorResponse } from './ErrorResponse.js';
  import { SigninRequest } from './SigninRequest.js';
  import { SigninResponse } from './SigninResponse.js';
  import { SignoutRequest } from './SignoutRequest.js';
  import { SignoutResponse } from './SignoutResponse.js';
  import { SigninState } from './SigninState.js';
  import { State } from './State.js';
  
  export class OidcClient {
      constructor(settings = {}) {
          if (settings instanceof OidcClientSettings) {
              this._settings = settings;
          }
          else {
              this._settings = new OidcClientSettings(settings);
          }
      }
  
      get _stateStore() {
          return this.settings.stateStore;
      }
      get _validator() {
          return this.settings.validator;
      }
      get _metadataService() {
          return this.settings.metadataService;
      }
  
      get settings() {
          return this._settings;
      }
      get metadataService() {
          return this._metadataService;
      }
  
      createSigninRequest({
          response_type, scope, redirect_uri,
          // data was meant to be the place a caller could indicate the data to
          // have round tripped, but people were getting confused, so i added state (since that matches the spec)
          // and so now if data is not passed, but state is then state will be used
          data, state, prompt, display, max_age, ui_locales, id_token_hint, login_hint, acr_values,
          resource, request, request_uri, response_mode, extraQueryParams } = {},
          stateStore
      ) {
          Log.debug("OidcClient.createSigninRequest");
  
          let client_id = this._settings.client_id;
          response_type = response_type || this._settings.response_type;
          scope = scope || this._settings.scope;
          redirect_uri = redirect_uri || this._settings.redirect_uri;
  
          // id_token_hint, login_hint aren't allowed on _settings
          prompt = prompt || this._settings.prompt;
          display = display || this._settings.display;
          max_age = max_age || this._settings.max_age;
          ui_locales = ui_locales || this._settings.ui_locales;
          acr_values = acr_values || this._settings.acr_values;
          resource = resource || this._settings.resource;
          response_mode = response_mode || this._settings.response_mode;
          extraQueryParams = extraQueryParams || this._settings.extraQueryParams;
  
          let authority = this._settings.authority;
  
          if (SigninRequest.isCode(response_type) && response_type !== "code") {
              return Promise.reject(new Error("OpenID Connect hybrid flow is not supported"));
          }
  
          return this._metadataService.getAuthorizationEndpoint().then(url => {
              Log.debug("OidcClient.createSigninRequest: Received authorization endpoint", url);
  
              let signinRequest = new SigninRequest({
                  url,
                  client_id,
                  redirect_uri,
                  response_type,
                  scope,
                  data: data || state,
                  authority,
                  prompt, display, max_age, ui_locales, id_token_hint, login_hint, acr_values,
                  resource, request, request_uri, extraQueryParams, response_mode
              });
  
              var signinState = signinRequest.state;
              stateStore = stateStore || this._stateStore;
  
              return stateStore.set(signinState.id, signinState.toStorageString()).then(() => {
                  return signinRequest;
              });
          });
      }
  
      processSigninResponse(url, stateStore) {
          Log.debug("OidcClient.processSigninResponse");
  
          let useQuery = this._settings.response_mode === "query" || 
              (!this._settings.response_mode && SigninRequest.isCode(this._settings.response_type));
          let delimiter = useQuery ? "?" : "#";
  
          var response = new SigninResponse(url, delimiter);
  
          if (!response.state) {
              Log.error("OidcClient.processSigninResponse: No state in response");
              return Promise.reject(new Error("No state in response"));
          }
  
          stateStore = stateStore || this._stateStore;
  
          return stateStore.remove(response.state).then(storedStateString => {
              if (!storedStateString) {
                  Log.error("OidcClient.processSigninResponse: No matching state found in storage");
                  throw new Error("No matching state found in storage");
              }
  
              let state = SigninState.fromStorageString(storedStateString);
  
              Log.debug("OidcClient.processSigninResponse: Received state from storage; validating response");
              return this._validator.validateSigninResponse(state, response);
          });
      }
  
      createSignoutRequest({id_token_hint, data, state, post_logout_redirect_uri, extraQueryParams } = {},
          stateStore
      ) {
          Log.debug("OidcClient.createSignoutRequest");
  
          post_logout_redirect_uri = post_logout_redirect_uri || this._settings.post_logout_redirect_uri;
          extraQueryParams = extraQueryParams || this._settings.extraQueryParams;
  
          return this._metadataService.getEndSessionEndpoint().then(url => {
              if (!url) {
                  Log.error("OidcClient.createSignoutRequest: No end session endpoint url returned");
                  throw new Error("no end session endpoint");
              }
  
              Log.debug("OidcClient.createSignoutRequest: Received end session endpoint", url);
  
              let request = new SignoutRequest({
                  url,
                  id_token_hint,
                  post_logout_redirect_uri,
                  data: data || state,
                  extraQueryParams
              });
  
              var signoutState = request.state;
              if (signoutState) {
                  Log.debug("OidcClient.createSignoutRequest: Signout request has state to persist");
  
                  stateStore = stateStore || this._stateStore;
                  stateStore.set(signoutState.id, signoutState.toStorageString());
              }
  
              return request;
          });
      }
  
      processSignoutResponse(url, stateStore) {
          Log.debug("OidcClient.processSignoutResponse");
  
          var response = new SignoutResponse(url);
          if (!response.state) {
              Log.debug("OidcClient.processSignoutResponse: No state in response");
  
              if (response.error) {
                  Log.warn("OidcClient.processSignoutResponse: Response was error: ", response.error);
                  return Promise.reject(new ErrorResponse(response));
              }
  
              return Promise.resolve(response);
          }
  
          var stateKey = response.state;
  
          stateStore = stateStore || this._stateStore;
  
          return stateStore.remove(stateKey).then(storedStateString => {
              if (!storedStateString) {
                  Log.error("OidcClient.processSignoutResponse: No matching state found in storage");
                  throw new Error("No matching state found in storage");
              }
  
              let state = State.fromStorageString(storedStateString);
  
              Log.debug("OidcClient.processSignoutResponse: Received state from storage; validating response");
              return this._validator.validateSignoutResponse(state, response);
          });
      }
  
      clearStaleState(stateStore) {
          Log.debug("OidcClient.clearStaleState");
  
          stateStore = stateStore || this._stateStore;
  
          return State.clearStaleState(stateStore, this.settings.staleStateAge);
      }
  }
  