Class: Siwe::Message
- Inherits:
-
Object
- Object
- Siwe::Message
- Defined in:
- lib/siwe/message.rb
Overview
Class that defines the EIP-4361 message fields and some utility methods to generate/validate the messages
Instance Attribute Summary collapse
-
#address ⇒ Object
Ethereum address performing the signing conformant to capitalization encoded checksum specified in EIP-55 where applicable.
-
#chain_id ⇒ Object
EIP-155 Chain ID to which the session is bound, and the network where Contract Accounts must be resolved.
-
#domain ⇒ Object
RFC 4501 dns authority that is requesting the signing.
-
#expiration_time ⇒ Object
ISO 8601 datetime string that, if present, indicates when the signed authentication message is no longer valid.
-
#issued_at ⇒ Object
ISO 8601 datetime string of the current time.
-
#nonce ⇒ Object
Randomized token used to prevent replay attacks, at least 8 alphanumeric characters.
-
#not_before ⇒ Object
ISO 8601 datetime string that, if present, indicates when the signed authentication message will become valid.
-
#request_id ⇒ Object
System-specific identifier that may be used to uniquely refer to the sign-in request.
-
#resources ⇒ Object
List of information or references to information the user wishes to have resolved as part of authentication by the relying party.
-
#signature ⇒ Object
Signature of the message signed by the wallet.
-
#statement ⇒ Object
Human-readable ASCII assertion that the user will sign, and it must not contain ‘n`.
-
#uri ⇒ Object
RFC 3986 URI referring to the resource that is the subject of the signing (as in the __subject__ of a claim).
-
#version ⇒ Object
Current version of the message.
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(domain, address, uri, version, options = {}) ⇒ Message
constructor
A new instance of Message.
- #personal_sign ⇒ Object
- #to_json_string ⇒ Object
- #validate(skip_signature = false) ⇒ Object
Constructor Details
#initialize(domain, address, uri, version, options = {}) ⇒ Message
Returns a new instance of Message.
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/siwe/message.rb', line 80 def initialize(domain, address, uri, version, = {}) @domain = domain @address = address @uri = uri @version = version @statement = .fetch :statement, "" @issued_at = .fetch :issued_at, Time.now.utc.iso8601 @nonce = .fetch :nonce, Siwe::Util.generate_nonce @chain_id = .fetch :chain_id, "1" @expiration_time = .fetch :expiration_time, "" @not_before = .fetch :not_before, "" @request_id = .fetch :request_id, "" @resources = .fetch :resources, [] @signature = .fetch :signature, "" validate(true) end |
Instance Attribute Details
#address ⇒ Object
Ethereum address performing the signing conformant to capitalization encoded checksum specified in EIP-55 where applicable.
36 37 38 |
# File 'lib/siwe/message.rb', line 36 def address @address end |
#chain_id ⇒ Object
EIP-155 Chain ID to which the session is bound, and the network where Contract Accounts must be resolved.
47 48 49 |
# File 'lib/siwe/message.rb', line 47 def chain_id @chain_id end |
#domain ⇒ Object
RFC 4501 dns authority that is requesting the signing.
32 33 34 |
# File 'lib/siwe/message.rb', line 32 def domain @domain end |
#expiration_time ⇒ Object
ISO 8601 datetime string that, if present, indicates when the signed authentication message is no longer valid.
62 63 64 |
# File 'lib/siwe/message.rb', line 62 def expiration_time @expiration_time end |
#issued_at ⇒ Object
ISO 8601 datetime string of the current time.
54 55 56 |
# File 'lib/siwe/message.rb', line 54 def issued_at @issued_at end |
#nonce ⇒ Object
Randomized token used to prevent replay attacks, at least 8 alphanumeric characters.
51 52 53 |
# File 'lib/siwe/message.rb', line 51 def nonce @nonce end |
#not_before ⇒ Object
ISO 8601 datetime string that, if present, indicates when the signed authentication message will become valid.
66 67 68 |
# File 'lib/siwe/message.rb', line 66 def not_before @not_before end |
#request_id ⇒ Object
System-specific identifier that may be used to uniquely refer to the sign-in request.
70 71 72 |
# File 'lib/siwe/message.rb', line 70 def request_id @request_id end |
#resources ⇒ Object
List of information or references to information the user wishes to have resolved as part of authentication by the relying party. They are expressed as RFC 3986 URIs separated by ‘n- `.
75 76 77 |
# File 'lib/siwe/message.rb', line 75 def resources @resources end |
#signature ⇒ Object
Signature of the message signed by the wallet.
78 79 80 |
# File 'lib/siwe/message.rb', line 78 def signature @signature end |
#statement ⇒ Object
Human-readable ASCII assertion that the user will sign, and it must not contain ‘n`.
58 59 60 |
# File 'lib/siwe/message.rb', line 58 def statement @statement end |
#uri ⇒ Object
RFC 3986 URI referring to the resource that is the subject of the signing (as in the __subject__ of a claim).
40 41 42 |
# File 'lib/siwe/message.rb', line 40 def uri @uri end |
#version ⇒ Object
Current version of the message.
43 44 45 |
# File 'lib/siwe/message.rb', line 43 def version @version end |
Class Method Details
.from_json_string(str) ⇒ Object
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/siwe/message.rb', line 142 def self.from_json_string(str) obj = JSON.parse str, { symbolize_names: true } msg = Siwe::Message.new( obj[:domain], obj[:address], obj[:uri], obj[:version], { chain_id: obj[:chain_id], nonce: obj[:nonce], issued_at: obj[:issued_at], statement: obj[:statement], expiration_time: obj[:expiration_time], not_before: obj[:not_before], request_id: obj[:request_id], resources: obj[:resources], signature: obj[:signature] } ) msg.validate(true) msg end |
.from_message(msg) ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/siwe/message.rb', line 98 def self.(msg) if ( = msg.match SIWE_MESSAGE) msg = new( [:domain], [:address], [:uri], [:version], { statement: [:statement] || "", issued_at: [:issued_at], nonce: [:nonce], chain_id: [:chain_id], expiration_time: [:expiration_time] || "", not_before: [:not_before] || "", request_id: [:request_id] || "", resources: [:resources]&.split("\n- ")&.drop(1) || [] } ) msg.validate(true) msg else throw "Invalid message input." end end |
Instance Method Details
#personal_sign ⇒ Object
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/siwe/message.rb', line 179 def personal_sign greeting = "#{@domain} wants you to sign in with your Ethereum account:" address = @address statement = "\n#{@statement}\n" header = [greeting, address] if @statement.empty? header.push "\n" else header.push statement end header = header.join "\n" uri = "URI: #{@uri}" version = "Version: #{@version}" chain_id = "Chain ID: #{@chain_id}" nonce = "Nonce: #{@nonce}" issued_at = "Issued At: #{@issued_at}" body = [uri, version, chain_id, nonce, issued_at] expiration_time = "Expiration Time: #{@expiration_time}" not_before = "Not Before: #{@not_before}" request_id = "Request ID: #{@request_id}" resources = "Resources:\n#{@resources.map { |x| "- #{x}" }.join "\n"}" body.push expiration_time unless @expiration_time.to_s.strip.empty? body.push not_before unless @not_before.to_s.strip.empty? body.push request_id unless @request_id.to_s.strip.empty? body.push resources unless @resources.empty? body = body.join "\n" [header, body].join "\n" end |
#to_json_string ⇒ Object
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/siwe/message.rb', line 123 def to_json_string obj = { domain: @domain, address: @address, uri: @uri, version: @version, chain_id: @chain_id, nonce: @nonce, issued_at: @issued_at, statement: @statement, expiration_time: @expiration_time, not_before: @not_before, request_id: @request_id, resources: @resources, signature: @signature } obj.to_json end |
#validate(skip_signature = false) ⇒ Object
164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/siwe/message.rb', line 164 def validate(skip_signature = false) raise "Message expired." if !@expiration_time.empty? && Time.now.utc > Time.parse(@expiration_time) raise "Message not yet valid." if !@not_before.empty? && Time.now.utc < Time.parse(@not_before) unless skip_signature raise "Missing signature field." if @signature.empty? pub_key = Eth::Signature.personal_recover personal_sign, @signature signature_address = Eth::Util.public_key_to_address pub_key raise "Signature doesn't match message." unless signature_address.to_s.downcase.eql? @address.to_s.downcase end true end |