class SSO::Client::Authentications::Passport

Logic to authenticate a Passport provided by an outsider app to an insider app.

Attributes

passport_id[R]
request[R]

Public Class Methods

new(request) click to toggle source
# File lib/sso/client/authentications/passport.rb, line 11
def initialize(request)
  @request = request
end

Public Instance Methods

authenticate() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 15
def authenticate
  debug { 'Performing authentication...' }
  result = authenticate!

  if result.success?
    debug { 'Authentication succeeded.' }
    return result
  end

  debug { "The Client Passport authentication failed: #{result.code}" }
  Operations.failure :passport_authentication_failed, object: failure_rack_array
end

Private Instance Methods

agent() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 204
def agent
  request.user_agent
end
authenticate!() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 32
def authenticate!
  chip_decryption         { |failure| return failure }
  check_request_signature { |failure| return failure }
  passport = retrieve_passport { |failure| return failure }
  passport.verified!

  Operations.success :passport_received, object: passport
end
check_request_signature() { |failure :invalid_passport_signature| ... } click to toggle source
# File lib/sso/client/authentications/passport.rb, line 55
def check_request_signature
  debug { "Verifying request signature using Passport secret #{chip_passport_secret.inspect}" }
  signature_request.authenticate do |passport_id|
    Signature::Token.new passport_id, chip_passport_secret
  end
  debug { 'Signature looks legit.' }
  Operations.success :passport_signature_valid

rescue ::Signature::AuthenticationError => exception
  debug { "The Signature Authentication failed. #{exception.message}" }
  yield Operations.failure :invalid_passport_signature
end
chip() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 191
def chip
  params['passport_chip']
end
chip_belongs_to_passport?() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 143
def chip_belongs_to_passport?
  unless passport_id
    debug { 'Unknown passport_id' }
    return false
  end

  unless chip_passport_id
    debug { 'Unknown passport_id' }
    return false
  end

  if passport_id.to_s == chip_passport_id
    debug { "The chip on passport #{passport_id.inspect} appears to belong to it." }
    true
  else
    info { "The passport with ID #{passport_id.inspect} has a chip with the wrong ID #{chip_passport_id.inspect}" }
    false
  end
end
chip_ciphertext() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 171
def chip_ciphertext
  Base64.decode64 encoded_chip_ciphertext
end
chip_ciphertext_and_iv() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 187
def chip_ciphertext_and_iv
  chip.to_s.split '|'
end
chip_decryption() { |failure(:missing_chip, object: params)| ... } click to toggle source
# File lib/sso/client/authentications/passport.rb, line 107
def chip_decryption
  debug { "Validating chip decryptability of raw chip #{chip.inspect}" }
  yield Operations.failure(:missing_chip, object: params) if chip.blank?
  yield Operations.failure(:missing_chip_key) unless chip_key
  yield Operations.failure(:missing_chip_iv) unless chip_iv
  yield Operations.failure(:chip_does_not_belong_to_passport) unless chip_belongs_to_passport?
  Operations.success :here_is_your_chip_plaintext, object: decrypt_chip

rescue OpenSSL::Cipher::CipherError => exception
  yield Operations.failure :chip_decryption_failed, object: exception.message
end
chip_digest() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 195
def chip_digest
  ::OpenSSL::Cipher::AES256.new :CBC
end
chip_iv() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 179
def chip_iv
  Base64.decode64 chip_ciphertext_and_iv.last
end
chip_key() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 163
def chip_key
  ::SSO.config.passport_chip_key
end
chip_passport_id() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 139
def chip_passport_id
  decrypt_chip.to_s.split('|').first
end
chip_passport_secret() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 135
def chip_passport_secret
  decrypt_chip.to_s.split('|').last
end
decrypt_chip() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 119
def decrypt_chip
  @decrypt_chip ||= decrypt_chip!
end
decrypt_chip!() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 123
def decrypt_chip!
  benchmark(name: 'Passport chip decryption') do
    decipher = chip_digest
    decipher.decrypt
    decipher.key = chip_key
    decipher.iv = chip_iv
    plaintext = decipher.update(chip_ciphertext) + decipher.final
    logger.debug { "Decrypted chip plaintext #{plaintext.inspect} using key #{chip_key.inspect} and iv #{chip_iv.inspect} and ciphertext #{chip_ciphertext.inspect}" }
    plaintext
  end
end
device_id() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 208
def device_id
  request.params['device_id']
end
encoded_chip_ciphertext() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 175
def encoded_chip_ciphertext
  chip_ciphertext_and_iv.first
end
encoded_chip_iv() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 183
def encoded_chip_iv
  chip_iv
end
failure_rack_array() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 87
def failure_rack_array
  payload = { success: false, code: :passport_verification_failed }
  [200, { 'Content-Type' => 'application/json' }, [payload.to_json]]
end
ip() click to toggle source

TODO: Use ActionDispatch::Request#remote_ip

# File lib/sso/client/authentications/passport.rb, line 200
def ip
  request.ip
end
retrieve_passport() { |verification| ... } click to toggle source
# File lib/sso/client/authentications/passport.rb, line 41
def retrieve_passport
  debug { 'Retrieving Passport from server...' }
  if verification.success? && verification.code == :passport_valid_and_modified
    passport = verification.object
    passport.modified!

    debug { "Successfully retrieved Passport with ID #{passport_id} from server." }
    return passport
  else
    debug { 'Could not obtain Passport from server.' }
    yield verification
  end
end
signature_request() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 92
def signature_request
  debug { "Verifying signature of #{request.request_method.inspect} #{request.path.inspect} #{request.params.inspect}" }
  ::Signature::Request.new request.request_method, request.path, request.params
end
user_state_digest() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 167
def user_state_digest
  ::OpenSSL::Digest.new 'sha1'
end
verification() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 83
def verification
  @verification ||= verifier.call
end
verifier() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 68
def verifier
  ::SSO::Client::PassportVerifier.new verifier_options
end
verifier_options() click to toggle source
# File lib/sso/client/authentications/passport.rb, line 72
def verifier_options
  {
    passport_id: passport_id,
    passport_state: 'refresh',
    passport_secret: chip_passport_secret,
    user_ip: ip,
    user_agent: agent,
    device_id: device_id,
  }
end