main
1# frozen_string_literal: true
2
3class Token < ApplicationRecord
4 audited associated_with: :subject
5 enum token_type: { access: 0, refresh: 1 }
6 belongs_to :authorization, optional: true
7 belongs_to :subject, polymorphic: true
8 belongs_to :audience, polymorphic: true
9
10 scope :active, -> { where.not(id: revoked.or(where(id: expired))) }
11 scope :expired, -> { where('expired_at < ?', Time.current) }
12 scope :revoked, -> { where('revoked_at < ?', Time.current) }
13
14 after_initialize do |x|
15 if x.expired_at.nil?
16 x.expired_at = access? ? 1.hour.from_now : 1.day.from_now
17 end
18 end
19
20 def issued_to?(audience)
21 self.audience == audience
22 end
23
24 def revoke!
25 ActiveRecord::Base.transaction do
26 update!(revoked_at: Time.current)
27 authorization&.revoke!
28 end
29 end
30
31 def revoked?
32 revoked_at.present?
33 end
34
35 def claims(custom_claims = {})
36 {
37 aud: audience.to_param,
38 exp: expired_at.to_i,
39 iat: created_at.to_i,
40 iss: Saml::Kit.configuration.entity_id,
41 jti: id,
42 nbf: created_at.to_i,
43 sub: subject.to_param,
44 token_type: token_type,
45 }.merge(custom_claims)
46 end
47
48 def to_jwt(custom_claims = {})
49 @to_jwt ||= BearerToken.new.encode(claims(custom_claims))
50 end
51
52 def issue_tokens_to(client, token_types: [:access, :refresh])
53 transaction do
54 revoke!
55 token_types.map do |x|
56 Token.create!(subject: subject, audience: client, token_type: x)
57 end
58 end
59 end
60
61 class << self
62 def revoked?(jti)
63 revoked = Rails.cache.fetch("revoked-tokens", expires_in: 10.minutes) do
64 Hash[Token.revoked.pluck(:id).map { |x| [x, true] }]
65 end
66 revoked[jti]
67 end
68
69 def claims_for(token, token_type: :access)
70 if token_type == :any
71 claims = claims_for(token, token_type: :access)
72 claims = claims_for(token, token_type: :refresh) if claims.empty?
73 return claims
74 end
75 BearerToken.new.decode(token)
76 end
77
78 def authenticate(jwt)
79 claims = claims_for(jwt, token_type: :access)
80 return if claims.empty?
81
82 token = Token.find(claims[:jti])
83 return if token.refresh? || token.revoked?
84
85 token
86 end
87 end
88end