### Fix Setup Config Example in README Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/CHANGELOG.md Corrects the setup configuration example provided in the README file. This ensures users can correctly configure the gem. ```markdown Fix setup config example in README ``` -------------------------------- ### Install Devise Token Auth Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/config/README.md Run the installation generator to set up the gem, specifying the user class and mount path. ```bash rails g devise_token_auth:install [USER_CLASS] [MOUNT_PATH] ``` ```bash rails g devise_token_auth:install_mongoid [USER_CLASS] [MOUNT_PATH] ``` ```bash rails g devise_token_auth:install User auth ``` -------------------------------- ### Install Devise Token Auth Source: https://context7.com/lynndylanhurley/devise_token_auth/llms.txt Add the gem to your Gemfile, install dependencies, and run the generator to set up the User model and database migrations. ```bash # Add to Gemfile gem 'devise_token_auth' # Install dependencies bundle install # Generate User model and configuration rails g devise_token_auth:install User auth # Run migrations rails db:migrate ``` -------------------------------- ### Install Gems Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/installation.md Run this command in your terminal after updating your Gemfile to install the gem and its dependencies. ```bash bundle install ``` -------------------------------- ### Install generator for additional authentication model Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/multiple_models.md Run this command to generate a new authentication model and define its routes. ```bash rails g devise_token_auth:install Admin admin_auth ``` -------------------------------- ### Run Database Migrations Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/config/README.md Execute migrations after the installation generator creates the necessary files for ActiveRecord. ```bash rake db:migrate ``` -------------------------------- ### Configure OmniAuth Providers in Rails Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/config/omniauth.md Set up your OmniAuth providers in `config/initializers/omniauth.rb`. Ensure your API keys and secrets are stored securely, for example, using environment variables. ```ruby # config/initializers/omniauth.rb Rails.application.config.middleware.use OmniAuth::Builder do provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'], scope: 'email,profile' provider :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET'] provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'] provider :apple, ENV['APPLE_CLIENT_ID'], '', { scope: 'email name', team_id: ENV['APPLE_TEAM_ID'], key_id: ENV['APPLE_KEY'], pem: ENV['APPLE_PEM'] } end ``` -------------------------------- ### Add OmniAuth Provider Gems to Gemfile Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/config/omniauth.md Include these gems in your Gemfile to enable authentication with GitHub, Facebook, Google, and Apple via OmniAuth. Run `bundle install` after adding them. ```ruby gem 'omniauth-github' gem 'omniauth-facebook' gem 'omniauth-google-oauth2' gem 'omniauth-apple' ``` -------------------------------- ### Implement RSpec request specs for authentication Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/testing.md Generic RSpec setup for testing authenticated API endpoints, including helper methods for login and header extraction. ```Ruby # spec/requests/authentication_test_spec.rb require 'rails_helper' include ActionController::RespondWith # The authentication header looks something like this: # {"access-token"=>"abcd1dMVlvW2BT67xIAS_A", "token-type"=>"Bearer", "client"=>"LSJEVZ7Pq6DX5LXvOWMq1w", "expiry"=>"1519086891", "uid"=>"darnell@konopelski.info"} describe 'Whether access is ocurring properly', type: :request do before(:each) do @current_user = FactoryBot.create(:user) @client = FactoryBot.create(:client) end context 'context: general authentication via API, ' do it "doesn't give you anything if you don't log in" do get api_client_path(@client) expect(response.status).to eq(401) end it 'gives you an authentication code if you are an existing user and you satisfy the password' do login # puts "#{response.headers.inspect}" # puts "#{response.body.inspect}" expect(response.has_header?('access-token')).to eq(true) end it 'gives you a status 200 on signing in ' do login expect(response.status).to eq(200) end it 'first get a token, then access a restricted page' do login auth_params = get_auth_params_from_login_response_headers(response) new_client = FactoryBot.create(:client) get api_find_client_by_name_path(new_client.name), headers: auth_params expect(response).to have_http_status(:success) end it 'deny access to a restricted page with an incorrect token' do login auth_params = get_auth_params_from_login_response_headers(response).tap do |h| h.each do |k, _v| if k == 'access-token' h[k] = '123' end end end new_client = FactoryBot.create(:client) get api_find_client_by_name_path(new_client.name), headers: auth_params expect(response).not_to have_http_status(:success) end end RSpec.shared_examples 'use authentication tokens of different ages' do |token_age, http_status| let(:vary_authentication_age) { token_age } it 'uses the given parameter' do expect(vary_authentication_age(token_age)).to have_http_status(http_status) end def vary_authentication_age(token_age) login auth_params = get_auth_params_from_login_response_headers(response) new_client = FactoryBot.create(:client) get api_find_client_by_name_path(new_client.name), headers: auth_params expect(response).to have_http_status(:success) allow(Time).to receive(:now).and_return(Time.now + token_age) get api_find_client_by_name_path(new_client.name), headers: auth_params response end end context 'test access tokens of varying ages' do include_examples 'use authentication tokens of different ages', 2.days, :success include_examples 'use authentication tokens of different ages', 5.years, :unauthorized end def login post api_user_session_path, params: { email: @current_user.email, password: 'password' }.to_json, headers: { 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' } end def get_auth_params_from_login_response_headers(response) client = response.headers['client'] token = response.headers['access-token'] expiry = response.headers['expiry'] token_type = response.headers['token-type'] uid = response.headers['uid'] auth_params = { 'access-token' => token, 'client' => client, 'uid' => uid, 'expiry' => expiry, 'token-type' => token_type } auth_params end end ``` -------------------------------- ### Perform Concurrent Batch Requests Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/conceptual.md Example of making multiple simultaneous API calls where token rotation must be suppressed to prevent authentication errors. ```javascript $scope.getResourceData = function() { $http.get('/api/restricted_resource_1').success(function(resp) { // handle response $scope.resource1 = resp.data; }); $http.get('/api/restricted_resource_2').success(function(resp) { // handle response $scope.resource2 = resp.data; }); }; ``` -------------------------------- ### GET /validate_token Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/README.md Validates existing authentication tokens. ```APIDOC ## GET /validate_token ### Description Validates tokens on return visits to the client. ### Method GET ### Endpoint /validate_token ### Parameters #### Query Parameters - **uid** (string) - Required - **client** (string) - Required - **access-token** (string) - Required ``` -------------------------------- ### Limit Access to Authenticated Users in a Controller Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/controller_methods.md Use the `before_action :authenticate_user!` filter to protect controller actions, ensuring only signed-in users can access them. This example demonstrates how to render user-specific information upon successful authentication. ```ruby # app/controllers/test_controller.rb class TestController < ApplicationController before_action :authenticate_user! def members_only render json: { data: { message: "Welcome #{current_user.name}", user: current_user } }, status: 200 end end ``` -------------------------------- ### Configure Initializer Settings Source: https://context7.com/lynndylanhurley/devise_token_auth/llms.txt Customize token behavior, security, and OAuth settings in the initializer file. ```ruby # config/initializers/devise_token_auth.rb DeviseTokenAuth.setup do |config| # Token changes after each request (recommended for security) config.change_headers_on_each_request = true # Token lifespan (users re-authenticate after expiration) config.token_lifespan = 2.weeks # BCrypt cost factor for token hashing (4-31, recommended <= 10) config.token_cost = 10 # Batch request window (concurrent requests share same token) config.batch_request_buffer_throttle = 5.seconds # OmniAuth callback prefix config.omniauth_prefix = '/omniauth' # Default redirect URLs config.default_confirm_success_url = 'https://myapp.com/auth/confirm' config.default_password_reset_url = 'https://myapp.com/auth/reset' # Whitelist allowed redirect URLs (supports wildcards) config.redirect_whitelist = ['https://myapp.com/*', 'https://staging.myapp.com/*'] # Invalidate all tokens when password changes config.remove_tokens_after_password_reset = true # Cookie-based token storage (alternative to headers) config.cookie_enabled = false config.cookie_name = 'auth_cookie' config.cookie_attributes = { httponly: true, secure: true, same_site: 'Lax' } # Validate user on each request (not just login) config.bypass_sign_in = false # Send confirmation email on registration config.send_confirmation_email = true end ``` -------------------------------- ### Initializer Configuration Settings Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/config/initialization.md A reference of all available configuration options for the devise_token_auth initializer. ```APIDOC ## Initializer Configuration ### Description Settings available for configuration in `config/initializers/devise_token_auth.rb` to control authentication behavior, security, and redirects. ### Configuration Options - **change_headers_on_each_request** (boolean) - Default: true. If true, the access-token header changes after each request. - **token_lifespan** (duration) - Default: 2.weeks. Duration until a token expires. - **token_cost** (integer) - Default: 10. BCrypt cost factor (range 4-31). - **batch_request_buffer_throttle** (duration) - Default: 5.seconds. Time window for batch requests to share the same token. - **omniauth_prefix** (string) - Default: "/omniauth". Prefix for OAuth2 redirect callbacks. - **default_confirm_success_url** (string) - Default: nil. Redirect URL after successful email confirmation. - **default_password_reset_url** (string) - Default: nil. Redirect URL after successful password reset. - **redirect_whitelist** (array) - Default: nil. Allowed URLs for redirects after validation. - **enable_standard_devise_support** (boolean) - Default: false. Enables legacy Devise authentication support. - **remove_tokens_after_password_reset** (boolean) - Default: false. If true, invalidates tokens upon password change. - **default_callbacks** (boolean) - Default: true. Includes UserOmniauthCallbacks concern. - **cookie_enabled** (boolean) - Default: false. Enables token transmission via cookies. - **cookie_name** (string) - Default: "auth_cookie". Name of the auth cookie. - **cookie_attributes** (hash) - Default: {}. Attributes for the auth cookie. - **bypass_sign_in** (boolean) - Default: true. If true, skips active_for_authentication? check on every request. - **send_confirmation_email** (boolean) - Default: false. If true, sends confirmation emails when using the confirmable module. ``` -------------------------------- ### POST /sign_in Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/README.md Authenticates a user via email and password. ```APIDOC ## POST /sign_in ### Description Authenticates a user and returns user data along with access tokens in the response headers. ### Method POST ### Endpoint /sign_in ### Request Body - **email** (string) - Required - **password** (string) - Required ``` -------------------------------- ### POST / Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/README.md Registers a new user via email. ```APIDOC ## POST / ### Description Email registration for new users. A verification email is sent to the provided address. ### Method POST ### Endpoint / ### Request Body - **email** (string) - Required - **password** (string) - Required - **password_confirmation** (string) - Required - **confirm_success_url** (string) - Optional ``` -------------------------------- ### Manual Devise Configuration (`devise.rb`) Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/config/initialization.md Allows for manual configuration of various Devise aspects by creating a `config/initializers/devise.rb` file. ```APIDOC ## Manual Devise Configuration ### Description Devise can be further configured by manually creating the `config/initializers/devise.rb` file. This allows for customization of mailers, ORM, and other middleware settings. ### Example Configuration (`config/initializers/devise.rb`) ```ruby Devise.setup do |config| # The e-mail address that mail will appear to be sent from # If absent, mail is sent from "please-change-me-at-config-initializers-devise@example.com" config.mailer_sender = "support@myapp.com" # ==> ORM configuration # Load and configure the ORM. Supports :active_record (default) and # :mongoid (bson_ext recommended) by default. Other ORMs may be # available as additional gems. require 'devise/orm/active_record' # If using rails-api, you may want to tell devise to not use ActionDispatch::Flash # middleware b/c rails-api does not include it. # See: https://stackoverflow.com/q/19600905/806956 config.navigational_formats = [:json] end ``` ### Key Configuration Options: - **`mailer_sender`**: Sets the sender email address for Devise emails. - **`require 'devise/orm/active_record'`**: Specifies the Object-Relational Mapper (ORM) to use (e.g., Active Record). - **`navigational_formats`**: Configures the formats for navigation, particularly useful for API-only applications. ``` -------------------------------- ### Fix Generator for Rails 5 User Model Injection Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/CHANGELOG.md Corrects the generator to properly inject content into the user model when using Rails 5. This ensures correct setup for new projects. ```ruby Fix generator to correctly inject content into the user model in rails 5 ``` -------------------------------- ### Create Authorization Header from Scratch Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/testing.md Uses FactoryBot to create a user and manually generates the token hash required for authentication headers. This is useful for simulating logged-in states in request specs. ```ruby require 'rails_helper' include ActionController::RespondWith def create_auth_header_from_scratch # You need to set up factory bot to use this method @current_user = FactoryBot.create(:user) # create token token = DeviseTokenAuth::TokenFactory.create # store client + token in user's token hash @current_user.tokens[token.client] = { token: token.token_hash, expiry: token.expiry } # Now we have to pretend like an API user has already logged in. # (When the user actually logs in, the server will send the user # - assuming that the user has correctly and successfully logged in # - four auth headers. We are to then use these headers to access # things which are typically restricted # The following assumes that the user has received those headers # and that they are then using those headers to make a request new_auth_header = @current_user.build_auth_headers(token.token, token.client) puts 'This is the new auth header' puts new_auth_header.to_s # update response with the header that will be required by the next request puts response.headers.merge!(new_auth_header).to_s end ``` -------------------------------- ### Configure Controller Options Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/overrides.md Define all available controller options with their default settings in the routes configuration. ```ruby mount_devise_token_auth_for 'User', at: 'auth', controllers: { confirmations: 'devise_token_auth/confirmations', passwords: 'devise_token_auth/passwords', omniauth_callbacks: 'devise_token_auth/omniauth_callbacks', registrations: 'devise_token_auth/registrations', sessions: 'devise_token_auth/sessions', token_validations: 'devise_token_auth/token_validations' } ``` -------------------------------- ### Register User via API Source: https://context7.com/lynndylanhurley/devise_token_auth/llms.txt Perform a POST request to the authentication endpoint to register a new user account. ```bash # Register new user curl -X POST http://localhost:3000/auth \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com", "password": "password123", "password_confirmation": "password123", "confirm_success_url": "https://myapp.com/login" }' # Response (200 OK) { "status": "success", "data": { "id": 1, "email": "user@example.com", "provider": "email", "uid": "user@example.com", "created_at": "2024-01-15T10:30:00.000Z", "updated_at": "2024-01-15T10:30:00.000Z" } } # Response headers (if email confirmation disabled) # access-token: wwwww # token-type: Bearer # client: xxxxx # expiry: 1705312200 # uid: user@example.com ``` -------------------------------- ### Enable standard Devise support Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/faq.md Configure the initializer to allow Devise Token Auth to coexist with standard Devise routes. ```ruby DeviseTokenAuth.setup do |config| config.enable_standard_devise_support = true end ``` -------------------------------- ### POST /auth Source: https://context7.com/lynndylanhurley/devise_token_auth/llms.txt Registers a new user account using email and password credentials. ```APIDOC ## POST /auth ### Description Register a new user account with email and password. Returns user data on success. If email confirmation is enabled, sends confirmation email and returns unconfirmed user data. ### Method POST ### Endpoint /auth ### Request Body - **email** (string) - Required - User email address - **password** (string) - Required - User password - **password_confirmation** (string) - Required - Password confirmation - **confirm_success_url** (string) - Optional - URL to redirect after confirmation ### Request Example { "email": "user@example.com", "password": "password123", "password_confirmation": "password123", "confirm_success_url": "https://myapp.com/login" } ### Response #### Success Response (200) - **status** (string) - Success status - **data** (object) - User object containing id, email, provider, uid, created_at, and updated_at #### Response Example { "status": "success", "data": { "id": 1, "email": "user@example.com", "provider": "email", "uid": "user@example.com", "created_at": "2024-01-15T10:30:00.000Z", "updated_at": "2024-01-15T10:30:00.000Z" } } ``` -------------------------------- ### Configure OmniAuth Full Host for Pow and xip.io Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/config/omniauth.md Set `OmniAuth.config.full_host` in your development environment configuration to resolve `redirect-uri-mismatch` errors when using pow or xip.io. ```ruby # config/environments/development.rb # when using pow OmniAuth.config.full_host = "http://app-name.dev" # when using xip.io OmniAuth.config.full_host = "http://xxx.xxx.xxx.app-name.xip.io" ``` -------------------------------- ### User Sign In API Source: https://context7.com/lynndylanhurley/devise_token_auth/llms.txt Authenticate a user with their email and password. Store the returned authentication headers for subsequent authenticated requests. ```bash # Sign in curl -X POST http://localhost:3000/auth/sign_in \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com", "password": "password123" }' # Response (200 OK) { "data": { "id": 1, "email": "user@example.com", "provider": "email", "uid": "user@example.com", "name": "John Doe" } } # Response headers (store these for authenticated requests) # access-token: BGjBLzQV7FGhFy8Y5X2p0g # token-type: Bearer # client: 4M5b6kPnLLMqNvXfOdE1Jw # expiry: 1705312200 # uid: user@example.com ``` -------------------------------- ### Configure Action Mailer for Authentication Source: https://context7.com/lynndylanhurley/devise_token_auth/llms.txt Set up SMTP settings and default URL options in development and production environments to enable email-based features. ```ruby # config/environments/development.rb Rails.application.configure do config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address: 'localhost', port: 1025 } end # config/environments/production.rb Rails.application.configure do config.action_mailer.default_url_options = { host: 'api.myapp.com', protocol: 'https' } config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address: ENV['SMTP_HOST'], port: ENV['SMTP_PORT'], user_name: ENV['SMTP_USERNAME'], password: ENV['SMTP_PASSWORD'], authentication: :plain, enable_starttls_auto: true } end # config/initializers/devise.rb Devise.setup do |config| config.mailer_sender = 'noreply@myapp.com' config.navigational_formats = [:json] end ``` -------------------------------- ### Create New Authentication Token Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/model_concerns.md Call `create_new_auth_token` to generate a new authentication token with necessary metadata. An optional `client` argument can be provided; otherwise, a new client will be generated. The method returns authentication headers for the client. ```ruby # extract client from auth header client = request.headers['client'] # update token, generate updated auth headers for response new_auth_header = @resource.create_new_auth_token(client) # update response with the header that will be required by the next request response.headers.merge!(new_auth_header) ``` -------------------------------- ### Build Authentication Headers for Response Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/model_concerns.md Use `build_auth_headers` to generate the authentication headers that should be sent to the client for the next request. This method requires `token` and `client` as arguments and returns a string. ```ruby # create token token = DeviseTokenAuth::TokenFactory.create # store client + token in user's token hash @resource.tokens[token.client] = { token: token.token_hash, expiry: token.expiry } # generate auth headers for response new_auth_header = @resource.build_auth_headers(token.token, token.client) # update response with the header that will be required by the next request response.headers.merge!(new_auth_header) ``` -------------------------------- ### Configure Multiple User Models Source: https://context7.com/lynndylanhurley/devise_token_auth/llms.txt Support distinct authentication endpoints for different user types like User and Admin. ```ruby Rails.application.routes.draw do # User authentication at /auth mount_devise_token_auth_for 'User', at: 'auth' # Admin authentication at /admin_auth mount_devise_token_auth_for 'Admin', at: 'admin_auth' # Routes using User authentication namespace :api do resources :posts end # Routes using Admin authentication devise_scope :admin do namespace :admin do resources :users resources :settings end end end ``` ```ruby class Api::PostsController < ApplicationController before_action :authenticate_user! def index render json: current_user.posts end end ``` ```ruby class Admin::UsersController < ApplicationController before_action :authenticate_admin! def index render json: User.all end def show render json: { admin: current_admin, user: User.find(params[:id]) } end end ``` -------------------------------- ### Configure Mailcatcher for Development Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/config/email_auth.md Set up Action Mailer to use SMTP settings for mailcatcher in the development environment. Ensure default URL options are also configured. ```ruby # config/environments/development.rb Rails.application.configure do config.action_mailer.default_url_options = { host: 'your-dev-host.dev' } config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address: 'your-dev-host.dev', port: 1025 } end ``` -------------------------------- ### create_new_auth_token Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/model_concerns.md Creates a new authentication token and returns the necessary headers. ```APIDOC ## create_new_auth_token ### Description Creates a new auth token with necessary metadata. If no client is provided, a new one is generated. ### Parameters - **client** (string) - Optional - The client identifier. ### Response - **Returns** (object) - The authentication headers to be sent to the client. ``` -------------------------------- ### Mount Authentication Routes Source: https://context7.com/lynndylanhurley/devise_token_auth/llms.txt Use the mount_devise_token_auth_for helper in your routes file to define authentication endpoints for your models. ```ruby # config/routes.rb Rails.application.routes.draw do # Mount authentication routes at /auth mount_devise_token_auth_for 'User', at: 'auth' # Custom controller overrides mount_devise_token_auth_for 'User', at: 'auth', controllers: { registrations: 'custom/registrations', sessions: 'custom/sessions', passwords: 'custom/passwords', confirmations: 'custom/confirmations', token_validations: 'custom/token_validations', omniauth_callbacks: 'custom/omniauth_callbacks' } # Multiple user models mount_devise_token_auth_for 'Admin', at: 'admin_auth' end ``` -------------------------------- ### Generate Email Templates Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/overrides.md Run the generator command to copy default email templates into the application for customization. ```bash rails generate devise_token_auth:install_views ``` -------------------------------- ### Allow Authentication via Headers or POST Request Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/CHANGELOG.md Enables authentication requests to be made using either headers or a POST request. This provides flexibility in how clients authenticate. ```ruby allowing authenticating using headers as well as a post request ``` -------------------------------- ### Configure routes for standard Devise and Token Auth Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/faq.md Define routes to support both standard Devise and Devise Token Auth, ensuring standard routes are declared first. ```ruby Rails.application.routes.draw do # standard devise routes available at /users # NOTE: make sure this comes first!!! devise_for :users # token auth routes available at /api/v1/auth namespace :api do scope :v1 do mount_devise_token_auth_for 'User', at: 'auth' end end end ``` -------------------------------- ### Configure User Model for Token Auth Source: https://context7.com/lynndylanhurley/devise_token_auth/llms.txt Include the User concern to enable token management and customize the token validation response. ```ruby # app/models/user.rb class User < ApplicationRecord # Include devise modules devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :confirmable, :omniauthable # Include devise_token_auth concern include DeviseTokenAuth::Concerns::User # Custom validations validates :name, presence: true, length: { maximum: 50 } # Token validation response (customize returned user data) def token_validation_response as_json(only: [:id, :email, :name, :created_at]) end end # Using model methods directly user = User.find_by(email: 'user@example.com') # Check if token is valid is_valid = user.valid_token?(token, client) # Create new authentication token auth_headers = user.create_new_auth_token(client) # => {"access-token"=>"...", "token-type"=>"Bearer", "client"=>"...", "expiry"=>"...", "uid"=>"..."} # Build auth headers for response token = DeviseTokenAuth::TokenFactory.create user.tokens[token.client] = { token: token.token_hash, expiry: token.expiry } user.save! headers = user.build_auth_headers(token.token, token.client) ``` -------------------------------- ### User Sign In API Source: https://context7.com/lynndylanhurley/devise_token_auth/llms.txt Authenticate a user with their email and password. Upon successful authentication, the API returns user data and essential authentication headers (access-token, token-type, client, expiry, uid) that must be stored and included in subsequent authenticated requests. ```APIDOC ## POST /auth/sign_in ### Description Authenticate user with email and password. Returns user data and authentication headers required for subsequent requests. Store these headers and include them in all authenticated API calls. ### Method POST ### Endpoint /auth/sign_in ### Request Body - **email** (string) - Required - The user's email address. - **password** (string) - Required - The user's password. ### Request Example ```json { "email": "user@example.com", "password": "password123" } ``` ### Response #### Success Response (200 OK) - **data** (object) - Contains user information including id, email, provider, uid, and name. #### Response Example ```json { "data": { "id": 1, "email": "user@example.com", "provider": "email", "uid": "user@example.com", "name": "John Doe" } } ``` ### Response Headers (Store for authenticated requests) - **access-token** (string) - **token-type** (string) - **client** (string) - **expiry** (string) - **uid** (string) ``` -------------------------------- ### Authorize requests with auth tokens Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/testing.md Use resource.create_new_auth_token to generate the necessary headers for authenticated API requests. ```Ruby request.headers.merge! resource.create_new_auth_token get '/api/authenticated_resource' # success ``` -------------------------------- ### Generate and Customize Email Templates Source: https://context7.com/lynndylanhurley/devise_token_auth/llms.txt Run the generator to copy Devise views into your application, then modify the ERB files to customize email content. ```bash # Generate email template files rails generate devise_token_auth:install_views ``` ```erb

Hello <%= @resource.email %>!

Someone has requested a link to change your password. You can do this through the link below.

<%= link_to 'Change my password', edit_user_password_url( reset_password_token: @token, redirect_url: message['redirect-url'].to_s ) %>

If you didn't request this, please ignore this email.

Your password won't change until you access the link above and create a new one.

Welcome <%= @resource.email %>!

You can confirm your account email through the link below:

<%= link_to 'Confirm my account', user_confirmation_url( confirmation_token: @token, redirect_url: message['redirect-url'].to_s ) %>

``` -------------------------------- ### Add Gemfile Dependency Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/installation.md Add this line to your Gemfile to include the devise_token_auth gem. ```ruby gem 'devise_token_auth' ``` -------------------------------- ### Update Config Wrapper Idiom Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/CHANGELOG.md Updates the configuration wrapper to conform with newer idiomatic practices. This improves code maintainability and readability. ```ruby updates config wrapper to conform with newer idiom ``` -------------------------------- ### build_auth_headers Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/model_concerns.md Generates the authentication headers for the next request. ```APIDOC ## build_auth_headers ### Description Generates the auth header that should be sent to the client with the next request. ### Parameters - **token** (string) - Required - The token string. - **client** (string) - Required - The client identifier. ### Response - **Returns** (string) - The formatted authentication header. ``` -------------------------------- ### Configure CORS for Authentication Headers Source: https://context7.com/lynndylanhurley/devise_token_auth/llms.txt Ensure authentication headers are exposed to the frontend by configuring rack-cors. ```ruby # Gemfile gem 'rack-cors' # config/application.rb module MyApp class Application < Rails::Application config.middleware.use Rack::Cors do allow do # Production: whitelist specific origins origins 'https://myapp.com', 'https://staging.myapp.com' # Development: allow all origins (dangerous in production!) # origins '*' resource '*', headers: :any, # CRITICAL: expose auth headers so frontend can read them expose: ['access-token', 'expiry', 'token-type', 'uid', 'client'], methods: [:get, :post, :put, :patch, :delete, :options, :head], credentials: true end end end end ``` -------------------------------- ### Configure OmniAuth Providers Source: https://context7.com/lynndylanhurley/devise_token_auth/llms.txt Set up third-party OAuth2 providers in the Rails middleware stack. ```ruby # Gemfile gem 'omniauth-google-oauth2' gem 'omniauth-facebook' gem 'omniauth-github' # config/initializers/omniauth.rb Rails.application.config.middleware.use OmniAuth::Builder do provider :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'], scope: 'email,profile' provider :facebook, ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_APP_SECRET'], scope: 'email,public_profile' provider :github, ENV['GITHUB_CLIENT_ID'], ENV['GITHUB_CLIENT_SECRET'], scope: 'user:email' end # Client-side OAuth flow # 1. Redirect user to: GET /auth/google_oauth2?auth_origin_url=https://myapp.com/oauth-callback # 2. User authenticates with provider # 3. User redirected back with auth params in URL # 4. Frontend extracts tokens from URL params ``` -------------------------------- ### POST /password Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/README.md Initiates a password reset request. ```APIDOC ## POST /password ### Description Sends a password reset confirmation email to the user. ### Method POST ### Endpoint /password ### Request Body - **email** (string) - Required - **redirect_url** (string) - Required ``` -------------------------------- ### Extend Controller Behavior with Blocks Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/overrides.md Add logic to existing controller actions by inheriting from the base controller and passing a block to super. ```ruby class Custom::RegistrationsController < DeviseTokenAuth::RegistrationsController def create super do |resource| resource.do_something(extra) end end end ``` -------------------------------- ### Mounting Devise Token Auth Routes Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/routes.md The `mount_devise_token_auth_for` method is used in `config/routes.rb` to add the necessary authentication routes. It accepts a model class name and an options hash for configuration. ```APIDOC ## Mounting Routes The authentication routes must be mounted to your project. This gem includes a route helper for this purpose: **`mount_devise_token_auth_for`** - similar to `devise_for`, this method is used to append the routes necessary for user authentication. This method accepts the following arguments: ### Arguments - **`class_name`** (string) - Default: 'User' - The name of the class to use for authentication. This class must include the [`DeviseTokenAuth::Concerns::User`](model_concerns.md) concern. - **`options`** (object) - Default: `{at: 'auth'}` - The routes to be used for authentication will be prefixed by the path specified in the `at` param of this object. ### Example ```ruby # config/routes.rb mount_devise_token_auth_for 'User', at: 'auth' ``` Any model class can be used, but the class will need to include [`DeviseTokenAuth::Concerns::User`](model_concerns.md) for authentication to work properly. You can mount this engine to any route that you like. `/auth` is used by default to conform with the defaults of the [ng-token-auth](https://github.com/lynndylanhurley/ng-token-auth) module and the [jToker](https://github.com/lynndylanhurley/j-toker) plugin. ``` -------------------------------- ### Configure rack-cors for Rails Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/config/cors.md Add the rack-cors gem to your Gemfile and configure the middleware in application.rb. Ensure the :expose parameter includes the required authentication headers to prevent 401 errors. ```ruby # gemfile gem 'rack-cors', :require => 'rack/cors' # config/application.rb module YourApp class Application < Rails::Application config.middleware.use Rack::Cors do allow do origins '*' resource '*', headers: :any, expose: ['access-token', 'expiry', 'token-type', 'uid', 'client'], methods: [:get, :post, :options, :delete, :put] end end end end ``` -------------------------------- ### API Request for Password Reset Initiation Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/reset_password.md Initiates the password reset process by sending the user's email to the API. Ensure the `redirect_url` parameter points to a frontend page that can handle password confirmation. ```HTTP POST /auth/password Parameters: email: (the email supplied in the field) redirect_url: (a page in the front end site that will contain a form with password and password_confirmation fields) ``` -------------------------------- ### Add Support for Devise 4.1.1 Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/CHANGELOG.md Introduces support for Devise version 4.1.1. This allows users to upgrade Devise without breaking token authentication. ```ruby Adding support for devise 4.1.1 ``` -------------------------------- ### Configure Devise Navigational Formats for API Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/config/initialization.md Configure Devise to not use ActionDispatch::Flash middleware when using rails-api, as it does not include it. This is useful for API-only applications. ```ruby # If using rails-api, you may want to tell devise to not use ActionDispatch::Flash # middleware b/c rails-api does not include it. # See: https://stackoverflow.com/q/19600905/806956 config.navigational_formats = [:json] ``` -------------------------------- ### Mirror Auth Header Keys in Build Auth URL Query Params Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/CHANGELOG.md This code ensures that authentication header keys are mirrored in the query parameters when building the authentication URL. This is useful for certain OAuth flows or API integrations. ```ruby auth_header_keys.each do |key| query_params[key] = request.headers[key] end ``` -------------------------------- ### Configure routes for multiple authentication models Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/multiple_models.md Define multiple devise mappings and use devise_scope to isolate routes for secondary models. ```ruby Rails.application.routes.draw do # when using multiple models, controllers will default to the first available # devise mapping. routes for subsequent devise mappings will need to defined # within a `devise_scope` block # define :users as the first devise mapping: mount_devise_token_auth_for 'User', at: 'auth' # define :admins as the second devise mapping. routes using this class will # need to be defined within a devise_scope as shown below mount_devise_token_auth_for "Admin", at: 'admin_auth' # this route will authorize requests using the User class get 'demo/members_only', to: 'demo#members_only' # routes within this block will authorize requests using the Admin class devise_scope :admin do get 'demo/admins_only', to: 'demo#admins_only' end end ``` -------------------------------- ### Set User By Token for Single Table Inheritance Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/CHANGELOG.md This snippet contemplates the use of single table inheritance (STI) within the `set_user_by_token` method. It aims to correctly identify and set the user based on token authentication, even when using STI. ```ruby def set_user_by_token(request) # ... other logic ... if token_info && token_info[:client_id] # Attempt to find user by token and client_id @resource = resource_class.find_by(tokens: { token_info[:client_id] => token_info[:token] }) end # ... other logic ... end ``` -------------------------------- ### Configure Custom Header Names Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/CHANGELOG.md Allows customization of header names used by devise_token_auth. This is useful for integrating with systems that use non-standard header names. ```ruby now possible to change headers names in the config file ``` -------------------------------- ### Update README for Header Names Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/CHANGELOG.md This change updates the README to reflect current header names used for authentication. It's important for clients to use the correct headers. ```markdown Update readme for headers names ``` -------------------------------- ### Password Reset Token Configuration Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/config/initialization.md Controls whether the password-reset confirmation link includes valid session credentials or a password reset token. ```APIDOC ## `require_client_password_reset_token` Option ### Description By default, the password-reset confirmation link redirects to the client with valid session credentials as querystring params. With this option enabled, the redirect will NOT include the valid session credentials. Instead the redirect will include a `password_reset_token` querystring param that can be used to reset the users password. Once the user has reset their password, the password-reset success response headers will contain valid session credentials. ### Configuration This is a boolean option that can be set to `true` or `false`. - **`require_client_password_reset_token`** (boolean) - Default: `false` ``` -------------------------------- ### Test API Authentication with RSpec Source: https://context7.com/lynndylanhurley/devise_token_auth/llms.txt Use these request specs to verify token-based authentication, header management, and sign-in functionality. ```ruby # spec/requests/api/posts_spec.rb require 'rails_helper' RSpec.describe 'Posts API', type: :request do let(:user) { create(:user) } let(:auth_headers) { auth_headers_for(user) } describe 'GET /api/posts' do it 'returns unauthorized without auth headers' do get '/api/posts' expect(response).to have_http_status(:unauthorized) end it 'returns posts with valid auth headers' do create_list(:post, 3, user: user) get '/api/posts', headers: auth_headers expect(response).to have_http_status(:success) expect(JSON.parse(response.body).length).to eq(3) end it 'returns new auth token in response headers' do get '/api/posts', headers: auth_headers expect(response.headers['access-token']).to be_present expect(response.headers['access-token']).not_to eq(auth_headers['access-token']) end end describe 'POST /auth/sign_in' do it 'returns auth headers on successful login' do post '/auth/sign_in', params: { email: user.email, password: 'password' } expect(response).to have_http_status(:success) expect(response.headers['access-token']).to be_present expect(response.headers['client']).to be_present expect(response.headers['uid']).to eq(user.email) end end end ``` -------------------------------- ### Resend Confirmation Email via API Source: https://context7.com/lynndylanhurley/devise_token_auth/llms.txt Use this endpoint to trigger a new confirmation email for unconfirmed accounts. ```bash # Resend confirmation email curl -X POST http://localhost:3000/auth/confirmation \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com", "redirect_url": "https://myapp.com/login" }' # Response (200 OK) { "success": true, "message": "An email has been sent to 'user@example.com' containing instructions for confirming your account." } ``` -------------------------------- ### Implement separate controller hierarchies Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/faq.md Use distinct base controllers for API and full-stack sections to avoid conflicts between Devise Token Auth and standard Devise. ```ruby module Api module V1 class ApplicationController < ::ApplicationController skip_before_action :verify_authenticity_token include DeviseTokenAuth::Concerns::SetUserByToken end end end ``` ```ruby module Admin class ApplicationController < ::ApplicationController before_action :authenticate_admin! end end ``` ```ruby class ApplicationController < ActionController::Base protect_from_forgery with: :exception end ``` ```ruby # config.enable_standard_devise_support = false ``` -------------------------------- ### Override RegistrationsController to permit custom parameters Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/faq.md Extend the RegistrationsController to allow additional parameters during sign up and account updates by overriding configure_permitted_parameters. ```ruby class RegistrationsController < DeviseTokenAuth::RegistrationsController before_action :configure_permitted_parameters protected def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: %i(name)) devise_parameter_sanitizer.permit(:account_update, keys: %i(name)) end end ``` -------------------------------- ### Test Authenticated Endpoints with RSpec Source: https://context7.com/lynndylanhurley/devise_token_auth/llms.txt Create helper methods to generate valid authentication headers for request specs. ```ruby module AuthHelpers def auth_headers_for(user) user.create_new_auth_token end def sign_in_user(user) post '/auth/sign_in', params: { email: user.email, password: 'password' } { 'access-token' => response.headers['access-token'], 'client' => response.headers['client'], 'uid' => response.headers['uid'], 'expiry' => response.headers['expiry'], 'token-type' => response.headers['token-type'] } end end RSpec.configure do |config| config.include AuthHelpers, type: :request end ``` -------------------------------- ### Include SetUserByToken Concern in ApplicationController Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/usage/controller_methods.md Include this concern in your base ApplicationController to make controller methods like `authenticate_user!` and `user_signed_in?` available to all controllers. It also includes an after_action to update the auth token. ```ruby # app/controllers/application_controller.rb class ApplicationController < ActionController::Base include DeviseTokenAuth::Concerns::SetUserByToken end ``` -------------------------------- ### Isolate controllers for ActiveAdmin compatibility Source: https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/faq.md Create separate base controllers to prevent Devise Token Auth concerns from interfering with ActiveAdmin. ```ruby # app/controllers/api_controller.rb # API routes extend from this controller class ApiController < ActionController::Base include DeviseTokenAuth::Concerns::SetUserByToken end # app/controllers/application_controller.rb # leave this for ActiveAdmin, and any other non-api routes class ApplicationController < ActionController::Base end ```