Skip to content

Commit

Permalink
add cookies as storing options for tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
Bilanuk committed Nov 19, 2022
1 parent f0a82c1 commit b66920d
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 21 deletions.
5 changes: 3 additions & 2 deletions app/controllers/api_guard/authentication_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ class AuthenticationController < ApplicationController

def create
if resource.authenticate(params[:password])
create_token_and_set_header(resource, resource_name)
render_success(message: I18n.t('api_guard.authentication.signed_in'))
create_and_set_token_pair(resource, resource_name)
render_success(data: resource, message: I18n.t('api_guard.authentication.signed_in'))
else
render_error(422, message: I18n.t('api_guard.authentication.invalid_login_credentials'))
end
end

def destroy
blacklist_token
remove_tokens_from_cookies
render_success(message: I18n.t('api_guard.authentication.signed_out'))
end

Expand Down
2 changes: 1 addition & 1 deletion app/controllers/api_guard/passwords_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def update
blacklist_token unless ApiGuard.invalidate_old_tokens_on_password_change
destroy_all_refresh_tokens(current_resource)

create_token_and_set_header(current_resource, resource_name)
create_and_set_token_pair(current_resource, resource_name)
render_success(message: I18n.t('api_guard.password.changed'))
else
render_error(422, object: current_resource)
Expand Down
7 changes: 4 additions & 3 deletions app/controllers/api_guard/registration_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@ class RegistrationController < ApplicationController
def create
init_resource(sign_up_params)
if resource.save
create_token_and_set_header(resource, resource_name)
render_success(message: I18n.t('api_guard.registration.signed_up'))
create_and_set_token_pair(resource, resource_name)
render_success(data: resource, message: I18n.t('api_guard.registration.signed_up'))
else
render_error(422, object: resource)
end
end

def destroy
current_resource.destroy
render_success(message: I18n.t('api_guard.registration.account_deleted'))
remove_tokens_from_cookies
render_success(data: nil, message: I18n.t('api_guard.registration.account_deleted'))
end

private
Expand Down
35 changes: 28 additions & 7 deletions app/controllers/api_guard/tokens_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,50 @@

module ApiGuard
class TokensController < ApplicationController
before_action :authenticate_resource, only: [:create]
before_action :find_refresh_token, only: [:create]

def create
create_token_and_set_header(current_resource, resource_name)
create_and_set_token_pair(current_resource, resource_name)

@refresh_token.destroy
blacklist_token if ApiGuard.blacklist_token_after_refreshing
blacklist_token if ApiGuard.blacklist_token_after_refreshing && access_token

render_success(message: I18n.t('api_guard.access_token.refreshed'))
end

private

def find_refresh_token
refresh_token_from_header = request.headers['Refresh-Token']

if refresh_token_from_header
@refresh_token = find_refresh_token_of(current_resource, refresh_token_from_header)
refresh_token_from_request = if ApiGuard.enable_tokens_in_cookies
request.cookies['refresh_token']
else
request.headers['Refresh-Token']
end

if refresh_token_from_request
@refresh_token = RefreshToken.find_by(token: refresh_token_from_request)
return render_error(401, message: I18n.t('api_guard.refresh_token.invalid')) unless @refresh_token
else
render_error(401, message: I18n.t('api_guard.refresh_token.missing'))
end
end

def access_token
@access_token ||= if ApiGuard.enable_tokens_in_cookies
request.cookies['access_token']
else
request.headers['Authorization']&.split('Bearer ')&.last
end
end

def current_resource
@current_resource ||= @refresh_token.send(resource_name)
end

def blacklist_token
return unless token_blacklisting_enabled?(current_resource)

blacklisted_tokens_for(current_resource).create(token: access_token)
end
end
end
3 changes: 3 additions & 0 deletions lib/api_guard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ module Test
{}
end

mattr_accessor :enable_tokens_in_cookies
self.enable_tokens_in_cookies = false

def self.setup
yield self
end
Expand Down
7 changes: 6 additions & 1 deletion lib/api_guard/jwt_auth/authentication.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ def respond_to_missing?(method_name, include_private = false)
def authenticate_and_set_resources(resource_names)
@resource_names = resource_names

@token = request.headers['Authorization']&.split('Bearer ')&.last
@token = if ApiGuard.enable_tokens_in_cookies
request.cookies['access_token']
else
request.headers['Authorization']&.split('Bearer ')&.last
end

return render_error(401, message: I18n.t('api_guard.access_token.missing')) unless @token

authenticate_token
Expand Down
59 changes: 52 additions & 7 deletions lib/api_guard/jwt_auth/json_web_token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ def current_time
@current_time ||= Time.now.utc
end

def token_expire_at
@token_expire_at ||= (current_time + ApiGuard.token_validity).to_i
def access_token_expire_at
@token_expire_at ||= (current_time + ApiGuard.token_validity)
end

def refresh_token_expire_at
@refresh_token_expire_at_date ||= (Time.now.utc + ApiGuard.refresh_token_validity)
end

def token_issued_at
Expand All @@ -38,7 +42,7 @@ def decode(token, verify = true)
def jwt_and_refresh_token(resource, resource_name, expired_token = false, expired_refresh_token = false)
payload = {
"#{resource_name}_id": resource.id,
exp: expired_token ? token_issued_at : token_expire_at,
exp: expired_token ? token_issued_at : access_token_expire_at.to_i,
iat: token_issued_at
}

Expand All @@ -48,17 +52,58 @@ def jwt_and_refresh_token(resource, resource_name, expired_token = false, expire
[encode(payload), new_refresh_token(resource, expired_refresh_token)]
end

# Create tokens and set response headers
def create_token_and_set_header(resource, resource_name)
# Create tokens and set response headers and cookies
def create_and_set_token_pair(resource, resource_name)
access_token, refresh_token = jwt_and_refresh_token(resource, resource_name)
set_token_headers(access_token, refresh_token)

if ApiGuard.enable_tokens_in_cookies
set_token_cookies(access_token, refresh_token)
else
set_token_headers(access_token, refresh_token)
end
end

# Set token details in response headers
def set_token_headers(token, refresh_token = nil)
response.headers['Access-Token'] = token
response.headers['Refresh-Token'] = refresh_token if refresh_token
response.headers['Expire-At'] = token_expire_at.to_s
response.headers['Expire-At'] = access_token_expire_at.to_i.to_s
end

def set_token_cookies(access_token, refresh_token)
response.set_cookie(
'access_token',
{
value: access_token,
http_only: true,
expires: refresh_token_expire_at,
path: '/'
}
)
response.set_cookie(
'refresh_token',
{
value: refresh_token,
http_only: true,
expires: refresh_token_expire_at,
path: '/'
}
)
end

def remove_tokens_from_cookies
response.delete_cookie(
'access_token',
{
path: '/'
}
)
response.delete_cookie(
'refresh_token',
{
path: '/'
}
)
end

# Set token issued at to current timestamp
Expand Down

0 comments on commit b66920d

Please sign in to comment.