Created
July 23, 2015 07:51
-
-
Save evadne/0ad4d309a87972058950 to your computer and use it in GitHub Desktop.
Ephemeral Secure Token with JWT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class EphemeralToken | |
attr_reader :origin, :targets, :expires_at, :payload | |
class TokenInvalid < StandardError; end | |
class TokenExpired < TokenInvalid; end | |
Algorithm = 'HS512' | |
Secret = ENV['SECRET_EPHEMERAL_TOKEN_KEY'] | |
ObjectToNotation = -> (target) { [target.class.model_name.name, target.id] } | |
NotationToObject = -> ((model_name, model_id)) { model_name.constantize.find_by_id(model_id) } | |
class << self | |
def parse (token) | |
payload, _ = decode_jwt(token) | |
data, exp = payload.values_at('data', 'exp') | |
new({ | |
origin: data['origin'].try(&NotationToObject), | |
targets: data['targets'].map(&NotationToObject).compact, | |
expires_at: exp.try { |x| Time.at(x) } | |
}) | |
end | |
private | |
def decode_jwt (token) | |
JWT.decode(token, Secret) | |
rescue JWT::ExpiredSignature | |
raise TokenExpired.new 'the token has expired' | |
rescue JWT::DecodeError | |
raise TokenInvalid.new 'the token is not valid' | |
end | |
end | |
def initialize (origin: nil, targets: [], expires_at: nil) | |
@origin = origin | |
@targets = Array(targets) | |
@expires_at = expires_at | |
end | |
def payload | |
@payload ||= build_payload | |
end | |
def to_jwt | |
JWT.encode payload, Secret, Algorithm | |
end | |
def has_target? (target) | |
targets.any? { |x| | |
(x.id == target.id) && (x.class.model_name.name == target.class.model_name.name) | |
} | |
end | |
private | |
def build_payload | |
{ | |
data: { | |
origin: origin.try(&ObjectToNotation), | |
targets: targets.map(&ObjectToNotation) | |
} | |
}.tap { |x| | |
if expires_at.present? | |
x[:exp] = expires_at.to_time.to_i | |
end | |
} | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Concerns::HasPreviewToken | |
extend ActiveSupport::Concern | |
def authorize_preview! (target) | |
unless preview_token.has_target?(target) | |
deny_preview | |
end | |
rescue EphemeralToken::TokenInvalid | |
deny_preview | |
end | |
def deny_preview | |
raise CanCan::AccessDenied.new('you are not authorized to view this page.') | |
end | |
def preview_token | |
@preview_token ||= EphemeralToken.parse(params[:preview_token]) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@yyyc514:
I have a Rails Controller Concern as follows, using CanCan, which I mix into controllers that utilize it: