From 73f4828026a5228eac80918ee39f0882bba6767a Mon Sep 17 00:00:00 2001 From: Karl Lingiah Date: Thu, 12 Dec 2024 10:16:19 +0000 Subject: [PATCH] DEVX-8994: Change default client tokens to JWT (#274) * Changing default client tokens to use JWT --- .github/workflows/ci.yml | 2 +- CHANGES.md | 4 + Gemfile | 2 + lib/opentok/opentok.rb | 1 + lib/opentok/session.rb | 1 + lib/opentok/token_generator.rb | 65 ++++- lib/opentok/version.rb | 2 +- spec/matchers/token.rb | 36 ++- spec/opentok/session_spec.rb | 1 - spec/shared/opentok_generates_tokens.rb | 366 +++++++++++++++++------- spec/shared/session_generates_tokens.rb | 148 +++++++--- 11 files changed, 478 insertions(+), 150 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 761134bb..70baddf5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - ruby: [2.5, 2.6, 2.7, 3.0] + ruby: [3.0, 3.1, 3.2, 3.3] exclude: - os: windows-latest ruby: 3.0 diff --git a/CHANGES.md b/CHANGES.md index c25ca9d5..ec61605f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +# 4.10.0 + +* Updating client token creation to use JWTs by default. See [#274](https://github.com/opentok/OpenTok-Ruby-SDK/pull/274) + # 4.9.0 * Adds the `publisheronly` role for client token creation. See [#272](https://github.com/opentok/OpenTok-Ruby-SDK/pull/272) diff --git a/Gemfile b/Gemfile index 57fae0ce..6a24022d 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,6 @@ source "http://rubygems.org" +gem "pry" + # Specify your gem's dependencies in opentok.gemspec gemspec diff --git a/lib/opentok/opentok.rb b/lib/opentok/opentok.rb index d47e5aa7..ae347845 100644 --- a/lib/opentok/opentok.rb +++ b/lib/opentok/opentok.rb @@ -36,6 +36,7 @@ module OpenTok # the token. # # @param [Hash] options A hash defining options for the token. + # @option options [String] :token_type The type of token to generate. Must be one of 'T1' or 'JWT'. 'JWT' is the default. # @option options [Symbol] :role The role for the token. Set this to one of the following # values: # * :subscriber -- A subscriber can only subscribe to streams. diff --git a/lib/opentok/session.rb b/lib/opentok/session.rb index 4bd7fd55..fba1475c 100644 --- a/lib/opentok/session.rb +++ b/lib/opentok/session.rb @@ -28,6 +28,7 @@ module OpenTok # Generates a token. # # @param [Hash] options + # @option options [String] :token_type The type of token to generate. Must be one of 'T1' or 'JWT'. 'JWT' is the default. # @option options [Symbol] :role The role for the token. Set this to one of the following # values: # * :subscriber -- A subscriber can only subscribe to streams. diff --git a/lib/opentok/token_generator.rb b/lib/opentok/token_generator.rb index a5cbee25..1720099a 100644 --- a/lib/opentok/token_generator.rb +++ b/lib/opentok/token_generator.rb @@ -6,10 +6,13 @@ require "openssl" require "active_support" require "active_support/time" +require "jwt" module OpenTok # @private module TokenGenerator + VALID_TOKEN_TYPES = ['T1', 'JWT'].freeze + # this works when using include TokenGenerator def self.included(base) base.extend(ClassMethods) @@ -33,7 +36,14 @@ def generates_tokens(arg_lambdas={}) end dynamic_args.compact! args = args.first(4-dynamic_args.length) - self.class.generate_token.call(*dynamic_args, *args) + token_type = if args.any? && args.last.is_a?(Hash) && args.last.has_key?(:token_type) + args.last[:token_type].upcase + else + "JWT" + end + raise "'#{token_type}' is not a valid token type. Must be one of: #{VALID_TOKEN_TYPES.join(', ')}" unless VALID_TOKEN_TYPES.include? token_type + + self.class.generate_token(token_type).call(*dynamic_args, *args) end end @@ -43,14 +53,14 @@ def arg_lambdas end # Generates a token - def generate_token - TokenGenerator::GENERATE_TOKEN_LAMBDA + def generate_token(token_type) + token_type == 'T1' ? TokenGenerator::GENERATE_T1_TOKEN_LAMBDA : TokenGenerator::GENERATE_JWT_LAMBDA end end # @private TODO: this probably doesn't need to be a constant anyone can read - GENERATE_TOKEN_LAMBDA = ->(api_key, api_secret, session_id, opts = {}) do + GENERATE_T1_TOKEN_LAMBDA = ->(api_key, api_secret, session_id, opts = {}) do # normalize required data params role = opts.fetch(:role, :publisher) unless ROLES.has_key? role @@ -101,6 +111,53 @@ def generate_token TOKEN_SENTINEL + Base64.strict_encode64(meta_string + ":" + data_string) end + GENERATE_JWT_LAMBDA = ->(api_key, api_secret, session_id, opts = {}) do + # normalize required data params + role = opts.fetch(:role, :publisher) + unless ROLES.has_key? role + raise "'#{role}' is not a recognized role" + end + unless Session.belongs_to_api_key? session_id.to_s, api_key + raise "Cannot generate token for a session_id that doesn't belong to api_key: #{api_key}" + end + + # minimum data params + data_params = { + :iss => api_key, + :ist => "project", + :iat => Time.now.to_i, + :exp => Time.now.to_i + 86400, + :nonce => Random.rand, + :role => role, + :scope => "session.connect", + :session_id => session_id, + } + + # normalize and add additional data params + unless (expire_time = opts[:expire_time].to_i) == 0 + unless expire_time.between?(Time.now.to_i, (Time.now + 30.days).to_i) + raise "Expire time must be within the next 30 days" + end + data_params[:exp] = expire_time.to_i + end + + unless opts[:data].nil? + unless (data = opts[:data].to_s).length < 1000 + raise "Connection data must be less than 1000 characters" + end + data_params[:connection_data] = data + end + + if opts[:initial_layout_class_list] + if opts[:initial_layout_class_list].is_a?(Array) + data_params[:initial_layout_class_list] = opts[:initial_layout_class_list].join(' ') + else + data_params[:initial_layout_class_list] = opts[:initial_layout_class_list].to_s + end + end + + JWT.encode(data_params, api_secret, 'HS256', header_fields={typ: 'JWT'}) + end # this works when using extend TokenGenerator # def generates_tokens(method_opts) diff --git a/lib/opentok/version.rb b/lib/opentok/version.rb index fcbe1618..b9ce99e1 100644 --- a/lib/opentok/version.rb +++ b/lib/opentok/version.rb @@ -1,4 +1,4 @@ module OpenTok # @private - VERSION = '4.9.0' + VERSION = '4.10.0' end diff --git a/spec/matchers/token.rb b/spec/matchers/token.rb index 0906697e..eed8f86d 100644 --- a/spec/matchers/token.rb +++ b/spec/matchers/token.rb @@ -3,8 +3,9 @@ require "base64" require "openssl" require "addressable/uri" +require "jwt" -RSpec::Matchers.define :carry_token_data do |input_data| +RSpec::Matchers.define :carry_t1_token_data do |input_data| option_to_token_key = { :api_key => :partner_id, :data => :connection_data, @@ -37,7 +38,7 @@ end end -RSpec::Matchers.define :carry_valid_token_signature do |api_secret| +RSpec::Matchers.define :carry_valid_t1_token_signature do |api_secret| match do |token| decoded_token = Base64.decode64(token[4..token.length]) metadata, data_string = decoded_token.split(':') @@ -48,3 +49,34 @@ signature == OpenSSL::HMAC.hexdigest(digest, api_secret, data_string) end end + +RSpec::Matchers.define :carry_jwt_token_data do |input_data| + match do |token| + decoded_token = JWT.decode(token, nil, false) + token_data = decoded_token.first.transform_keys {|k| k.to_sym}.transform_values {|v| v.to_s} + check_token_data = lambda { |key, value| + if token_data.has_key? key + unless value.nil? + return token_data[key] == value.to_s + end + return true + end + false + } + unless input_data.respond_to? :all? + return check_token_data.call(input_data, nil) + end + input_data.all? { |k, v| check_token_data.call(k, v) } + end +end + +RSpec::Matchers.define :carry_valid_jwt_token_signature do |api_secret| + match do |token| + begin + JWT.decode(token, api_secret, true) + rescue + return false + end + true + end +end diff --git a/spec/opentok/session_spec.rb b/spec/opentok/session_spec.rb index 628c50e8..b6a6c9fc 100644 --- a/spec/opentok/session_spec.rb +++ b/spec/opentok/session_spec.rb @@ -72,5 +72,4 @@ end include_examples "session generates tokens" end - end diff --git a/spec/shared/opentok_generates_tokens.rb b/spec/shared/opentok_generates_tokens.rb index a96185bc..277756ab 100644 --- a/spec/shared/opentok_generates_tokens.rb +++ b/spec/shared/opentok_generates_tokens.rb @@ -15,124 +15,284 @@ let(:api_secret) { "1234567890abcdef1234567890abcdef1234567890" } let(:session_id) { "1_MX4xMjM0NTZ-flNhdCBNYXIgMTUgMTQ6NDI6MjMgUERUIDIwMTR-MC40OTAxMzAyNX4" } let(:default_role) { :publisher } + let(:ist) { "project" } + let(:scope) { "session.connect" } - it "generates plain tokens" do - plain_token = opentok.generate_token session_id - expect(plain_token).to be_an_instance_of String - expect(plain_token).to carry_token_data :session_id => session_id - expect(plain_token).to carry_token_data :api_key => api_key - expect(plain_token).to carry_token_data :role => default_role - expect(plain_token).to carry_token_data [:nonce, :create_time] - expect(plain_token).to carry_valid_token_signature api_secret - end + context "when token type is T1" do + it "generates plain tokens" do + plain_token = opentok.generate_token session_id, :token_type => "T1" + expect(plain_token).to be_an_instance_of String + expect(plain_token).to carry_t1_token_data :session_id => session_id + expect(plain_token).to carry_t1_token_data :api_key => api_key + expect(plain_token).to carry_t1_token_data :role => default_role + expect(plain_token).to carry_t1_token_data [:nonce, :create_time] + expect(plain_token).to carry_valid_t1_token_signature api_secret + end - it "generates tokens with an expire time" do - expire_time = Time.now + (60*60*24) - expiring_token = opentok.generate_token session_id, :expire_time => expire_time - expect(expiring_token).to be_an_instance_of String - expect(expiring_token).to carry_token_data :session_id => session_id - expect(expiring_token).to carry_token_data :api_key => api_key - expect(expiring_token).to carry_token_data :role => default_role - expect(expiring_token).to carry_token_data :expire_time => expire_time.to_i - expect(expiring_token).to carry_token_data [:nonce, :create_time] - expect(expiring_token).to carry_valid_token_signature api_secret - end + it "generates tokens with an expire time" do + expire_time = Time.now + (60*60*24) + expiring_token = opentok.generate_token session_id, :expire_time => expire_time, :token_type => "T1" + expect(expiring_token).to be_an_instance_of String + expect(expiring_token).to carry_t1_token_data :session_id => session_id + expect(expiring_token).to carry_t1_token_data :api_key => api_key + expect(expiring_token).to carry_t1_token_data :role => default_role + expect(expiring_token).to carry_t1_token_data :expire_time => expire_time.to_i + expect(expiring_token).to carry_t1_token_data [:nonce, :create_time] + expect(expiring_token).to carry_valid_t1_token_signature api_secret + end - it "generates tokens with an integer expire time" do - expire_time = Time.now.to_i + (60*60*24) - expiring_token = opentok.generate_token session_id, :expire_time => expire_time - expect(expiring_token).to be_an_instance_of String - expect(expiring_token).to carry_token_data :session_id => session_id - expect(expiring_token).to carry_token_data :api_key => api_key - expect(expiring_token).to carry_token_data :role => default_role - expect(expiring_token).to carry_token_data :expire_time => expire_time - expect(expiring_token).to carry_token_data [:nonce, :create_time] - expect(expiring_token).to carry_valid_token_signature api_secret - end + it "generates tokens with an integer expire time" do + expire_time = Time.now.to_i + (60*60*24) + expiring_token = opentok.generate_token session_id, :expire_time => expire_time, :token_type => "T1" + expect(expiring_token).to be_an_instance_of String + expect(expiring_token).to carry_t1_token_data :session_id => session_id + expect(expiring_token).to carry_t1_token_data :api_key => api_key + expect(expiring_token).to carry_t1_token_data :role => default_role + expect(expiring_token).to carry_t1_token_data :expire_time => expire_time + expect(expiring_token).to carry_t1_token_data [:nonce, :create_time] + expect(expiring_token).to carry_valid_t1_token_signature api_secret + end - it "generates tokens with a publisher role" do - role = :publisher - role_token = opentok.generate_token session_id, :role => role - expect(role_token).to be_an_instance_of String - expect(role_token).to carry_token_data :session_id => session_id - expect(role_token).to carry_token_data :api_key => api_key - expect(role_token).to carry_token_data :role => role - expect(role_token).to carry_token_data [:nonce, :create_time] - expect(role_token).to carry_valid_token_signature api_secret - end + it "generates tokens with a publisher role" do + role = :publisher + role_token = opentok.generate_token session_id, :role => role, :token_type => "T1" + expect(role_token).to be_an_instance_of String + expect(role_token).to carry_t1_token_data :session_id => session_id + expect(role_token).to carry_t1_token_data :api_key => api_key + expect(role_token).to carry_t1_token_data :role => role + expect(role_token).to carry_t1_token_data [:nonce, :create_time] + expect(role_token).to carry_valid_t1_token_signature api_secret + end - it "generates tokens with a subscriber role" do - role = :subscriber - role_token = opentok.generate_token session_id, :role => role - expect(role_token).to be_an_instance_of String - expect(role_token).to carry_token_data :session_id => session_id - expect(role_token).to carry_token_data :api_key => api_key - expect(role_token).to carry_token_data :role => role - expect(role_token).to carry_token_data [:nonce, :create_time] - expect(role_token).to carry_valid_token_signature api_secret - end + it "generates tokens with a subscriber role" do + role = :subscriber + role_token = opentok.generate_token session_id, :role => role, :token_type => "T1" + expect(role_token).to be_an_instance_of String + expect(role_token).to carry_t1_token_data :session_id => session_id + expect(role_token).to carry_t1_token_data :api_key => api_key + expect(role_token).to carry_t1_token_data :role => role + expect(role_token).to carry_t1_token_data [:nonce, :create_time] + expect(role_token).to carry_valid_t1_token_signature api_secret + end - it "generates tokens with a moderator role" do - role = :moderator - role_token = opentok.generate_token session_id, :role => role - expect(role_token).to be_an_instance_of String - expect(role_token).to carry_token_data :session_id => session_id - expect(role_token).to carry_token_data :api_key => api_key - expect(role_token).to carry_token_data :role => role - expect(role_token).to carry_token_data [:nonce, :create_time] - expect(role_token).to carry_valid_token_signature api_secret - end + it "generates tokens with a moderator role" do + role = :moderator + role_token = opentok.generate_token session_id, :role => role, :token_type => "T1" + expect(role_token).to be_an_instance_of String + expect(role_token).to carry_t1_token_data :session_id => session_id + expect(role_token).to carry_t1_token_data :api_key => api_key + expect(role_token).to carry_t1_token_data :role => role + expect(role_token).to carry_t1_token_data [:nonce, :create_time] + expect(role_token).to carry_valid_t1_token_signature api_secret + end - it "generates tokens with a publisheronly role" do - role = :publisheronly - role_token = opentok.generate_token session_id, :role => role - expect(role_token).to be_an_instance_of String - expect(role_token).to carry_token_data :session_id => session_id - expect(role_token).to carry_token_data :api_key => api_key - expect(role_token).to carry_token_data :role => role - expect(role_token).to carry_token_data [:nonce, :create_time] - expect(role_token).to carry_valid_token_signature api_secret - end + it "generates tokens with a publisheronly role" do + role = :publisheronly + role_token = opentok.generate_token session_id, :role => role, :token_type => "T1" + expect(role_token).to be_an_instance_of String + expect(role_token).to carry_t1_token_data :session_id => session_id + expect(role_token).to carry_t1_token_data :api_key => api_key + expect(role_token).to carry_t1_token_data :role => role + expect(role_token).to carry_t1_token_data [:nonce, :create_time] + expect(role_token).to carry_valid_t1_token_signature api_secret + end - it "generates tokens with data" do - data = "name=Johnny" - data_bearing_token = opentok.generate_token session_id, :data => data - expect(data_bearing_token).to be_an_instance_of String - expect(data_bearing_token).to carry_token_data :session_id => session_id - expect(data_bearing_token).to carry_token_data :api_key => api_key - expect(data_bearing_token).to carry_token_data :role => default_role - expect(data_bearing_token).to carry_token_data :data => data - expect(data_bearing_token).to carry_token_data [:nonce, :create_time] - expect(data_bearing_token).to carry_valid_token_signature api_secret + it "generates tokens with data" do + data = "name=Johnny" + data_bearing_token = opentok.generate_token session_id, :data => data, :token_type => "T1" + expect(data_bearing_token).to be_an_instance_of String + expect(data_bearing_token).to carry_t1_token_data :session_id => session_id + expect(data_bearing_token).to carry_t1_token_data :api_key => api_key + expect(data_bearing_token).to carry_t1_token_data :role => default_role + expect(data_bearing_token).to carry_t1_token_data :data => data + expect(data_bearing_token).to carry_t1_token_data [:nonce, :create_time] + expect(data_bearing_token).to carry_valid_t1_token_signature api_secret + end + + it "generates tokens with initial layout classes" do + layout_classes = ["focus", "small"] + layout_class_bearing_token = opentok.generate_token session_id, :initial_layout_class_list => layout_classes, :token_type => "T1" + expect(layout_class_bearing_token).to be_an_instance_of String + expect(layout_class_bearing_token).to carry_t1_token_data :session_id => session_id + expect(layout_class_bearing_token).to carry_t1_token_data :api_key => api_key + expect(layout_class_bearing_token).to carry_t1_token_data :role => default_role + expect(layout_class_bearing_token).to carry_t1_token_data :initial_layout_class_list => layout_classes.join(' ') + expect(layout_class_bearing_token).to carry_t1_token_data [:nonce, :create_time] + expect(layout_class_bearing_token).to carry_valid_t1_token_signature api_secret + end + + it "generates tokens with one initial layout class" do + layout_class = "focus" + layout_class_bearing_token = opentok.generate_token session_id, :initial_layout_class_list => layout_class, :token_type => "T1" + expect(layout_class_bearing_token).to be_an_instance_of String + expect(layout_class_bearing_token).to carry_t1_token_data :session_id => session_id + expect(layout_class_bearing_token).to carry_t1_token_data :api_key => api_key + expect(layout_class_bearing_token).to carry_t1_token_data :role => default_role + expect(layout_class_bearing_token).to carry_t1_token_data :initial_layout_class_list => layout_class + expect(layout_class_bearing_token).to carry_t1_token_data [:nonce, :create_time] + expect(layout_class_bearing_token).to carry_valid_t1_token_signature api_secret + end + + context "when the role is invalid" do + it "raises an error" do + expect { opentok.generate_token session_id, :role => :invalid_role, :token_type => "T1" }.to raise_error + end + end end - it "generates tokens with initial layout classes" do - layout_classes = ["focus", "small"] - layout_class_bearing_token = opentok.generate_token session_id, :initial_layout_class_list => layout_classes - expect(layout_class_bearing_token).to be_an_instance_of String - expect(layout_class_bearing_token).to carry_token_data :session_id => session_id - expect(layout_class_bearing_token).to carry_token_data :api_key => api_key - expect(layout_class_bearing_token).to carry_token_data :role => default_role - expect(layout_class_bearing_token).to carry_token_data :initial_layout_class_list => layout_classes.join(' ') - expect(layout_class_bearing_token).to carry_token_data [:nonce, :create_time] - expect(layout_class_bearing_token).to carry_valid_token_signature api_secret + context "when token type is JWT" do + it "generates plain tokens" do + plain_token = opentok.generate_token session_id, :token_type => "JWT" + expect(plain_token).to be_an_instance_of String + expect(plain_token).to carry_jwt_token_data :session_id => session_id + expect(plain_token).to carry_jwt_token_data :iss => api_key + expect(plain_token).to carry_jwt_token_data :ist => ist + expect(plain_token).to carry_jwt_token_data :scope => scope + expect(plain_token).to carry_jwt_token_data :role => default_role + expect(plain_token).to carry_jwt_token_data [:ist, :iat, :nonce] + expect(plain_token).to carry_valid_jwt_token_signature api_secret + end + + it "generates tokens with a custom expire time" do + expire_time = Time.now + (60*60*24) + expiring_token = opentok.generate_token session_id, :expire_time => expire_time, :token_type => "JWT" + expect(expiring_token).to be_an_instance_of String + expect(expiring_token).to carry_jwt_token_data :session_id => session_id + expect(expiring_token).to carry_jwt_token_data :iss => api_key + expect(expiring_token).to carry_jwt_token_data :ist => ist + expect(expiring_token).to carry_jwt_token_data :scope => scope + expect(expiring_token).to carry_jwt_token_data :role => default_role + expect(expiring_token).to carry_jwt_token_data :exp => expire_time.to_i + expect(expiring_token).to carry_jwt_token_data [:ist, :iat, :nonce] + expect(expiring_token).to carry_valid_jwt_token_signature api_secret + end + + it "generates tokens with an integer expire time" do + expire_time = Time.now.to_i + (60*60*24) + expiring_token = opentok.generate_token session_id, :expire_time => expire_time, :token_type => "JWT" + expect(expiring_token).to be_an_instance_of String + expect(expiring_token).to carry_jwt_token_data :session_id => session_id + expect(expiring_token).to carry_jwt_token_data :iss => api_key + expect(expiring_token).to carry_jwt_token_data :ist => ist + expect(expiring_token).to carry_jwt_token_data :scope => scope + expect(expiring_token).to carry_jwt_token_data :role => default_role + expect(expiring_token).to carry_jwt_token_data :exp => expire_time + expect(expiring_token).to carry_jwt_token_data [:ist, :iat, :nonce] + expect(expiring_token).to carry_valid_jwt_token_signature api_secret + end + + it "generates tokens with a publisher role" do + role = :publisher + role_token = opentok.generate_token session_id, :role => role, :token_type => "JWT" + expect(role_token).to be_an_instance_of String + expect(role_token).to carry_jwt_token_data :session_id => session_id + expect(role_token).to carry_jwt_token_data :iss => api_key + expect(role_token).to carry_jwt_token_data :ist => ist + expect(role_token).to carry_jwt_token_data :scope => scope + expect(role_token).to carry_jwt_token_data :role => role + expect(role_token).to carry_jwt_token_data [:ist, :iat, :nonce] + expect(role_token).to carry_valid_jwt_token_signature api_secret + end + + it "generates tokens with a subscriber role" do + role = :subscriber + role_token = opentok.generate_token session_id, :role => role, :token_type => "JWT" + expect(role_token).to be_an_instance_of String + expect(role_token).to carry_jwt_token_data :session_id => session_id + expect(role_token).to carry_jwt_token_data :iss => api_key + expect(role_token).to carry_jwt_token_data :ist => ist + expect(role_token).to carry_jwt_token_data :scope => scope + expect(role_token).to carry_jwt_token_data :role => role + expect(role_token).to carry_jwt_token_data [:ist, :iat, :nonce] + expect(role_token).to carry_valid_jwt_token_signature api_secret + end + + it "generates tokens with a moderator role" do + role = :moderator + role_token = opentok.generate_token session_id, :role => role, :token_type => "JWT" + expect(role_token).to be_an_instance_of String + expect(role_token).to carry_jwt_token_data :session_id => session_id + expect(role_token).to carry_jwt_token_data :iss => api_key + expect(role_token).to carry_jwt_token_data :ist => ist + expect(role_token).to carry_jwt_token_data :scope => scope + expect(role_token).to carry_jwt_token_data :role => role + expect(role_token).to carry_jwt_token_data [:ist, :iat, :nonce] + expect(role_token).to carry_valid_jwt_token_signature api_secret + end + + it "generates tokens with a publisheronly role" do + role = :publisheronly + role_token = opentok.generate_token session_id, :role => role, :token_type => "JWT" + expect(role_token).to be_an_instance_of String + expect(role_token).to carry_jwt_token_data :session_id => session_id + expect(role_token).to carry_jwt_token_data :iss => api_key + expect(role_token).to carry_jwt_token_data :ist => ist + expect(role_token).to carry_jwt_token_data :scope => scope + expect(role_token).to carry_jwt_token_data :role => role + expect(role_token).to carry_jwt_token_data [:ist, :iat, :nonce] + expect(role_token).to carry_valid_jwt_token_signature api_secret + end + + it "generates tokens with data" do + data = "name=Johnny" + data_bearing_token = opentok.generate_token session_id, :data => data, :token_type => "JWT" + expect(data_bearing_token).to be_an_instance_of String + expect(data_bearing_token).to carry_jwt_token_data :session_id => session_id + expect(data_bearing_token).to carry_jwt_token_data :iss => api_key + expect(data_bearing_token).to carry_jwt_token_data :ist => ist + expect(data_bearing_token).to carry_jwt_token_data :scope => scope + expect(data_bearing_token).to carry_jwt_token_data :role => default_role + expect(data_bearing_token).to carry_jwt_token_data :connection_data => data + expect(data_bearing_token).to carry_jwt_token_data [:ist, :iat, :nonce] + expect(data_bearing_token).to carry_valid_jwt_token_signature api_secret + end + + it "generates tokens with initial layout classes" do + layout_classes = ["focus", "small"] + layout_class_bearing_token = opentok.generate_token session_id, :initial_layout_class_list => layout_classes, :token_type => "JWT" + expect(layout_class_bearing_token).to be_an_instance_of String + expect(layout_class_bearing_token).to carry_jwt_token_data :session_id => session_id + expect(layout_class_bearing_token).to carry_jwt_token_data :iss => api_key + expect(layout_class_bearing_token).to carry_jwt_token_data :ist => ist + expect(layout_class_bearing_token).to carry_jwt_token_data :scope => scope + expect(layout_class_bearing_token).to carry_jwt_token_data :role => default_role + expect(layout_class_bearing_token).to carry_jwt_token_data :initial_layout_class_list => layout_classes.join(' ') + expect(layout_class_bearing_token).to carry_jwt_token_data [:ist, :iat, :nonce] + expect(layout_class_bearing_token).to carry_valid_jwt_token_signature api_secret + end + + it "generates tokens with one initial layout class" do + layout_class = "focus" + layout_class_bearing_token = opentok.generate_token session_id, :initial_layout_class_list => layout_class, :token_type => "JWT" + expect(layout_class_bearing_token).to be_an_instance_of String + expect(layout_class_bearing_token).to carry_jwt_token_data :session_id => session_id + expect(layout_class_bearing_token).to carry_jwt_token_data :iss => api_key + expect(layout_class_bearing_token).to carry_jwt_token_data :ist => ist + expect(layout_class_bearing_token).to carry_jwt_token_data :scope => scope + expect(layout_class_bearing_token).to carry_jwt_token_data :role => default_role + expect(layout_class_bearing_token).to carry_jwt_token_data :initial_layout_class_list => layout_class + expect(layout_class_bearing_token).to carry_jwt_token_data [:ist, :iat, :nonce] + expect(layout_class_bearing_token).to carry_valid_jwt_token_signature api_secret + end + + context "when the role is invalid" do + it "raises an error" do + expect { opentok.generate_token session_id, :role => :invalid_role, :token_type => "JWT" }.to raise_error + end + end end - it "generates tokens with one initial layout class" do - layout_class = "focus" - layout_class_bearing_token = opentok.generate_token session_id, :initial_layout_class_list => layout_class - expect(layout_class_bearing_token).to be_an_instance_of String - expect(layout_class_bearing_token).to carry_token_data :session_id => session_id - expect(layout_class_bearing_token).to carry_token_data :api_key => api_key - expect(layout_class_bearing_token).to carry_token_data :role => default_role - expect(layout_class_bearing_token).to carry_token_data :initial_layout_class_list => layout_class - expect(layout_class_bearing_token).to carry_token_data [:nonce, :create_time] - expect(layout_class_bearing_token).to carry_valid_token_signature api_secret + context "when token type is not specified" do + it "generates a JWT token by default" do + default_token = opentok.generate_token session_id + expect(default_token).to be_an_instance_of String + expect(default_token).to carry_valid_jwt_token_signature api_secret + end end - context "when the role is invalid" do + context "when token type is invalid" do it "raises an error" do - expect { opentok.generate_token session_id, :role => :invalid_role }.to raise_error + expect { opentok.generate_token session_id, :token_type => "invalid_token_type" }.to raise_error end end diff --git a/spec/shared/session_generates_tokens.rb b/spec/shared/session_generates_tokens.rb index 7690b4b2..6f81c8fa 100644 --- a/spec/shared/session_generates_tokens.rb +++ b/spec/shared/session_generates_tokens.rb @@ -15,52 +15,124 @@ let(:api_secret) { "1234567890abcdef1234567890abcdef1234567890" } let(:session_id) { "1_MX4xMjM0NTZ-flNhdCBNYXIgMTUgMTQ6NDI6MjMgUERUIDIwMTR-MC40OTAxMzAyNX4" } let(:default_role) { :publisher } + let(:ist) { "project" } + let(:scope) { "session.connect" } - it "generates plain tokens" do - plain_token = session.generate_token - expect(plain_token).to be_an_instance_of String - expect(plain_token).to carry_token_data :session_id => session_id - expect(plain_token).to carry_token_data :api_key => api_key - expect(plain_token).to carry_token_data :role => default_role - expect(plain_token).to carry_token_data [:nonce, :create_time] - expect(plain_token).to carry_valid_token_signature api_secret - end + context "when token type is T1" do + it "generates plain tokens" do + plain_token = session.generate_token :token_type => "T1" + expect(plain_token).to be_an_instance_of String + expect(plain_token).to carry_t1_token_data :session_id => session_id + expect(plain_token).to carry_t1_token_data :api_key => api_key + expect(plain_token).to carry_t1_token_data :role => default_role + expect(plain_token).to carry_t1_token_data [:nonce, :create_time] + expect(plain_token).to carry_valid_t1_token_signature api_secret + end + + it "generates tokens with an expire time" do + expire_time = Time.now + (60*60*24) + expiring_token = session.generate_token :expire_time => expire_time, :token_type => "T1" + expect(expiring_token).to be_an_instance_of String + expect(expiring_token).to carry_t1_token_data :session_id => session_id + expect(expiring_token).to carry_t1_token_data :api_key => api_key + expect(expiring_token).to carry_t1_token_data :role => default_role + expect(expiring_token).to carry_t1_token_data :expire_time => expire_time.to_i + expect(expiring_token).to carry_t1_token_data [:nonce, :create_time] + expect(expiring_token).to carry_valid_t1_token_signature api_secret + end - it "generates tokens with an expire time" do - expire_time = Time.now + (60*60*24) - expiring_token = session.generate_token :expire_time => expire_time - expect(expiring_token).to be_an_instance_of String - expect(expiring_token).to carry_token_data :session_id => session_id - expect(expiring_token).to carry_token_data :api_key => api_key - expect(expiring_token).to carry_token_data :role => default_role - expect(expiring_token).to carry_token_data :expire_time => expire_time.to_i - expect(expiring_token).to carry_token_data [:nonce, :create_time] - expect(expiring_token).to carry_valid_token_signature api_secret + it "generates tokens with a role" do + role = :moderator + role_token = session.generate_token :role => role, :token_type => "T1" + expect(role_token).to be_an_instance_of String + expect(role_token).to carry_t1_token_data :session_id => session_id + expect(role_token).to carry_t1_token_data :api_key => api_key + expect(role_token).to carry_t1_token_data :role => role + expect(role_token).to carry_t1_token_data [:nonce, :create_time] + expect(role_token).to carry_valid_t1_token_signature api_secret + end + + it "generates tokens with data" do + data = "name=Johnny" + data_bearing_token = session.generate_token :data => data, :token_type => "T1" + expect(data_bearing_token).to be_an_instance_of String + expect(data_bearing_token).to carry_t1_token_data :session_id => session_id + expect(data_bearing_token).to carry_t1_token_data :api_key => api_key + expect(data_bearing_token).to carry_t1_token_data :role => default_role + expect(data_bearing_token).to carry_t1_token_data :data => data + expect(data_bearing_token).to carry_t1_token_data [:nonce, :create_time] + expect(data_bearing_token).to carry_valid_t1_token_signature api_secret + end end - it "generates tokens with a role" do - role = :moderator - role_token = session.generate_token :role => role - expect(role_token).to be_an_instance_of String - expect(role_token).to carry_token_data :session_id => session_id - expect(role_token).to carry_token_data :api_key => api_key - expect(role_token).to carry_token_data :role => role - expect(role_token).to carry_token_data [:nonce, :create_time] - expect(role_token).to carry_valid_token_signature api_secret + context "when token type is JWT" do + it "generates plain tokens" do + plain_token = session.generate_token :token_type => "JWT" + expect(plain_token).to be_an_instance_of String + expect(plain_token).to carry_jwt_token_data :session_id => session_id + expect(plain_token).to carry_jwt_token_data :iss => api_key + expect(plain_token).to carry_jwt_token_data :ist => ist + expect(plain_token).to carry_jwt_token_data :scope => scope + expect(plain_token).to carry_jwt_token_data :role => default_role + expect(plain_token).to carry_jwt_token_data [:ist, :iat, :nonce] + expect(plain_token).to carry_valid_jwt_token_signature api_secret + end + + it "generates tokens with a custom expire time" do + expire_time = Time.now + (60*60*24) + expiring_token = session.generate_token :expire_time => expire_time, :token_type => "JWT" + expect(expiring_token).to be_an_instance_of String + expect(expiring_token).to carry_jwt_token_data :session_id => session_id + expect(expiring_token).to carry_jwt_token_data :iss => api_key + expect(expiring_token).to carry_jwt_token_data :ist => ist + expect(expiring_token).to carry_jwt_token_data :scope => scope + expect(expiring_token).to carry_jwt_token_data :role => default_role + expect(expiring_token).to carry_jwt_token_data :exp => expire_time.to_i + expect(expiring_token).to carry_jwt_token_data [:ist, :iat, :nonce] + expect(expiring_token).to carry_valid_jwt_token_signature api_secret + end + + it "generates tokens with a non-default role" do + role = :moderator + role_token = session.generate_token :role => role, :token_type => "JWT" + expect(role_token).to be_an_instance_of String + expect(role_token).to carry_jwt_token_data :session_id => session_id + expect(role_token).to carry_jwt_token_data :iss => api_key + expect(role_token).to carry_jwt_token_data :ist => ist + expect(role_token).to carry_jwt_token_data :scope => scope + expect(role_token).to carry_jwt_token_data :role => role + expect(role_token).to carry_jwt_token_data [:ist, :iat, :nonce] + expect(role_token).to carry_valid_jwt_token_signature api_secret + end + + it "generates tokens with data" do + data = "name=Johnny" + data_bearing_token = session.generate_token :data => data, :token_type => "JWT" + expect(data_bearing_token).to be_an_instance_of String + expect(data_bearing_token).to carry_jwt_token_data :session_id => session_id + expect(data_bearing_token).to carry_jwt_token_data :iss => api_key + expect(data_bearing_token).to carry_jwt_token_data :ist => ist + expect(data_bearing_token).to carry_jwt_token_data :scope => scope + expect(data_bearing_token).to carry_jwt_token_data :role => default_role + expect(data_bearing_token).to carry_jwt_token_data :connection_data => data + expect(data_bearing_token).to carry_jwt_token_data [:ist, :iat, :nonce] + expect(data_bearing_token).to carry_valid_jwt_token_signature api_secret + end end - it "generates tokens with data" do - data = "name=Johnny" - data_bearing_token = session.generate_token :data => data - expect(data_bearing_token).to be_an_instance_of String - expect(data_bearing_token).to carry_token_data :session_id => session_id - expect(data_bearing_token).to carry_token_data :api_key => api_key - expect(data_bearing_token).to carry_token_data :role => default_role - expect(data_bearing_token).to carry_token_data :data => data - expect(data_bearing_token).to carry_token_data [:nonce, :create_time] - expect(data_bearing_token).to carry_valid_token_signature api_secret + context "when token type is not specified" do + it "generates a JWT token by default" do + default_token = session.generate_token + expect(default_token).to be_an_instance_of String + expect(default_token).to carry_valid_jwt_token_signature api_secret + end end + context "when token type is invalid" do + it "raises an error" do + expect { session.generate_token :token_type => "invalid_token_type" }.to raise_error + end + end # TODO a context about using a bad session_id end