Commit 8c53db7
Changed files (9)
app
models
config
initializers
db
spec
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"