[cover image by Henri Guérin at pixels.com].
В настоящее время я работаю над разработкой api с использованием grape и devise jwt для аутентификации пользователей.
Настройка devise jwt довольно проста, все, что вам нужно сделать, это просто следовать инструкциям в readme.
с Grape
Установите gems
gem 'grape'
gem 'devise'
gem 'devise-jwt'
установить пакет
в app/api/api.rb
помощники AuthHelpers
помощники do
def unauthorized_error!
error!(‘Unauthorized’, 401)
end
end
mount V1::UserRegistrationApi
в app/api/auth_helpers.rb
модуль AuthHelpers
def current_user
Warden::JWTAuth::UserDecoder.new.call(token, :user, nil)
спасение
unauthorized_error!
end
def token
auth = headers[‘Authorization’].to_s
auth.split.last
end
end
в app/models/user.rb
class User < ApplicationRecord
include Devise::JWT::RevocationStrategies::Allowlist
devise :database_authenticatable, :registerable,
:восстанавливаемый, :запоминаемый, :валидируемый,
:jwt_authenticatable, jwt_revocation_strategy: self
end
создать AllowlistedJwt
bin/rails g модель AllowlistedJwt
class CreateAllowlistedJwts < ActiveRecord::Migration[6.1]
def change
create_table :allowlisted_jwts do |t|
t.string :jti, null: false
t.string :aud
t.datetime :exp, null: false
t.references :user, foreign_key: { on_delete: :cascade }, null: false
t.timestamps
end
add_index :allowlisted_jwts, :jti, unique: true
end
end
в config/initializers/devise.rb
config.jwt do |jwt|
jwt.secret = Rails.application.credentials.devise_jwt_secret_key!
jwt.expiration_time = 3600
конец
в app/api/v1/user_registration_api.rb
frozen_string_literal: true
модуль V1
class UserRegistrationApi < Grape::API
namespace :user do
пространство имен :register do
before do
@user_mobile_number = UserRegistrationWithMobileNumberService.new params[:mobile_number]
end
after do
header 'Authorization', @user_mobile_number.token
end
post do
@user_mobile_number.register
end
end
put :verify do
current_user.verify(params[:code])
end
end
end
end
в app/services/user_registration_with_mobile_number_service.rb
class UserRegistrationWithMobileNumberService
attr_reader :mobile_number, :token
def initialize(mobile_number)
@mobile_number = mobile_number
end
def register
user = User.find_or_initialize_by mobile_number: mobile_number
if user.save
@token, payload = Warden::JWTAuth::UserEncoder.new.call(user, :user, nil)
user.on_jwt_dispatch(@token, payload)
# TODO: UserRegistrationJob.perform_later(user.id)
end
user
end
end
в spec/api/v1/user_registration_api_spec.rb
frozen_string_literal: true
require ‘rails_helper’
RSpec.describe V1::UserRegistrationApi, ‘/api/v1/user/register’ do
let(:mobile_number) { ‘01234567’ }
контекст ‘с номером телефона’ do
it ‘создает нового пользователя’ do
ожидать do
post ‘/api/v1/user/register’, params: { mobile_number: mobile_number }
end.to change(User, :count).by 1
end
it 'responses the new created user' do
post '/api/v1/user/register', params: { mobile_number: mobile_number }
expect(json_body).to include mobile_number: mobile_number
expect(json_body).to include status: 'pending'
expect(json_body[:code]).to be_present
end
it 'responses with jwt authorization token' do
post '/api/v1/user/register', params: { mobile_number: mobile_number }
expect(jwt_token).to match /(^[w-]*.[w-]*.[w-]*$)/
end
context 'when mobile number is already registered' do
let!(:user) { create(:user, mobile_number: mobile_number)}
it 'responses with jwt token' do
post '/api/v1/user/register', params: { mobile_number: mobile_number }
expect(jwt_token).to match /(^[w-]*.[w-]*.[w-]*$)/
end
end
end
контекст ‘when confirm’ do
before do
post ‘/api/v1/user/register’, params: { mobile_number: mobile_number }
end
context 'with correct code' do
it 'changes status from pending to confirmed' do
put '/api/v1/user/verify', params: { code: json_body[:code] }, headers: { 'Authorization': "Bearer #{jwt_token}" }
expect(json_body(reload: true)[:status]).to eq 'confirmed'
end
end
context 'with wrong code' do
it 'unable to confirm' do
put '/api/v1/user/verify', params: { code: 'wrong-code' }, headers: { 'Authorization': "Bearer #{jwt_token}" }
expect(json_body(reload: true)[:status]).to eq 'pending'
end
end
context 'without authorized jwt token header' do
it 'responses unauthorized' do
put '/api/v1/user/verify', params: { code: json_body[:code] }
expect(response).to be_unauthorized
end
end
end
end