Class: CF::UAA::TokenCoder
- Inherits:
-
Object
- Object
- CF::UAA::TokenCoder
- Defined in:
- lib/uaa/token_coder.rb
Overview
This class is for OAuth Resource Servers. Resource Servers get tokens and need to validate and decode them, but they do not obtain them from the Authorization Server. This class is for resource servers which accept bearer JWT tokens.
For more on JWT, see the JSON Web Token RFC here: http://tools.ietf.org/id/draft-ietf-oauth-json-web-token-05.html
An instance of this class can be used to decode and verify the contents of a bearer token. Methods of this class can validate token signatures with a secret or public key, and they can also enforce that the token is for a particular audience.
Class Method Summary collapse
-
.decode(token, options = {}, obsolete1 = nil, obsolete2 = nil) ⇒ Hash
Decodes a JWT token and optionally verifies the signature.
-
.encode(token_body, options = {}, obsolete1 = nil, obsolete2 = nil) ⇒ String
Constructs a signed JWT.
Instance Method Summary collapse
-
#decode(auth_header) ⇒ Hash
Returns hash of values decoded from the token contents.
-
#encode(token_body = {}, algorithm = nil) ⇒ String
Encode a JWT token.
-
#initialize(options = {}, obsolete1 = nil, obsolete2 = nil) ⇒ TokenCoder
constructor
Creates a new token en/decoder for a service that is associated with the the audience_ids, the symmetrical token validation key, and the public and/or private keys.
Constructor Details
#initialize(options = {}, obsolete1 = nil, obsolete2 = nil) ⇒ TokenCoder
the TokenCoder instance must be configured with the appropriate key material to support particular algorithm families and operations – i.e. :pkey must include a private key in order to sign tokens with the RS algorithms.
Creates a new token en/decoder for a service that is associated with the the audience_ids, the symmetrical token validation key, and the public and/or private keys.
140 141 142 143 144 145 146 147 148 |
# File 'lib/uaa/token_coder.rb', line 140 def initialize( = {}, obsolete1 = nil, obsolete2 = nil) unless .is_a?(Hash) && obsolete1.nil? && obsolete2.nil? # deprecated: def initialize(audience_ids, skey, pkey = nil) warn "#{self.class}##{__method__} is deprecated with these parameters. Please use options hash." = {:audience_ids => } [:skey], [:pkey] = obsolete1, obsolete2 end @options = self.class.() end |
Class Method Details
.decode(token, options = {}, obsolete1 = nil, obsolete2 = nil) ⇒ Hash
Decodes a JWT token and optionally verifies the signature. Both a symmetrical key and a public key can be provided for signature verification. The JWT header indicates what signature algorithm was used and the corresponding key is used to verify the signature (if verify
is true).
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 |
# File 'lib/uaa/token_coder.rb', line 84 def self.decode(token, = {}, obsolete1 = nil, obsolete2 = nil) unless .is_a?(Hash) && obsolete1.nil? && obsolete2.nil? # deprecated: def self.decode(token, skey = nil, pkey = nil, verify = true) warn "#{self.class}##{__method__} is deprecated with these parameters. Please use options hash." = {:skey => } [:pkey], [:verify] = obsolete1, obsolete2 end = () segments = token.split('.') raise DecodeError, "Not enough or too many segments" unless [2,3].include? segments.length header_segment, payload_segment, crypto_segment = segments signing_input = [header_segment, payload_segment].join('.') header = Util.json_decode64(header_segment) payload = Util.json_decode64(payload_segment, (:sym if [:symbolize_keys])) return payload unless [:verify] raise DecodeError, "Signature algorithm not accepted" unless [:accept_algorithms].include?(algo = header["alg"]) return payload if algo == 'none' signature = Util.decode64(crypto_segment) if ["HS256", "HS384", "HS512"].include?(algo) raise DecodeError, "Signature verification failed" unless signature == OpenSSL::HMAC.digest(init_digest(algo), [:skey], signing_input) elsif ["RS256", "RS384", "RS512"].include?(algo) raise DecodeError, "Signature verification failed" unless [:pkey].verify(init_digest(algo), signature, signing_input) else raise DecodeError, "Algorithm not supported" end payload end |
.encode(token_body, options = {}, obsolete1 = nil, obsolete2 = nil) ⇒ String
Constructs a signed JWT.
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/uaa/token_coder.rb', line 53 def self.encode(token_body, = {}, obsolete1 = nil, obsolete2 = nil) unless .is_a?(Hash) && obsolete1.nil? && obsolete2.nil? # deprecated: def self.encode(token_body, skey, pkey = nil, algo = 'HS256') warn "#{self.class}##{__method__} is deprecated with these parameters. Please use options hash." = {:skey => } [:pkey], [:algorithm] = obsolete1, obsolete2 end = () algo = [:algorithm] segments = [Util.json_encode64("typ" => "JWT", "alg" => algo)] segments << Util.json_encode64(token_body) if ["HS256", "HS384", "HS512"].include?(algo) sig = OpenSSL::HMAC.digest(init_digest(algo), [:skey], segments.join('.')) elsif ["RS256", "RS384", "RS512"].include?(algo) sig = [:pkey].sign(init_digest(algo), segments.join('.')) elsif algo == "none" sig = "" else raise ArgumentError, "unsupported signing method" end segments << Util.encode64(sig) segments.join('.') end |
Instance Method Details
#decode(auth_header) ⇒ Hash
Returns hash of values decoded from the token contents. If the audience_ids were specified in the options to this instance (see #initialize) and the token does not contain one or more of those audience_ids, an AuthError will be raised. AuthError is raised if the token has expired.
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/uaa/token_coder.rb', line 167 def decode(auth_header) unless auth_header && (tkn = auth_header.split(' ')).length == 2 && tkn[0] =~ /^bearer$/i raise DecodeError, "invalid authentication header: #{auth_header}" end reply = self.class.decode(tkn[1], @options) auds = Util.arglist(reply[:aud] || reply['aud']) if @options[:audience_ids] && (!auds || (auds & @options[:audience_ids]).empty?) raise AuthError, "invalid audience: #{auds}" end exp = reply[:exp] || reply['exp'] unless exp.is_a?(Integer) && exp > Time.now.to_i raise AuthError, "token expired" end reply end |
#encode(token_body = {}, algorithm = nil) ⇒ String
Encode a JWT token. Takes a hash of values to use as the token body. Returns a signed token in JWT format (header, body, signature).
155 156 157 158 159 |
# File 'lib/uaa/token_coder.rb', line 155 def encode(token_body = {}, algorithm = nil) token_body[:aud] = @options[:audience_ids] if @options[:audience_ids] && !token_body[:aud] && !token_body['aud'] token_body[:exp] = Time.now.to_i + 7 * 24 * 60 * 60 unless token_body[:exp] || token_body['exp'] self.class.encode(token_body, algorithm ? @options.merge(:algorithm => algorithm) : @options) end |