Rails api auth с помощью Grape и Devise JWT

[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

Оцените статью
Procodings.ru
Добавить комментарий