Class: Nylas::ServiceAccountSigner
- Inherits:
-
Object
- Object
- Nylas::ServiceAccountSigner
- Defined in:
- lib/nylas/handler/service_account_signer.rb
Overview
Builds Nylas Service Account request signing headers for organization admin APIs.
Constant Summary collapse
- NONCE_ALPHABET =
("a".."z").to_a.concat(("A".."Z").to_a, ("0".."9").to_a).freeze
- DEFAULT_NONCE_LENGTH =
20- SIGNED_BODY_METHODS =
%w[post put patch].freeze
Instance Attribute Summary collapse
-
#private_key_id ⇒ Object
readonly
Returns the value of attribute private_key_id.
Class Method Summary collapse
-
.canonical_json(data) ⇒ String
Returns deterministic JSON with keys sorted at every object level and no extra whitespace.
-
.generate_nonce(length = DEFAULT_NONCE_LENGTH) ⇒ String
Generates a cryptographically secure alphanumeric nonce.
-
.load_rsa_private_key(private_key_pem) ⇒ OpenSSL::PKey::RSA
Loads an RSA private key from a PEM string.
Instance Method Summary collapse
-
#build_headers(method:, path:, body: nil, timestamp: nil, nonce: nil) ⇒ Array(Hash, String)
Builds signed headers and, for JSON body methods, the exact canonical body to send.
-
#initialize(private_key_pem:, private_key_id:) ⇒ ServiceAccountSigner
constructor
A new instance of ServiceAccountSigner.
Constructor Details
#initialize(private_key_pem:, private_key_id:) ⇒ ServiceAccountSigner
Returns a new instance of ServiceAccountSigner.
21 22 23 24 |
# File 'lib/nylas/handler/service_account_signer.rb', line 21 def initialize(private_key_pem:, private_key_id:) @private_key = self.class.load_rsa_private_key(private_key_pem) @private_key_id = private_key_id end |
Instance Attribute Details
#private_key_id ⇒ Object (readonly)
Returns the value of attribute private_key_id.
17 18 19 |
# File 'lib/nylas/handler/service_account_signer.rb', line 17 def private_key_id @private_key_id end |
Class Method Details
.canonical_json(data) ⇒ String
Returns deterministic JSON with keys sorted at every object level and no extra whitespace.
30 31 32 |
# File 'lib/nylas/handler/service_account_signer.rb', line 30 def self.canonical_json(data) JSON.generate(canonicalize(data)) end |
.generate_nonce(length = DEFAULT_NONCE_LENGTH) ⇒ String
Generates a cryptographically secure alphanumeric nonce.
52 53 54 |
# File 'lib/nylas/handler/service_account_signer.rb', line 52 def self.generate_nonce(length = DEFAULT_NONCE_LENGTH) Array.new(length) { NONCE_ALPHABET[SecureRandom.random_number(NONCE_ALPHABET.length)] }.join end |
.load_rsa_private_key(private_key_pem) ⇒ OpenSSL::PKey::RSA
Loads an RSA private key from a PEM string.
38 39 40 41 42 43 44 45 46 |
# File 'lib/nylas/handler/service_account_signer.rb', line 38 def self.load_rsa_private_key(private_key_pem) key = OpenSSL::PKey::RSA.new(private_key_pem) raise ArgumentError, "Private key must be RSA private key" unless key.private? raise ArgumentError, "Private key must be at least 2048 bits" if key.n.num_bits < 2048 key rescue OpenSSL::PKey::PKeyError raise ArgumentError, "Private key must be RSA PEM" end |
Instance Method Details
#build_headers(method:, path:, body: nil, timestamp: nil, nonce: nil) ⇒ Array(Hash, String)
Builds signed headers and, for JSON body methods, the exact canonical body to send.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/nylas/handler/service_account_signer.rb', line 64 def build_headers(method:, path:, body: nil, timestamp: nil, nonce: nil) ||= Time.now.to_i nonce ||= self.class.generate_nonce method_value = method.to_s.downcase serialized_body = nil if SIGNED_BODY_METHODS.include?(method_value) && !body.nil? serialized_body = self.class.canonical_json(body) end envelope = { method: method_value, nonce: nonce, path: path, timestamp: } envelope[:payload] = serialized_body if serialized_body signature = @private_key.sign(OpenSSL::Digest.new("SHA256"), self.class.canonical_json(envelope)) [ { "X-Nylas-Kid" => private_key_id, "X-Nylas-Nonce" => nonce, "X-Nylas-Timestamp" => .to_s, "X-Nylas-Signature" => Base64.strict_encode64(signature) }, serialized_body ] end |