-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
mac.cr
73 lines (61 loc) · 2.33 KB
/
mac.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
require "random/secure"
require "openssl/hmac"
require "base64"
require "./access_token"
class OAuth2::AccessToken::Mac < OAuth2::AccessToken
def self.new(pull : JSON::PullParser)
OAuth2::AccessToken.new(pull).as(self)
end
property mac_algorithm : String
property mac_key : String
property issued_at : Int64
def initialize(access_token, expires_in, @mac_algorithm, @mac_key, refresh_token = nil, scope = nil, @issued_at = Time.utc.to_unix, extra = nil)
super(access_token, expires_in, refresh_token, scope, extra)
end
def token_type
"Mac"
end
def authenticate(request : HTTP::Request, tls)
ts = Time.utc.to_unix
nonce = "#{ts - @issued_at}:#{Random::Secure.hex}"
method = request.method
uri = request.resource
host, port = host_and_port request, tls
ext = ""
mac = Mac.signature ts, nonce, method, uri, host, port, ext, mac_algorithm, mac_key
header = %(MAC id="#{access_token}", nonce="#{nonce}", ts="#{ts}", mac="#{mac}")
request.headers["Authorization"] = header
end
def self.signature(ts, nonce, method, uri, host, port, ext, mac_algorithm, mac_key)
normalized_request_string = "#{ts}\n#{nonce}\n#{method}\n#{uri}\n#{host}\n#{port}\n#{ext}\n"
digest = case mac_algorithm
when "hmac-sha-1" then OpenSSL::Algorithm::SHA1
when "hmac-sha-256" then OpenSSL::Algorithm::SHA256
else raise "Unsupported algorithm: #{mac_algorithm}"
end
Base64.strict_encode OpenSSL::HMAC.digest(digest, mac_key, normalized_request_string)
end
def to_json(json : JSON::Builder)
json.object do
json.field "token_type", "mac"
json.field "access_token", access_token
json.field "expires_in", expires_in
json.field "refresh_token", refresh_token if refresh_token
json.field "scope", scope if scope
json.field "mac_algorithm", mac_algorithm
json.field "mac_key", mac_key
end
end
def_equals_and_hash access_token, expires_in, mac_algorithm, mac_key, refresh_token, scope
private def host_and_port(request, tls)
host_header = request.headers["Host"]
if colon_index = host_header.index ':'
host = host_header[0...colon_index]
port = host_header[colon_index + 1..-1].to_i
else
host = host_header
port = tls ? 443 : 80
end
{host, port}
end
end