Skip to content

Commit e7ae6ec

Browse files
committed
initial commit
0 parents  commit e7ae6ec

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+999
-0
lines changed

Diff for: .gitignore

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
2+
#
3+
# If you find yourself ignoring temporary files generated by your text editor
4+
# or operating system, you probably want to add a global ignore instead:
5+
# git config --global core.excludesfile '~/.gitignore_global'
6+
7+
# Ignore bundler config.
8+
/.bundle
9+
10+
# Ignore the default SQLite database.
11+
/db/*.sqlite3
12+
/db/*.sqlite3-journal
13+
14+
# Ignore all logfiles and tempfiles.
15+
/log/*
16+
/tmp/*
17+
!/log/.keep
18+
!/tmp/.keep
19+
20+
# Ignore uploaded files in development
21+
/storage/*
22+
!/storage/.keep
23+
24+
.byebug_history
25+
26+
# Ignore master key for decrypting credentials and more.
27+
/config/master.key

Diff for: .ruby-version

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ruby-2.6.5

Diff for: Gemfile

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
source 'https://rubygems.org'
2+
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3+
4+
ruby '2.6.5'
5+
6+
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
7+
gem 'rails', '~> 5.2.5'
8+
# Use sqlite3 as the database for Active Record
9+
gem 'sqlite3'
10+
# Use Puma as the app server
11+
gem 'puma', '~> 3.11'
12+
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
13+
# gem 'jbuilder', '~> 2.5'
14+
# Use Redis adapter to run Action Cable in production
15+
# gem 'redis', '~> 4.0'
16+
# Use ActiveModel has_secure_password
17+
gem 'bcrypt', '~> 3.1.7'
18+
19+
# Use ActiveStorage variant
20+
# gem 'mini_magick', '~> 4.8'
21+
22+
# Use Capistrano for deployment
23+
# gem 'capistrano-rails', group: :development
24+
25+
# Reduces boot times through caching; required in config/boot.rb
26+
gem 'bootsnap', '>= 1.1.0', require: false
27+
28+
# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
29+
# gem 'rack-cors'
30+
31+
group :development, :test do
32+
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
33+
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
34+
end
35+
36+
group :development do
37+
gem 'listen', '>= 3.0.5', '< 3.2'
38+
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
39+
gem 'spring'
40+
gem 'spring-watcher-listen', '~> 2.0.0'
41+
end
42+
43+
44+
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
45+
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

Diff for: Gemfile.lock

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
GEM
2+
remote: https://rubygems.org/
3+
specs:
4+
actioncable (5.2.5)
5+
actionpack (= 5.2.5)
6+
nio4r (~> 2.0)
7+
websocket-driver (>= 0.6.1)
8+
actionmailer (5.2.5)
9+
actionpack (= 5.2.5)
10+
actionview (= 5.2.5)
11+
activejob (= 5.2.5)
12+
mail (~> 2.5, >= 2.5.4)
13+
rails-dom-testing (~> 2.0)
14+
actionpack (5.2.5)
15+
actionview (= 5.2.5)
16+
activesupport (= 5.2.5)
17+
rack (~> 2.0, >= 2.0.8)
18+
rack-test (>= 0.6.3)
19+
rails-dom-testing (~> 2.0)
20+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
21+
actionview (5.2.5)
22+
activesupport (= 5.2.5)
23+
builder (~> 3.1)
24+
erubi (~> 1.4)
25+
rails-dom-testing (~> 2.0)
26+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
27+
activejob (5.2.5)
28+
activesupport (= 5.2.5)
29+
globalid (>= 0.3.6)
30+
activemodel (5.2.5)
31+
activesupport (= 5.2.5)
32+
activerecord (5.2.5)
33+
activemodel (= 5.2.5)
34+
activesupport (= 5.2.5)
35+
arel (>= 9.0)
36+
activestorage (5.2.5)
37+
actionpack (= 5.2.5)
38+
activerecord (= 5.2.5)
39+
marcel (~> 1.0.0)
40+
activesupport (5.2.5)
41+
concurrent-ruby (~> 1.0, >= 1.0.2)
42+
i18n (>= 0.7, < 2)
43+
minitest (~> 5.1)
44+
tzinfo (~> 1.1)
45+
arel (9.0.0)
46+
bcrypt (3.1.12)
47+
bootsnap (1.7.3)
48+
msgpack (~> 1.0)
49+
builder (3.2.4)
50+
byebug (11.1.3)
51+
concurrent-ruby (1.1.8)
52+
crass (1.0.6)
53+
erubi (1.10.0)
54+
ffi (1.15.0)
55+
globalid (0.4.2)
56+
activesupport (>= 4.2.0)
57+
i18n (1.8.10)
58+
concurrent-ruby (~> 1.0)
59+
listen (3.1.5)
60+
rb-fsevent (~> 0.9, >= 0.9.4)
61+
rb-inotify (~> 0.9, >= 0.9.7)
62+
ruby_dep (~> 1.2)
63+
loofah (2.9.1)
64+
crass (~> 1.0.2)
65+
nokogiri (>= 1.5.9)
66+
mail (2.7.1)
67+
mini_mime (>= 0.1.1)
68+
marcel (1.0.1)
69+
method_source (1.0.0)
70+
mini_mime (1.1.0)
71+
mini_portile2 (2.5.0)
72+
minitest (5.14.4)
73+
msgpack (1.4.2)
74+
nio4r (2.5.7)
75+
nokogiri (1.11.3)
76+
mini_portile2 (~> 2.5.0)
77+
racc (~> 1.4)
78+
puma (3.12.6)
79+
racc (1.5.2)
80+
rack (2.2.3)
81+
rack-test (1.1.0)
82+
rack (>= 1.0, < 3)
83+
rails (5.2.5)
84+
actioncable (= 5.2.5)
85+
actionmailer (= 5.2.5)
86+
actionpack (= 5.2.5)
87+
actionview (= 5.2.5)
88+
activejob (= 5.2.5)
89+
activemodel (= 5.2.5)
90+
activerecord (= 5.2.5)
91+
activestorage (= 5.2.5)
92+
activesupport (= 5.2.5)
93+
bundler (>= 1.3.0)
94+
railties (= 5.2.5)
95+
sprockets-rails (>= 2.0.0)
96+
rails-dom-testing (2.0.3)
97+
activesupport (>= 4.2.0)
98+
nokogiri (>= 1.6)
99+
rails-html-sanitizer (1.3.0)
100+
loofah (~> 2.3)
101+
railties (5.2.5)
102+
actionpack (= 5.2.5)
103+
activesupport (= 5.2.5)
104+
method_source
105+
rake (>= 0.8.7)
106+
thor (>= 0.19.0, < 2.0)
107+
rake (13.0.3)
108+
rb-fsevent (0.10.4)
109+
rb-inotify (0.10.1)
110+
ffi (~> 1.0)
111+
ruby_dep (1.5.0)
112+
spring (2.1.1)
113+
spring-watcher-listen (2.0.1)
114+
listen (>= 2.7, < 4.0)
115+
spring (>= 1.2, < 3.0)
116+
sprockets (4.0.2)
117+
concurrent-ruby (~> 1.0)
118+
rack (> 1, < 3)
119+
sprockets-rails (3.2.2)
120+
actionpack (>= 4.0)
121+
activesupport (>= 4.0)
122+
sprockets (>= 3.0.0)
123+
sqlite3 (1.4.2)
124+
thor (1.1.0)
125+
thread_safe (0.3.6)
126+
tzinfo (1.2.9)
127+
thread_safe (~> 0.1)
128+
websocket-driver (0.7.3)
129+
websocket-extensions (>= 0.1.0)
130+
websocket-extensions (0.1.5)
131+
132+
PLATFORMS
133+
ruby
134+
135+
DEPENDENCIES
136+
bcrypt (~> 3.1.7)
137+
bootsnap (>= 1.1.0)
138+
byebug
139+
listen (>= 3.0.5, < 3.2)
140+
puma (~> 3.11)
141+
rails (~> 5.2.5)
142+
spring
143+
spring-watcher-listen (~> 2.0.0)
144+
sqlite3
145+
tzinfo-data
146+
147+
RUBY VERSION
148+
ruby 2.6.5p114
149+
150+
BUNDLED WITH
151+
1.17.3

Diff for: README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# API Key Authentication in Rails Without Devise
2+
3+
See: https://keygen.sh/blog/how-to-implement-api-key-authentication-in-rails-without-devise/

Diff for: Rakefile

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Add your own tasks in files placed in lib/tasks ending in .rake,
2+
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3+
4+
require_relative 'config/application'
5+
6+
Rails.application.load_tasks

Diff for: app/channels/application_cable/channel.rb

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module ApplicationCable
2+
class Channel < ActionCable::Channel::Base
3+
end
4+
end

Diff for: app/channels/application_cable/connection.rb

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module ApplicationCable
2+
class Connection < ActionCable::Connection::Base
3+
end
4+
end

Diff for: app/controllers/api_keys_controller.rb

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
class ApiKeysController < ApplicationController
2+
include ApiKeyAuthenticatable
3+
4+
# Require API key authentication
5+
prepend_before_action :authenticate_with_api_key!, only: %i[index destroy]
6+
7+
def index
8+
render json: current_bearer.api_keys
9+
end
10+
11+
def create
12+
authenticate_with_http_basic do |email, password|
13+
user = User.find_by email: email
14+
15+
if user&.authenticate(password)
16+
api_key = user.api_keys.create! token: SecureRandom.hex
17+
18+
render json: api_key, status: :created and return
19+
end
20+
end
21+
22+
render status: :unauthorized
23+
end
24+
25+
def destroy
26+
api_key = current_bearer.api_keys.find(params[:id])
27+
28+
api_key.destroy
29+
end
30+
end

Diff for: app/controllers/application_controller.rb

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class ApplicationController < ActionController::API
2+
end

Diff for: app/controllers/concerns/.keep

Whitespace-only changes.

Diff for: app/controllers/concerns/api_key_authenticatable.rb

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
module ApiKeyAuthenticatable
2+
include ActionController::HttpAuthentication::Basic::ControllerMethods
3+
include ActionController::HttpAuthentication::Token::ControllerMethods
4+
5+
extend ActiveSupport::Concern
6+
7+
attr_reader :current_api_key
8+
attr_reader :current_bearer
9+
10+
# Use this to raise an error and automatically respond with a 401 HTTP status
11+
# code when API key authentication fails
12+
def authenticate_with_api_key!
13+
@current_bearer = authenticate_or_request_with_http_token &method(:authenticator)
14+
end
15+
16+
# Use this for optional API key authentication
17+
def authenticate_with_api_key
18+
@current_bearer = authenticate_with_http_token &method(:authenticator)
19+
end
20+
21+
private
22+
23+
attr_writer :current_api_key
24+
attr_writer :current_bearer
25+
26+
def authenticator(token, options)
27+
@current_api_key = ApiKey.authenticate_by_token token
28+
29+
current_api_key&.bearer
30+
end
31+
end

Diff for: app/jobs/application_job.rb

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class ApplicationJob < ActiveJob::Base
2+
end

Diff for: app/mailers/application_mailer.rb

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class ApplicationMailer < ActionMailer::Base
2+
default from: '[email protected]'
3+
layout 'mailer'
4+
end

Diff for: app/models/api_key.rb

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
class ApiKey < ApplicationRecord
2+
HMAC_SECRET_KEY = ENV.fetch('API_KEY_HMAC_SECRET_KEY')
3+
4+
# Virtual attribute for raw token value, allowing us to respond with the
5+
# API key's non-hashed token value. but only directly after creation.
6+
attr_accessor :token
7+
8+
belongs_to :bearer, polymorphic: true
9+
10+
before_create :generate_token_hmac
11+
12+
def self.authenticate_by_token!(token)
13+
digest = OpenSSL::HMAC.hexdigest 'SHA256', HMAC_SECRET_KEY, token
14+
15+
find_by! token_digest: digest
16+
end
17+
18+
def self.authenticate_by_token(token)
19+
authenticate_by_token! token
20+
rescue ActiveRecord::RecordNotFound
21+
nil
22+
end
23+
24+
# Add virtual token attribute to serializable attributes, and exclude
25+
# the token's HMAC digest
26+
def serializable_hash(options = nil)
27+
h = super options.merge(except: 'token_digest')
28+
h.merge! 'token' => token if token.present?
29+
h
30+
end
31+
32+
private
33+
34+
def generate_token_hmac
35+
raise ActiveRecord::RecordInvalid, 'token is required' unless token.present?
36+
37+
digest = OpenSSL::HMAC.hexdigest 'SHA256', HMAC_SECRET_KEY, token
38+
39+
self.token_digest = digest
40+
end
41+
end

Diff for: app/models/application_record.rb

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class ApplicationRecord < ActiveRecord::Base
2+
self.abstract_class = true
3+
end

Diff for: app/models/concerns/.keep

Whitespace-only changes.

Diff for: app/models/user.rb

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class User < ApplicationRecord
2+
has_many :api_keys, as: :bearer
3+
4+
has_secure_password
5+
end

Diff for: app/views/layouts/mailer.html.erb

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5+
<style>
6+
/* Email styles need to be inline */
7+
</style>
8+
</head>
9+
10+
<body>
11+
<%= yield %>
12+
</body>
13+
</html>

Diff for: app/views/layouts/mailer.text.erb

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<%= yield %>

0 commit comments

Comments
 (0)