Module: Pray::AuthClient

Defined in:
lib/pray/auth_client.rb

Class Method Summary collapse

Class Method Details

.login_with_passkey(server_url, credential_id, private_key_path, session_root, email:) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/pray/auth_client.rb', line 13

def (server_url, credential_id, private_key_path, session_root, email:)
  challenge = post_json(
    "#{trim_slash(server_url)}/v1/auth/passkeys/challenge",
    {"credential_id" => credential_id}
  )
  seed = File.binread(private_key_path)
  unless seed.bytesize == 32
    raise Error.unsupported("passkey private key must be 32 raw bytes")
  end

  signature = Base64.strict_encode64(sign_ed25519(seed, challenge["challenge"].to_s))
  response = post_json(
    "#{trim_slash(server_url)}/v1/auth/passkeys/login",
    {
      "credential_id" => credential_id,
      "challenge_id" => challenge["challenge_id"],
      "signature" => signature
    }
  )
  (server_url, response, email, "passkey", nil, session_root)
end

.login_with_ssh_agent(server_url, public_key_path, session_root, email:) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/pray/auth_client.rb', line 35

def (server_url, public_key_path, session_root, email:)
  public_key = File.read(public_key_path).strip
  challenge = post_json(
    "#{trim_slash(server_url)}/v1/auth/ssh-keys/challenge",
    {"public_key" => public_key}
  )
  signature = SshAgent.sign(public_key, challenge["challenge"].to_s)
  response = post_json(
    "#{trim_slash(server_url)}/v1/auth/ssh-keys/login",
    {
      "public_key" => public_key,
      "challenge_id" => challenge["challenge_id"],
      "signature" => signature
    }
  )
  (
    server_url, response, email, "ssh_key", challenge["fingerprint"], session_root
  )
end

.persist_login(server_url, response, email, kind, fingerprint, session_root) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/pray/auth_client.rb', line 55

def (server_url, response, email, kind, fingerprint, session_root)
  response_email = response["email"].to_s
  unless response_email == email
    raise Error.resolution(
      "login email mismatch: expected #{email}, got #{response_email}"
    )
  end

  Session.persist(
    session_root,
    SessionFile.new(
      server_url: server_url,
      email: response_email,
      token: response["token"].to_s,
      kind: kind,
      signer_fingerprint: fingerprint
    )
  )
end

.post_json(url, body) ⇒ Object



86
87
88
89
90
91
# File 'lib/pray/auth_client.rb', line 86

def post_json(url, body)
  response = Registry.http_post(url, "application/json", JSON.generate(body))
  JSON.parse(response)
rescue JSON::ParserError => error
  raise Error.parse("auth response", error.message)
end

.sign_ed25519(seed, message) ⇒ Object



75
76
77
78
79
80
81
82
83
84
# File 'lib/pray/auth_client.rb', line 75

def sign_ed25519(seed, message)
  unless OpenSSL::PKey.respond_to?(:generate_key)
    raise Error.unsupported("OpenSSL Ed25519 support is required for passkey login")
  end

  key = OpenSSL::PKey.new_raw_private_key("ED25519", seed)
  key.sign(nil, message)
rescue OpenSSL::PKey::PKeyError => error
  raise Error.unsupported("OpenSSL Ed25519 signing failed: #{error.message}")
end

.trim_slash(value) ⇒ Object



93
94
95
# File 'lib/pray/auth_client.rb', line 93

def trim_slash(value)
  value.to_s.sub(%r{/+\z}, "")
end