class Kontena::Cli::Cloud::LoginCommand

Public Instance Methods

execute() click to toggle source
# File lib/kontena/cli/cloud/login_command.rb, line 13
def execute
  if self.code && self.force?
    exit_with_error "Can't use --code and --force together"
  end

  if self.token
    exit_with_error "Can't use --token and --force together" if self.force?
    exit_with_error "Can't use --token and --code together"  if self.code
  end

  if !kontena_account.token || !kontena_account.token.access_token || self.token || self.force?
    kontena_account.token = Kontena::Cli::Config::Token.new(access_token: self.token, parent_type: :account, parent_name: kontena_account.name)
  end

  use_authorization_code(self.code) if self.code

  client = Kontena::Client.new(kontena_account.userinfo_endpoint, kontena_account.token, prefix: '')

  if kontena_account.token.access_token
    auth_ok = vspinner "Verifying current access token" do
      client.authentication_ok?(kontena_account.userinfo_endpoint)
    end
    if auth_ok
      finish and return
    end
  end
  if remote?
    remote_login
  else
    web_flow
  end
  finish
end
finish() click to toggle source
# File lib/kontena/cli/cloud/login_command.rb, line 47
def finish
  update_userinfo unless kontena_account.username
  config.current_account = kontena_account.name
  config.write
  config.reset_instance
  reset_cloud_client
  display_logo
  display_login_info(only: :account)
  true
end
remote_login() click to toggle source
# File lib/kontena/cli/cloud/login_command.rb, line 58
def remote_login
  client_id = kontena_account.client_id || Kontena::Client::CLIENT_ID
  params = {
    client_id: client_id
  }
  cloud_url = kontena_account.url
  client = Kontena::Client.new(cloud_url, nil)
  auth_request_response = client.post('/auth_requests', params, {}, { 'Content-Type' => 'application/x-www-form-urlencoded' }) rescue nil
  if !auth_request_response.kind_of?(Hash)
    exit_with_error "Remote login request failed"
  elsif auth_request_response['error']
    exit_with_error "Remote login request failed: #{auth_request_response['error']}"
  end
  begin
    verification_uri = URI.parse(auth_request_response['verification_uri'])
  rescue => e
    exit_with_error "Parsing remote login URL failed."
  end

  puts "Please visit #{pastel.cyan(verification_uri.to_s)} and enter the code"
  puts
  puts "#{auth_request_response['user_code']}"
  puts
  puts "Once the authentication is complete you can close the browser"
  puts "window or tab and return to this window to continue."
  puts

  code_request_params = {
    client_id: client_id,
    device_code: auth_request_response['device_code']
  }
  code_response = nil
  spinner "Waiting for authentication" do
    until code_response do
      code_response = client.post("/auth_requests/code",  code_request_params, {}, { 'Content-Type' => 'application/x-www-form-urlencoded' }) rescue nil
      sleep 1
    end
  end
  update_token(code_response)
end
update_token(response) click to toggle source
# File lib/kontena/cli/cloud/login_command.rb, line 171
def update_token(response)
  if !response.kind_of?(Hash)
    raise TypeError, "Invalid authentication response, expected Hash, got #{response.class}"
  elsif response['error']
    exit_with_error "Authentication failed: #{response['error']}"
  elsif response['code']
    use_authorization_code(response['code'])
  else
    kontena_account.token.access_token  = response['access_token']
    kontena_account.token.refresh_token = response['refresh_token']
    kontena_account.token.expires_at    = response['expires_at']
    true
  end
end
update_userinfo() click to toggle source
# File lib/kontena/cli/cloud/login_command.rb, line 149
def update_userinfo
  uri = URI.parse(kontena_account.userinfo_endpoint)
  path = uri.path
  uri.path = '/'

  response = Kontena::Client.new(uri.to_s, kontena_account.token).get(path)
  if response.kind_of?(Hash) && response['data'] && response['data']['attributes']
    kontena_account.username = response['data']['attributes']['username']
  elsif response && response['error']
    exit_with_error response['error']
  else
    exit_with_error "Userinfo request failed"
  end
end
use_authorization_code(code) click to toggle source
# File lib/kontena/cli/cloud/login_command.rb, line 164
def use_authorization_code(code)
  response = vspinner "Exchanging authorization code to access token" do
    Kontena::Client.new(kontena_account.token_endpoint, kontena_account.token).exchange_code(code)
  end
  update_token(response)
end
web_flow() click to toggle source
# File lib/kontena/cli/cloud/login_command.rb, line 99
def web_flow
  if Kontena.browserless? && !force?
    $stderr.puts "Your current environment does not seem to support opening a local graphical WWW browser. Using remote login instead."
    $stderr.puts
    remote_login
    return
  end

  require_relative '../localhost_web_server'
  require 'kontena/cli/browser_launcher'

  uri = URI.parse(kontena_account.authorization_endpoint)
  uri.host ||= kontena_account.url

  web_server = Kontena::LocalhostWebServer.new

  params = {
    client_id: kontena_account.client_id || Kontena::Client::CLIENT_ID,
    response_type: 'code',
    redirect_uri: "http://localhost:#{web_server.port}/cb"
  }

  uri.query = URI.encode_www_form(params)

  puts "Opening a browser to #{uri.scheme}://#{uri.host}"
  #puts
  #puts "If you are running this command over an ssh connection or it's"
  #puts "otherwise not possible to open a browser from this terminal"
  #puts "then you must use a pregenerated access token using the --token"
  #puts "option : kontena cloud login --token <access_token>"
  puts
  puts "Once the authentication is complete you can close the browser"
  puts "window or tab and return to this window to continue."
  puts
  any_key_to_continue(10)

  puts "If the browser does not open, try visiting this URL manually:"
  puts "#{uri.to_s}"
  puts

  server_thread  = Thread.new { Thread.main['response'] = web_server.serve_one }
  Kontena::Cli::BrowserLauncher.open(uri.to_s)

  spinner "Waiting for browser authorization response" do
    server_thread.join
  end

  update_token(Thread.main['response'])
end