File: mac_token.rb

package info (click to toggle)
ruby-oauth2 1.4.4-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, sid
  • size: 352 kB
  • sloc: ruby: 1,549; makefile: 3
file content (122 lines) | stat: -rw-r--r-- 4,021 bytes parent folder | download | duplicates (3)
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
require 'base64'
require 'digest'
require 'openssl'
require 'securerandom'

module OAuth2
  class MACToken < AccessToken
    # Generates a MACToken from an AccessToken and secret
    #
    # @param [AccessToken] token the OAuth2::Token instance
    # @option [String] secret the secret key value
    # @param [Hash] opts the options to create the Access Token with
    # @see MACToken#initialize
    def self.from_access_token(token, secret, options = {})
      new(token.client, token.token, secret, token.params.merge(:refresh_token => token.refresh_token, :expires_in => token.expires_in, :expires_at => token.expires_at).merge(options))
    end

    attr_reader :secret, :algorithm

    # Initalize a MACToken
    #
    # @param [Client] client the OAuth2::Client instance
    # @param [String] token the Access Token value
    # @option [String] secret the secret key value
    # @param [Hash] opts the options to create the Access Token with
    # @option opts [String] :refresh_token (nil) the refresh_token value
    # @option opts [FixNum, String] :expires_in (nil) the number of seconds in which the AccessToken will expire
    # @option opts [FixNum, String] :expires_at (nil) the epoch time in seconds in which AccessToken will expire
    # @option opts [FixNum, String] :algorithm (hmac-sha-256) the algorithm to use for the HMAC digest (one of 'hmac-sha-256', 'hmac-sha-1')
    def initialize(client, token, secret, opts = {})
      @secret = secret
      self.algorithm = opts.delete(:algorithm) || 'hmac-sha-256'

      super(client, token, opts)
    end

    # Make a request with the MAC Token
    #
    # @param [Symbol] verb the HTTP request method
    # @param [String] path the HTTP URL path of the request
    # @param [Hash] opts the options to make the request with
    # @see Client#request
    def request(verb, path, opts = {}, &block)
      url = client.connection.build_url(path, opts[:params]).to_s

      opts[:headers] ||= {}
      opts[:headers]['Authorization'] = header(verb, url)

      @client.request(verb, path, opts, &block)
    end

    # Get the headers hash (always an empty hash)
    def headers
      {}
    end

    # Generate the MAC header
    #
    # @param [Symbol] verb the HTTP request method
    # @param [String] url the HTTP URL path of the request
    def header(verb, url)
      timestamp = Time.now.utc.to_i
      nonce = Digest::MD5.hexdigest([timestamp, SecureRandom.hex].join(':'))

      uri = URI.parse(url)

      raise(ArgumentError, "could not parse \"#{url}\" into URI") unless uri.is_a?(URI::HTTP)

      mac = signature(timestamp, nonce, verb, uri)

      "MAC id=\"#{token}\", ts=\"#{timestamp}\", nonce=\"#{nonce}\", mac=\"#{mac}\""
    end

    # Generate the Base64-encoded HMAC digest signature
    #
    # @param [Fixnum] timestamp the timestamp of the request in seconds since epoch
    # @param [String] nonce the MAC header nonce
    # @param [Symbol] verb the HTTP request method
    # @param [String] url the HTTP URL path of the request
    def signature(timestamp, nonce, verb, uri)
      signature = [
        timestamp,
        nonce,
        verb.to_s.upcase,
        uri.request_uri,
        uri.host,
        uri.port,
        '', nil
      ].join("\n")

      strict_encode64(OpenSSL::HMAC.digest(@algorithm, secret, signature))
    end

    # Set the HMAC algorithm
    #
    # @param [String] alg the algorithm to use (one of 'hmac-sha-1', 'hmac-sha-256')
    def algorithm=(alg)
      @algorithm = begin
        case alg.to_s
        when 'hmac-sha-1'
          OpenSSL::Digest::SHA1.new
        when 'hmac-sha-256'
          OpenSSL::Digest::SHA256.new
        else
          raise(ArgumentError, 'Unsupported algorithm')
        end
      end
    end

  private

    # No-op since we need the verb and path
    # and the MAC always goes in a header
    def token=(_noop)
    end

    # Base64.strict_encode64 is not available on Ruby 1.8.7
    def strict_encode64(str)
      Base64.encode64(str).delete("\n")
    end
  end
end