Commit 8c53db7

mo <mo.khan@gmail.com>
2018-09-09 18:09:15
create token model.
1 parent a8c4735
app/models/token.rb
@@ -1,4 +1,45 @@
 class Token < ApplicationRecord
+  enum token_type: { access: 0, refresh: 1 }
   belongs_to :subject, polymorphic: true
   belongs_to :audience, polymorphic: true
+
+  scope :expired, ->{ where('expired_at < ?', Time.now) }
+  scope :revoked, ->{ where('revoked_at < ?', Time.now) }
+
+  after_initialize do |x|
+    x.uuid = SecureRandom.uuid if x.uuid.nil?
+    x.expired_at = 1.hour.from_now if x.expired_at.nil?
+  end
+
+  def revoke!
+    update!(revoked_at: Time.now)
+  end
+
+  def claims(custom_claims = {})
+    {
+      aud: audience.to_param,
+      exp: expired_at.to_i,
+      iat: created_at.to_i,
+      iss: Saml::Kit.configuration.entity_id,
+      jti: uuid,
+      nbf: created_at.to_i,
+      sub: subject.to_param,
+      token_type: token_type,
+    }.merge(custom_claims)
+  end
+
+  def to_jwt(custom_claims = {})
+    @to_jwt ||= BearerToken.new.encode(claims(custom_claims))
+  end
+
+  class << self
+    def claims_for(token, token_type: :access)
+      if token_type == :any
+        claims = claims_for(token, token_type: :access)
+        claims = claims_for(token, token_type: :refresh) if claims.empty?
+        return claims
+      end
+      BearerToken.new.decode(token)
+    end
+  end
 end
config/initializers/configuration.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+config = Rails.application.config
+config.x.jwt.private_key = OpenSSL::PKey::RSA.new(2048)
config/initializers/jwt.rb
@@ -1,3 +0,0 @@
-# frozen_string_literal: true
-
-Rails.application.config.x.jwt.private_key = OpenSSL::PKey::RSA.new(2048)
db/migrate/20180909173139_create_tokens.rb
@@ -5,7 +5,7 @@ class CreateTokens < ActiveRecord::Migration[5.2]
       t.references :subject, polymorphic: true
       t.references :audience, polymorphic: true
       t.integer :token_type, default: 0
-      t.datetime :expires_at
+      t.datetime :expired_at
       t.datetime :revoked_at
 
       t.timestamps
db/schema.rb
@@ -51,7 +51,7 @@ ActiveRecord::Schema.define(version: 2018_09_09_173139) do
     t.string "audience_type"
     t.integer "audience_id"
     t.integer "token_type", default: 0
-    t.datetime "expires_at"
+    t.datetime "expired_at"
     t.datetime "revoked_at"
     t.datetime "created_at", null: false
     t.datetime "updated_at", null: false
spec/models/token_spec.rb
@@ -1,5 +1,40 @@
 require 'rails_helper'
 
 RSpec.describe Token, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+  describe "revoke!" do
+    subject { create(:access_token) }
+
+    context "when the token has not been revoked yet" do
+      before { freeze_time }
+      before { subject.revoke! }
+
+      specify { expect(subject.reload.revoked_at.to_i).to eql(DateTime.now.to_i) }
+    end
+  end
+
+  describe ".expired" do
+    let!(:active_token) { create(:access_token) }
+    let!(:expired_token) { create(:access_token, expired_at: 1.second.ago) }
+
+    specify { expect(Token.expired).to match_array([expired_token]) }
+  end
+
+  describe ".revoked" do
+    let!(:revoked_token) { create(:access_token, revoked_at: 1.second.ago) }
+    let!(:active_token) { create(:access_token) }
+
+    specify { expect(Token.revoked).to match_array([revoked_token]) }
+  end
+
+  describe ".claims_for" do
+    subject { described_class }
+    let(:access_token) { build_stubbed(:access_token).to_jwt }
+    let(:refresh_token) { build_stubbed(:refresh_token).to_jwt }
+
+    specify { expect(subject.claims_for('blah', token_type: :access)).to be_empty }
+    specify { expect(subject.claims_for('blah', token_type: :refresh)).to be_empty }
+    specify { expect(subject.claims_for('blah', token_type: :any)).to be_empty }
+    specify { expect(subject.claims_for(access_token, token_type: :access)).to be_present }
+    specify { expect(subject.claims_for(refresh_token, token_type: :refresh)).to be_present }
+  end
 end
spec/support/active_support.rb
@@ -0,0 +1,11 @@
+RSpec.configure do |config|
+  config.include ActiveSupport::Testing::TimeHelpers
+  config.after :each do |example|
+    travel_back
+  end
+  config.include(Module.new do
+    def freeze_time
+      travel_to 1.second.from_now
+    end
+  end)
+end
spec/factories.rb
@@ -1,6 +1,16 @@
 FactoryBot.define do
   factory :token do
     uuid { SecureRandom.uuid }
+    association :audience, factory: :client
+    association :subject, factory: :user
+
+    factory :access_token do
+      token_type { :access }
+    end
+
+    factory :refresh_token do
+      token_type { :refresh }
+    end
   end
 
   factory :authorization do
spec/rails_helper.rb
@@ -27,7 +27,6 @@ Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
 ActiveRecord::Migration.maintain_test_schema!
 
 RSpec.configure do |config|
-  config.include ActiveSupport::Testing::TimeHelpers
   # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
   config.fixture_path = "#{::Rails.root}/spec/fixtures"