Token issuance table (DRAFT)
parent
43bd7a7a1c
commit
ef3259f664
|
@ -2,14 +2,16 @@ module Api
|
|||
class TokensController < Api::AbstractController
|
||||
skip_before_action :authenticate_user!, only: :create
|
||||
skip_before_action :check_fbos_version, only: :create
|
||||
before_action :clean_out_old_tokens
|
||||
|
||||
CREDS = Auth::CreateTokenFromCredentials
|
||||
NO_CREDS = Auth::CreateToken
|
||||
NO_USER_ATTR = "API requets need a `user` attribute that is a JSON object."
|
||||
|
||||
# Give you the same token, but reloads all claims except `exp`
|
||||
def show
|
||||
mutate Auth::ReloadToken.run(jwt: request.headers["Authorization"],
|
||||
fbos_version: fbos_version)
|
||||
mutate Auth::ReloadToken
|
||||
.run(jwt: request.headers["Authorization"], fbos_version: fbos_version)
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -39,6 +41,12 @@ module Api
|
|||
request.xhr?
|
||||
end
|
||||
|
||||
# Every time a token is created, sweep the old TokenIssuances out of the
|
||||
# database.
|
||||
def clean_out_old_tokens
|
||||
TokenIssuance.where("exp < ?", Time.now.to_i).destroy_all
|
||||
end
|
||||
|
||||
def if_properly_formatted
|
||||
user = params.as_json.deep_symbolize_keys.fetch(:user, {})
|
||||
# If data handling for this method gets any more complicated,
|
||||
|
|
|
@ -36,14 +36,17 @@ module Api
|
|||
.as_json
|
||||
.merge!(params.as_json["user"] || {})
|
||||
.deep_symbolize_keys
|
||||
|
||||
jti = RequestStore
|
||||
.store
|
||||
.dig(:jwt, :jti) || "NONE FOUND"
|
||||
{email: user[:email],
|
||||
name: user[:name],
|
||||
password: user[:password],
|
||||
password_confirmation: user[:password_confirmation],
|
||||
new_password: user[:new_password],
|
||||
new_password_confirmation: user[:new_password_confirmation],
|
||||
agree_to_terms: user[:agree_to_terms]}
|
||||
agree_to_terms: user[:agree_to_terms],
|
||||
jti: jti}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,10 +26,12 @@ class SessionToken < AbstractJwtToken
|
|||
raise Errors::Forbidden, MUST_VERIFY
|
||||
end
|
||||
url = CalculateUpgrade.run!(version: fbos_version)
|
||||
jti = SecureRandom.uuid
|
||||
TokenIssuance.create!(device_id: user.device.id, exp: exp, jti: jti)
|
||||
self.new([{ aud: aud,
|
||||
sub: user.id,
|
||||
iat: iat,
|
||||
jti: SecureRandom.uuid,
|
||||
jti: jti,
|
||||
iss: iss,
|
||||
exp: exp,
|
||||
mqtt: MQTT,
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
class TokenIssuance < ApplicationRecord
|
||||
belongs_to :device
|
||||
end
|
|
@ -10,11 +10,21 @@ module Auth
|
|||
RequestStore.store[:jwt] = claims.deep_symbolize_keys
|
||||
u = User.includes(:device).find(claims["sub"])
|
||||
Device.current = u.device
|
||||
TokenIssuance.find_by!(jti: claims["jti"])
|
||||
u
|
||||
rescue JWT::DecodeError, ActiveRecord::RecordNotFound
|
||||
add_error :jwt, :decode_error, Auth::ReloadToken::BAD_SUB
|
||||
end
|
||||
|
||||
# Triggers ActiveRecord::RecordNotFound if TokenIssuance is expired or
|
||||
# missing.
|
||||
def check_it(claims)
|
||||
device_id = claims["bot"].gsub("device_", "").to_i
|
||||
TokenIssuance
|
||||
.where("exp > ?", Time.now.to_i)
|
||||
.find_by!(jti: claims["jti"], bot: device_id)
|
||||
end
|
||||
|
||||
def just_the_token
|
||||
# Token auth requires the `authorization` header to be in the format of:
|
||||
# "Authorization: Bearer <INSERT_TOKEN_HERE>"
|
||||
|
|
|
@ -10,6 +10,9 @@ module Users
|
|||
string :password
|
||||
string :new_password
|
||||
string :new_password_confirmation
|
||||
# Lock everyone out except for the person who requested
|
||||
# the password change.
|
||||
string :old_token_jti
|
||||
end
|
||||
|
||||
def validate
|
||||
|
@ -24,12 +27,23 @@ module Users
|
|||
excludable = [:user]
|
||||
excludable.push(:email) unless skip_email_stuff
|
||||
user.update_attributes!(inputs.except(:user, :email))
|
||||
SendFactoryResetJob.perform_later(user.device) if inputs[:password]
|
||||
if inputs[:password]
|
||||
SendFactoryResetJob.perform_later(user.device)
|
||||
delete_all_tokens_except_this_one
|
||||
end
|
||||
user.reload
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def delete_all_tokens_except_this_one
|
||||
TokenIssuance
|
||||
.where(device_id: user.device.id)
|
||||
.where
|
||||
.not(jti: old_token_jti)
|
||||
.destroy_all
|
||||
end
|
||||
|
||||
# Self hosted users will often not have an email server.
|
||||
# We can update emails immediately in those circumstances.
|
||||
def skip_email_stuff
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
class CreateTokenIssuances < ActiveRecord::Migration[5.1]
|
||||
def change
|
||||
create_table :token_issuances do |t|
|
||||
t.references :device, foreign_key: true, null: false
|
||||
t.integer :exp, null: false
|
||||
t.string :jti, null: false, limit: 45
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
class DropTokenExpirations < ActiveRecord::Migration[5.1]
|
||||
def change
|
||||
drop_table :token_expirations do |t|
|
||||
t.string :sub
|
||||
t.integer :exp
|
||||
t.string :jti
|
||||
t.datetime :created_at, null: false
|
||||
t.datetime :updated_at, null: false
|
||||
end
|
||||
end
|
||||
end
|
12
db/schema.rb
12
db/schema.rb
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20180326160853) do
|
||||
ActiveRecord::Schema.define(version: 20180328212540) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -358,12 +358,13 @@ ActiveRecord::Schema.define(version: 20180326160853) do
|
|||
t.index ["device_id"], name: "index_sequences_on_device_id"
|
||||
end
|
||||
|
||||
create_table "token_expirations", id: :serial, force: :cascade do |t|
|
||||
t.string "sub"
|
||||
t.integer "exp"
|
||||
t.string "jti"
|
||||
create_table "token_issuances", force: :cascade do |t|
|
||||
t.bigint "device_id", null: false
|
||||
t.integer "exp", null: false
|
||||
t.string "jti", limit: 45, null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["device_id"], name: "index_token_issuances_on_device_id"
|
||||
end
|
||||
|
||||
create_table "tool_slots", id: :serial, force: :cascade do |t|
|
||||
|
@ -466,5 +467,6 @@ ActiveRecord::Schema.define(version: 20180326160853) do
|
|||
add_foreign_key "primary_nodes", "sequences"
|
||||
add_foreign_key "sensor_readings", "devices"
|
||||
add_foreign_key "sensors", "devices"
|
||||
add_foreign_key "token_issuances", "devices"
|
||||
add_foreign_key "tool_slots", "tools"
|
||||
end
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
FactoryBot.define do
|
||||
factory :token_issuance do
|
||||
device
|
||||
exp { (Time.now + 4.days).to_i }
|
||||
jti { SecureRandom.uuid }
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe TokenIssuance, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
Loading…
Reference in New Issue