<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Josh Chen</title>
    <description>The latest articles on DEV Community by Josh Chen (@montekaka).</description>
    <link>https://dev.to/montekaka</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F183093%2Febe512a3-341b-43cb-a3a9-967eeb9dda0c.jpeg</url>
      <title>DEV Community: Josh Chen</title>
      <link>https://dev.to/montekaka</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/montekaka"/>
    <language>en</language>
    <item>
      <title>How to do Twitter authentication with devise_token_auth</title>
      <dc:creator>Josh Chen</dc:creator>
      <pubDate>Thu, 16 Dec 2021 07:09:15 +0000</pubDate>
      <link>https://dev.to/montekaka/how-to-do-twitter-authentication-with-devisetokenauth-3l69</link>
      <guid>https://dev.to/montekaka/how-to-do-twitter-authentication-with-devisetokenauth-3l69</guid>
      <description>&lt;h2&gt;
  
  
  Acknowledgement
&lt;/h2&gt;

&lt;p&gt;I would like to express my sincere gratitude to &lt;a class="mentioned-user" href="https://dev.to/risafj"&gt;@risafj&lt;/a&gt; for writing the &lt;a href="https://dev.to/risafj/guide-to-devisetokenauth-simple-authentication-in-rails-api-pfj"&gt;Guide to devise_token_auth: Simple Authentication in Rails API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have implemented an authentication system for my Rails API by following &lt;a class="mentioned-user" href="https://dev.to/risafj"&gt;@risafj&lt;/a&gt;'s guide, and I would like to add Sign in with Twitter on top of it.  I can't find any good tutorial on how to add Twitter authentication using Omniauth (gem) with devise_token_auth.  So instead, I decided to make up (&lt;strong&gt;hack&lt;/strong&gt;) my own solution.&lt;/p&gt;

&lt;p&gt;Note: Guide is for Linux or MacOS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.loom.com/share/054fa5f058934ba1947f61a7e53cc06b" rel="noopener noreferrer"&gt;Demo video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fezpwqrblqrcf1hgrxt3a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fezpwqrblqrcf1hgrxt3a.png" alt="Sign in with Twitter"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  User workflow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;You click the "Sign in with Twitter" button&lt;/li&gt;
&lt;li&gt;You get redirected to a consent screen&lt;/li&gt;
&lt;li&gt;You click "Authenticate app"&lt;/li&gt;
&lt;li&gt;The page redirects to the actual application&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Authentication workflow
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs0706e6xqx2licxyw4fw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs0706e6xqx2licxyw4fw.png" alt="Twitter authentication process"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a new Twitter application
&lt;/h2&gt;

&lt;p&gt;To make it possible for us to add Twitter authentication to our website, we need to register a new Twitter application on Twitter.  This is a necessary step for getting the key and secret code for all communication between our application and Twitter.  This is necessary for our application to confirm our identify to Twitter.&lt;/p&gt;

&lt;p&gt;We can create a new Twitter application &lt;a href="https://developer.twitter.com/en/portal/dashboard" rel="noopener noreferrer"&gt;here&lt;/a&gt; by clicking Create Project button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhu4s3ylpg4u4lujot73h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhu4s3ylpg4u4lujot73h.png" alt="Twitter developer portal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we need to enter our application details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcyibeqbrrdg6qhdrcjl1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcyibeqbrrdg6qhdrcjl1.png" alt="Application detail"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After we have registered our application, we will get a screen where we can find the key and the secret of our Twitter application, the tab Keys and Access Tokens. Later we will need to use these keys, so save them in your computer for now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4tg39eghcahevgcskzmr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4tg39eghcahevgcskzmr.png" alt="App Keys"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once this is done, you can use the left side menu bar to navigate to your project to config User authentication settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzo1xiaumiydes0qmy5mz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzo1xiaumiydes0qmy5mz.png" alt="App settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pick the v1 API&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frr21lmomds70a7o9grnn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frr21lmomds70a7o9grnn.png" alt="Twitter API v1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the bottom of the page we can find the Callback URI / Redirect URL, and Website URL.  Fill them up and press the Save button to update the settings.  Our Twitter application is now ready.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3gi28098al85bwmn9o8i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3gi28098al85bwmn9o8i.png" alt="Callback URL"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open your Rails project, and create a &lt;strong&gt;.env&lt;/strong&gt; file in the project root (if you don't have one yet).  Add the Twitter keys you obtain earlier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .env

TWITTER_API_KEY=KEY FORM TWITTER
TWITTER_API_SECRET=KEY FORM TWITTER
TWITTER_API_BEARER_TOKEN=KEY FORM TWITTER
TWITTER_API_CALLBACK=http://localhost:4200/twitter_login_oauth_callback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The TWITTER_API_CALLBACK is pointing to your client app, and need to match with the one we submitted to Twitter earlier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Twitter OAuth helper file
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;twitter.rb&lt;/code&gt; file inside &lt;code&gt;app/services/Oauth/&lt;/code&gt; and copy and paste the code below to the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app/services/Oauth/twitter.rb
require 'net/http'

module Oauth
  class Twitter
    def initialize(params)
      @callback = params[:callback]
      @app_key = params[:app_key]
      @app_secret = params[:app_secret]
    end


    def get_redirect
      # our front end will go to the callback url
      # and user will need to login from there

      # e.g.
      # https://api.twitter.com/oauth/authenticate?oauth_token=Bviz-wAAAAAAiEDZAAABdOLQn-s
      tokens = get_request_token
      oauth_token = tokens["oauth_token"]
      oauth_token_secret = tokens["oauth_token_secret"]

      callback_url = "https://api.twitter.com/oauth/authenticate?oauth_token=#{oauth_token}"

      return {
        "oauth_token": oauth_token,
        "url": callback_url,
        "oauth_token_secret": oauth_token_secret
      }

    end 

    def obtain_access_token(oauth_token, oauth_token_secret, oauth_verifier)
      tokens = get_access_token(oauth_token, oauth_token_secret, oauth_verifier)
    end

    private

    def get_access_token(oauth_token, oauth_token_secret, oauth_verifier)
      method = 'POST'
      uri = "https://api.twitter.com/oauth/access_token"
      url = URI(uri)
      oauth_timestamp = Time.now.getutc.to_i.to_s
      oauth_nonce = generate_nonce

      oauth_params = {
        'oauth_consumer_key' =&amp;gt; @app_key, # Your consumer key
        'oauth_nonce' =&amp;gt; oauth_nonce, # A random string, see below for function
        'oauth_signature_method' =&amp;gt; 'HMAC-SHA1', # How you'll be signing (see later)
        'oauth_timestamp' =&amp;gt; oauth_timestamp, # Timestamp
        'oauth_version' =&amp;gt; '1.0', # oAuth version
        'oauth_verifier' =&amp;gt; oauth_verifier,
        'oauth_token' =&amp;gt; oauth_token
      }

      oauth_params['oauth_callback'] = url_encode(@callback+"\n")
      oauth_callback = oauth_params['oauth_callback']

      base_string = signature_base_string(method, uri, oauth_params)      
      oauth_signature = url_encode(sign(@app_secret + '&amp;amp;', base_string))

      authorization = "OAuth oauth_callback=\"#{oauth_callback}\", oauth_consumer_key=\"#{@app_key}\", oauth_nonce=\"#{oauth_nonce}\", oauth_signature=\"#{oauth_signature}\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"#{oauth_timestamp}\", oauth_token=\"#{oauth_token}\", oauth_verifier=\"#{oauth_verifier}\", oauth_version=\"1.0\""
      # authorization = 'OAuth oauth_callback="http%3A%2F%2Flocalhost%3A9000%2Ftwitter_connection%0A", oauth_consumer_key="QJImAUogu5MUalOP2Tv5jRt3X", oauth_nonce="a9900fe68e2573b27a37f10fbad6a755", oauth_signature="Y6y8dg4ENFXorvDPu7kyjrdbVYI%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1601796648", oauth_token="NPASDwAAAAAAiEDZAAABdOzo3sU", oauth_verifier="KiPMEx5rkceLjH1sCV3LfIVsxko0sBrc%0A", oauth_version="1.0"'

      http = Net::HTTP.new(url.host, url.port)
      http.use_ssl = true
      http.verify_mode = OpenSSL::SSL::VERIFY_NONE
      request = Net::HTTP::Post.new(url)
      request["authorization"] = authorization

      response = http.request(request)

      parse_response_body(response)
    end

    def get_request_token
      # https://wiki.openstreetmap.org/wiki/OAuth_ruby_examples
      # http://www.drcoen.com/2011/12/oauth-1-0-in-ruby-without-a-gem/
      # http://www.drcoen.com/2011/12/oauth-with-the-twitter-api-in-ruby-on-rails-without-a-gem/

      method = 'POST'
      uri = "https://api.twitter.com/oauth/request_token"
      url = URI(uri)
      oauth_timestamp = Time.now.getutc.to_i.to_s
      oauth_nonce = generate_nonce

      oauth_params = {
        'oauth_consumer_key' =&amp;gt; @app_key, # Your consumer key
        'oauth_nonce' =&amp;gt; oauth_nonce, # A random string, see below for function
        'oauth_signature_method' =&amp;gt; 'HMAC-SHA1', # How you'll be signing (see later)
        'oauth_timestamp' =&amp;gt; oauth_timestamp, # Timestamp
        'oauth_version' =&amp;gt; '1.0' # oAuth version
      }

      oauth_params['oauth_callback'] = url_encode(@callback+"\n")

      base_string = signature_base_string(method, uri, oauth_params)
      oauth_signature = url_encode(sign(@app_secret + '&amp;amp;', base_string))

      oauth_callback = oauth_params['oauth_callback']

      authorization = "OAuth oauth_callback=\"#{oauth_callback}\", oauth_consumer_key=\"#{@app_key}\", oauth_nonce=\"#{oauth_nonce}\", oauth_signature=\"#{oauth_signature}\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"#{oauth_timestamp}\", oauth_version=\"1.0\""
      puts authorization

      http = Net::HTTP.new(url.host, url.port)
      http.use_ssl = true
      http.verify_mode = OpenSSL::SSL::VERIFY_NONE
      request = Net::HTTP::Post.new(url)
      request["authorization"] = authorization
      response = http.request(request)

      parse_response_body(response)

    end

    def url_encode(string)
      CGI::escape(string)
    end

    def signature_base_string(method, uri, params)
      encoded_params = params.sort.collect{ |k, v| url_encode("#{k}=#{v}") }.join('%26')
      method + '&amp;amp;' + url_encode(uri) + '&amp;amp;' + encoded_params
    end

    def sign(key, base_string)
      # digest = OpenSSL::Digest::Digest.new('sha1')
      digest = OpenSSL::Digest::SHA1.new
      hmac = OpenSSL::HMAC.digest(digest, key, base_string)
      Base64.encode64(hmac).chomp.gsub(/\n/, '')
    end

    def generate_nonce(size=7)
      Base64.encode64(OpenSSL::Random.random_bytes(size)).gsub(/\W/, '')
    end  

    def parse_response_body(response)
      ret = {}
      body = response.read_body
      body.split('&amp;amp;').each do |pair|
        key_and_val = pair.split('=')
        ret[key_and_val[0]] = key_and_val[1]
      end

      ret
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  OAuth step-by-step
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1. User clicks the "Sign in with Twitter" button in the client app&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2. Redirect to the Twitter consent screen to authenticate our app.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In my case, clicking the button calls the &lt;code&gt;oauth_sessions_controller&lt;/code&gt;'s &lt;code&gt;get_login_link&lt;/code&gt; method, which then redirect the page.&lt;/p&gt;

&lt;p&gt;Create a new file &lt;code&gt;oauth_sessions_controller.rb&lt;/code&gt;, and put it under your &lt;code&gt;controllers/api/v1&lt;/code&gt; directory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# controllers/api/v1/oauth_sessions_controller.rb

module Api::V1
  class OauthSessionsController &amp;lt; ApplicationController

    # GET /v1/twitter_sign_in_link
    def get_login_link
      redirect_to OauthSession.get_sign_in_redirect_link
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a line in your &lt;code&gt;config/routes.rb&lt;/code&gt; file to setup the &lt;code&gt;/v1/twitter_sign_in_link endpoint&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# config/routes.rb

scope module: 'api' do
  namespace :v1 do
    ...

     get 'twitter_sign_in_link' =&amp;gt; 'oauth_sessions#get_login_link'

    ...
  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might ask.  What is &lt;code&gt;OauthSession.get_sign_in_redirect_link&lt;/code&gt;?  This is going to return us a redirect URL to the Twitter consent screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7x2q39i67o0b5evmlak3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7x2q39i67o0b5evmlak3.png" alt="Consent screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  OauthSession
&lt;/h3&gt;

&lt;p&gt;To implement the &lt;code&gt;get_sign_in_redirect_link&lt;/code&gt;, we will first generate the &lt;code&gt;OauthSession&lt;/code&gt; model.&lt;/p&gt;

&lt;p&gt;Execute this from your command line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails g model OauthSession
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will do many things, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a &lt;code&gt;OauthSession&lt;/code&gt; model, which stores information such as &lt;code&gt;oauth_token&lt;/code&gt;, and &lt;code&gt;oauth_token_secret&lt;/code&gt;, and a corresponding migration file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add the following code to the newly generated migration file, which looks something like &lt;code&gt;YYYYMMDDTTTT_create_oauth_sessions.rb&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class CreateOauthSessions &amp;lt; ActiveRecord::Migration[6.1]
  def change
    create_table :oauth_sessions do |t|
      t.string :provider
      t.string :oauth_token
      t.string :oauth_token_secret

      t.timestamps
    end
  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Migrate database by running &lt;code&gt;rails db:migrate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Implement the &lt;code&gt;get_sign_in_redirect_link&lt;/code&gt; class method to OauthSession.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# models/oauth_session.rb

class OauthSession &amp;lt; ApplicationRecord
  def self.get_sign_in_redirect_link
    params = {callback: ENV['TWITTER_API_CALLBACK'], app_key: ENV['TWITTER_API_KEY'], app_secret: ENV['TWITTER_API_SECRET']}
    twitter_service = Oauth::Twitter.new(params)
    result = twitter_service.get_redirect // {oauth_token, oauth_token_secret, url}


    oauth_token = result[:oauth_token]    
    oauth_token_secret = result[:oauth_token_secret]

    # Save the token and secret to the table, so that we can use it later
    twitter_auth_session = OauthSession.where("oauth_token = ? and provider = ?", oauth_token, "Twitter").first
    unless twitter_auth_session
      twitter_auth_session = OauthSession.new()
    end

    twitter_auth_session.oauth_token = oauth_token
    twitter_auth_session.oauth_token_secret = oauth_token_secret
    twitter_auth_session.provider = "Twitter"
    twitter_auth_session.save

    return result[:url]
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick explanation of the function above:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make an API to request to Twitter to get oauth_token, oauth_token_secret and url to consent screen.&lt;/li&gt;
&lt;li&gt;Save the oauth_token, and oauth_token_secret to the table. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 3. The user clicks "Authenticate app" on the consent screen&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4. The page redirects to your &lt;code&gt;callback_uri&lt;/code&gt; with &lt;code&gt;oauth_token&lt;/code&gt; and &lt;code&gt;oauth_verifier&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Example URL to redirect to your &lt;code&gt;callback_uri&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http://localhost:4200/twitter_login_oauth_callback?oauth_token=NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0&amp;amp;oauth_verifier=uw7NjWHT6OJ1MpJOXsHfNxoAhPKpgI8BlYDhxEjIBY&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Our client app will need to make an HTTP POST request to our Rails API endpoint (&lt;code&gt;/v1/twitter_sign_in&lt;/code&gt;) to pass &lt;code&gt;oauth_token&lt;/code&gt; and &lt;code&gt;oauth_verifier&lt;/code&gt; back to our backend server.&lt;/p&gt;

&lt;p&gt;Add a line in your &lt;code&gt;config/routes.rb&lt;/code&gt; file to setup the &lt;code&gt;/v1/twitter_sign_in endpoint&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# config/routes.rb

scope module: 'api' do
  namespace :v1 do
    ...

     post 'twitter_sign_in' =&amp;gt; 'twitter_auths#sign_in'

    ...
  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;twitter_oauths_controller.rb&lt;/code&gt; and save to &lt;code&gt;/controllers/api/v1/&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# twitter_oauths_controller.rb

module Api::V1
  class TwitterOauthsController &amp;lt; ApplicationController

    # POST /v1/twitter_sign_in
    def sign_in
      # converting the request token to access token
      oauth_token = params[:oauth_token]
      oauth_verifier = params[:oauth_verifier]

      if oauth_token &amp;amp;&amp;amp; oauth_verifier
        user = TwitterAuth.sign_in(oauth_token, oauth_verifier)
        if user
          header = user.create_new_auth_token
          response.set_header('access-token', header["access-token"]) 
          response.set_header('token-type', header["token-type"]) 
          response.set_header('client', header["client"]) 
          response.set_header('expiry', header["expiry"]) 
          response.set_header('uid', header["uid"]) 

          render json: {data: user}, status: :ok
        else
          res = {
            "success": false,
            "errors": [
              "Invalid login credentials. Please try again."
            ]
          }
          render json: res, status: :unauthorized         
        end
      else
        res = {
          "success": false,
          "errors": [
            "Failed to sign in with Twitter"
          ]
        }         
        render json: res, status: :unauthorized
      end

    end

  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick explanation of the code above:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If &lt;code&gt;oauth_token&lt;/code&gt; and &lt;code&gt;oauth_verifier&lt;/code&gt; are available, then we will try to use them to sign in to our application.
&lt;/li&gt;
&lt;li&gt;If the &lt;code&gt;user&lt;/code&gt; is available, then use &lt;code&gt;create_new_auth_token&lt;/code&gt; method from &lt;code&gt;devise_token_auth&lt;/code&gt; to generate the header meta&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 5. Exchange the oauth token for an user&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To implement the &lt;code&gt;TwitterAuth.sign_in(oauth_token, oauth_verifier)&lt;/code&gt;, we will first generate the &lt;code&gt;TwitterAuth&lt;/code&gt; model.&lt;/p&gt;

&lt;p&gt;Execute this from your command line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails g model TwitterAuth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following code to the newly generated migration file, which looks something like &lt;code&gt;YYYYMMDDTTTT_create_twitter_auths.rb&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class CreateTwitterAuths &amp;lt; ActiveRecord::Migration[6.1]
  def change
    create_table :twitter_auths do |t|
      t.string :twitter_user_id
      t.string :screen_name

      t.references :user, foreign_key: true
      t.timestamps
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the user has one twitter_auth relationship.  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the &lt;code&gt;user.rb&lt;/code&gt;, add &lt;code&gt;has_one :twitter_auth&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In the &lt;code&gt;twitter_auth.rb&lt;/code&gt;, add &lt;code&gt;belongs_to :user&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Migrate database by running &lt;code&gt;rails db:migrate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Implement the &lt;code&gt;sign_in&lt;/code&gt; class method to TwitterAuth.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# /models/twitter_auth.rb

class TwitterAuth &amp;lt; ApplicationRecord
  belongs_to :user


  def self.sign_in(oauth_token, oauth_verifier)
    params = {callback: ENV['TWITTER_API_CALLBACK'], app_key: ENV['TWITTER_API_KEY'], app_secret: ENV['TWITTER_API_SECRET']}

    # 1. look up the oauth_token_secret by oauth_token
    twitter_auth_session = OauthSession.where("oauth_token = ? and provider = ?", oauth_token, "Twitter").first
    if twitter_auth_session
      twitter_service = Oauth::Twitter.new(params)
      oauth_token_secret = twitter_auth_session.oauth_token_secret
      twitter_resp = twitter_service.obtain_access_token(oauth_token, oauth_token_secret, oauth_verifier)

      twitter_user_id = twitter_resp["user_id"]
      screen_name = twitter_resp["screen_name"]

      # Here is the fun part
      # 1. we look up if the twitter_user_id exist
      # if yes, then it's an existing user
      # if no, then it's a new user

      # for new user we will create a new account
      # for existing user, we will look up the user info, and return the header for auth

      twitter_auth = TwitterAuth.find_by_twitter_user_id(twitter_user_id)
      if twitter_auth
        # return the user basic on the twitter id
        user = twitter_auth.user        
      else
        # insert a new twitter_auth and also create a new user account        
        str = (0...8).map { (65 + rand(26)).chr }.join
        password = Digest::SHA256.base64digest "#{twitter_user_id}#{screen_name}#{str}".first(8) # generate a password

        user = User.create(email: "#{screen_name}@yourwebsite.com", password: password)

        twitter_auth = TwitterAuth.new()
        twitter_auth.user_id = user.id
        twitter_auth.twitter_user_id = twitter_user_id
        twitter_auth.screen_name = screen_name
        twitter_auth.save        
      end

      twitter_auth_session.delete # remove the auth session 

      return user
    else
      return nil
    end
  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all you need to setup a Sign in with Twitter for your Rails API.&lt;/p&gt;




&lt;p&gt;In this post, we handled everything manually without replying on gems like &lt;code&gt;omniauth-twitter&lt;/code&gt;.  This is more a "hack" than a solution, it might be easier and better to use the &lt;code&gt;omniauth-twitter&lt;/code&gt; gem.  I hope you have enjoyed this tutorial, There is much more to be done, but this should get you started.  Thanks for reading!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>devisetokenauth</category>
      <category>twitter</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Add chapter markers to podcast/audio using FFmpeg</title>
      <dc:creator>Josh Chen</dc:creator>
      <pubDate>Wed, 29 Sep 2021 06:26:03 +0000</pubDate>
      <link>https://dev.to/montekaka/add-chapter-markers-to-podcast-audio-using-ffmpeg-3c46</link>
      <guid>https://dev.to/montekaka/add-chapter-markers-to-podcast-audio-using-ffmpeg-3c46</guid>
      <description>&lt;p&gt;Chapter markers are an easier way for listeners to navigate your podcast, see what's coming up, or skip over spoilers they don't want to hear. So I want to add the support for users to add chapter markers on &lt;a href="https://justcast.com/"&gt;JustCast&lt;/a&gt;.  To do this, I turn to my weapons of choice, FFmpeg (v4.4).&lt;/p&gt;

&lt;h2&gt;
  
  
  Add chapter markers
&lt;/h2&gt;

&lt;p&gt;FFmpeg can take metadata from a file and save it to the audio file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ffmpeg -i "input-ep1-hello-world.mp3" -i "ep1-hello-world-metadata.txt" -map_metadata 1 -codec copy "output-ep1-hello-world.mp3"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ep1-hello-world-metadata.txt
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;;FFMETADATA1

[CHAPTER]
TIMEBASE=1/1000
START=0
END=60000
title=chapter 1

[CHAPTER]
TIMEBASE=1/1
START=60
END=90
title=chapter 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to ensure two things: 1. end time needs to be greater than the start time, 2. the chapter start time needs to be greater than the last chapter end time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ffmpeg.org/ffmpeg-formats.html#Metadata-1"&gt;https://ffmpeg.org/ffmpeg-formats.html#Metadata-1&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  fluent-ffmpeg
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/fluent-ffmpeg/node-fluent-ffmpeg"&gt;Fluent-FFmpeg&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ffmpeg(url)
      .input(metafile)
      .audioCodec('copy')
      // .outputOptions('-metadata', 'title=song x')
      .outputOptions([
        "-map_metadata 0" // # 0 means copy whatever in the existing meta, 1 means ignore the existing
      ])
      .toFormat('mp3')
      .saveToFile(this.outputFilePath)   
      .on('codecData', (audioData) =&amp;gt; {
        // console.log(getSecondsFromHHMMSS(audioData.duration))
        this.duration = getSecondsFromHHMMSS(audioData.duration);
      })   
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
  </channel>
</rss>
