Skip to content

Commit 8064230

Browse files
authored
Mounted engine for creating tokens (#4)
1 parent fd90dbc commit 8064230

File tree

10 files changed

+163
-55
lines changed

10 files changed

+163
-55
lines changed

config/routes.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# frozen_string_literal: true
2+
3+
Tokenable::Engine.routes.draw do
4+
post '/', to: 'tokens#create'
5+
end

lib/tokenable.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
module Tokenable
88
class Error < StandardError; end
9-
class Unauthorized < Error; end
109

11-
# Your code goes here...
10+
class Unauthorized < Error; end
1211
end

lib/tokenable/authable.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ def token_from_header
3838
headers['Authorization'].to_s.split(' ').last
3939
end
4040

41+
def token_from_user(user_id)
42+
jwt_data = {
43+
user_id: user_id,
44+
}
45+
jwt_token = JWT.encode(jwt_data, jwt_secret, 'HS256')
46+
{
47+
user_id: user_id,
48+
token: jwt_token,
49+
}
50+
end
51+
4152
def jwt_user_id
4253
jwt['data']['user_id']
4354
end
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# frozen_string_literal: true
2+
3+
module Tokenable
4+
class TokensController < ::ActionController::API
5+
include Authable
6+
7+
def create
8+
user_id, = User.from_params(params)
9+
raise Tokenable::Unauthorized unless user_id
10+
11+
token = token_from_user(user_id)
12+
render json: { data: token }, status: 201
13+
end
14+
end
15+
end

lib/tokenable/engine.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# frozen_string_literal: true
2+
3+
require_relative 'controllers/tokens_controller'
4+
require_relative 'strategies/devise'
5+
require_relative 'strategies/secure_password'
6+
7+
module Tokenable
8+
class Engine < ::Rails::Engine
9+
isolate_namespace Tokenable
10+
end
11+
end

lib/tokenable/railtie.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22

33
require_relative 'authable'
4+
require_relative 'engine'
45

56
module Tokebale
67
class Railtie < ::Rails::Railtie

lib/tokenable/strategies/devise.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# frozen_string_literal: true
2+
3+
module Tokenable
4+
module Strategies
5+
module Devise
6+
extend ActiveSupport::Concern
7+
8+
class_methods do
9+
# @return [string, nil] Returns user_id + revocation key (not used yet)
10+
def from_params(params)
11+
email, password = parse_auth_params(params)
12+
13+
user = User.find_by(email: email)
14+
return nil unless user
15+
16+
return nil unless user.valid_password?(password)
17+
18+
[user.id, nil]
19+
end
20+
21+
private
22+
23+
def parse_auth_params(params)
24+
[
25+
params.require(:email),
26+
params.require(:password),
27+
]
28+
end
29+
end
30+
end
31+
end
32+
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# frozen_string_literal: true
2+
3+
module Tokenable
4+
module Strategies
5+
module SecurePassword
6+
extend ActiveSupport::Concern
7+
8+
class_methods do
9+
# @return [string, nil] Returns user_id + revocation key (not used yet)
10+
def from_params(params)
11+
email, password = parse_auth_params(params)
12+
13+
user = User.select(:id, :password_digest).find_by(email: email)
14+
return nil unless user
15+
16+
return nil unless user.authenticate(password)
17+
18+
[user.id, nil]
19+
end
20+
21+
private
22+
23+
def parse_auth_params(params)
24+
[
25+
params.require(:email),
26+
params.require(:password),
27+
]
28+
end
29+
end
30+
end
31+
end
32+
end

spec/spec_helper.rb

Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
# This file was generated by the `rspec --init` command. Conventionally, all
24
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
35
# The generated `.rspec` file contains `--require spec_helper` which will cause
@@ -44,57 +46,55 @@
4446
# triggering implicit auto-inclusion in groups with matching metadata.
4547
config.shared_context_metadata_behavior = :apply_to_host_groups
4648

47-
# The settings below are suggested to provide a good initial experience
48-
# with RSpec, but feel free to customize to your heart's content.
49-
=begin
50-
# This allows you to limit a spec run to individual examples or groups
51-
# you care about by tagging them with `:focus` metadata. When nothing
52-
# is tagged with `:focus`, all examples get run. RSpec also provides
53-
# aliases for `it`, `describe`, and `context` that include `:focus`
54-
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
55-
config.filter_run_when_matching :focus
56-
57-
# Allows RSpec to persist some state between runs in order to support
58-
# the `--only-failures` and `--next-failure` CLI options. We recommend
59-
# you configure your source control system to ignore this file.
60-
config.example_status_persistence_file_path = "spec/examples.txt"
61-
62-
# Limits the available syntax to the non-monkey patched syntax that is
63-
# recommended. For more details, see:
64-
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
65-
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
66-
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
67-
config.disable_monkey_patching!
68-
69-
# This setting enables warnings. It's recommended, but in some cases may
70-
# be too noisy due to issues in dependencies.
71-
config.warnings = true
72-
73-
# Many RSpec users commonly either run the entire suite or an individual
74-
# file, and it's useful to allow more verbose output when running an
75-
# individual spec file.
76-
if config.files_to_run.one?
77-
# Use the documentation formatter for detailed output,
78-
# unless a formatter has already been configured
79-
# (e.g. via a command-line flag).
80-
config.default_formatter = "doc"
81-
end
82-
83-
# Print the 10 slowest examples and example groups at the
84-
# end of the spec run, to help surface which specs are running
85-
# particularly slow.
86-
config.profile_examples = 10
87-
88-
# Run specs in random order to surface order dependencies. If you find an
89-
# order dependency and want to debug it, you can fix the order by providing
90-
# the seed, which is printed after each run.
91-
# --seed 1234
92-
config.order = :random
93-
94-
# Seed global randomization in this process using the `--seed` CLI option.
95-
# Setting this allows you to use `--seed` to deterministically reproduce
96-
# test failures related to randomization by passing the same `--seed` value
97-
# as the one that triggered the failure.
98-
Kernel.srand config.seed
99-
=end
49+
# The settings below are suggested to provide a good initial experience
50+
# with RSpec, but feel free to customize to your heart's content.
51+
# # This allows you to limit a spec run to individual examples or groups
52+
# # you care about by tagging them with `:focus` metadata. When nothing
53+
# # is tagged with `:focus`, all examples get run. RSpec also provides
54+
# # aliases for `it`, `describe`, and `context` that include `:focus`
55+
# # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
56+
# config.filter_run_when_matching :focus
57+
#
58+
# # Allows RSpec to persist some state between runs in order to support
59+
# # the `--only-failures` and `--next-failure` CLI options. We recommend
60+
# # you configure your source control system to ignore this file.
61+
# config.example_status_persistence_file_path = "spec/examples.txt"
62+
#
63+
# # Limits the available syntax to the non-monkey patched syntax that is
64+
# # recommended. For more details, see:
65+
# # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
66+
# # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
67+
# # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
68+
# config.disable_monkey_patching!
69+
#
70+
# # This setting enables warnings. It's recommended, but in some cases may
71+
# # be too noisy due to issues in dependencies.
72+
# config.warnings = true
73+
#
74+
# # Many RSpec users commonly either run the entire suite or an individual
75+
# # file, and it's useful to allow more verbose output when running an
76+
# # individual spec file.
77+
# if config.files_to_run.one?
78+
# # Use the documentation formatter for detailed output,
79+
# # unless a formatter has already been configured
80+
# # (e.g. via a command-line flag).
81+
# config.default_formatter = "doc"
82+
# end
83+
#
84+
# # Print the 10 slowest examples and example groups at the
85+
# # end of the spec run, to help surface which specs are running
86+
# # particularly slow.
87+
# config.profile_examples = 10
88+
#
89+
# # Run specs in random order to surface order dependencies. If you find an
90+
# # order dependency and want to debug it, you can fix the order by providing
91+
# # the seed, which is printed after each run.
92+
# # --seed 1234
93+
# config.order = :random
94+
#
95+
# # Seed global randomization in this process using the `--seed` CLI option.
96+
# # Setting this allows you to use `--seed` to deterministically reproduce
97+
# # test failures related to randomization by passing the same `--seed` value
98+
# # as the one that triggered the failure.
99+
# Kernel.srand config.seed
100100
end

spec/tokenable_spec.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
require 'spec_helper'
24

35
describe Tokenable do

0 commit comments

Comments
 (0)