Authentication Devise - lazaronixon/react-native-turbolinks GitHub Wiki

This Wiki show you how to use React Native Turbolinks integrated with Devise.

How to respond with 401 for an APP request ?

By default devise will repond a unauthenticated request with a redirect(302) for html requests, but we need made it respond with a 401 status.

Rails

models/custom_failure.rb

class CustomFailure < Devise::FailureApp
  def respond
    if mobile_http_auth?
      mobile_http_auth
    elsif http_auth?
      http_auth
    elsif warden_options[:recall]
      recall
    else
      redirect
    end
  end

  private
    def mobile_http_auth?
      is_navigational_format? && mobile_user_agent?
    end

    def mobile_http_auth
      self.status        = 401
      self.content_type  = Mime[:html]
      self.response_body = ApplicationController.render('layouts/custom_failure', layout: false)
    end

    def mobile_user_agent?
      request.user_agent =~ /MobileUserAgent/
    end
end

config/initializers/devise.rb

config.warden do |manager|
  manager.failure_app = CustomFailure
end

layouts/custom_failure.html.erb

<html>
  <head><%= javascript_include_tag 'turbolinks' %></head>
  <body></body>
</html>

config/initializers/assets.rb

Rails.application.config.assets.precompile += %w( turbolinks.js )

React Native

App.js

import React, { Component } from 'react'
import Turbolinks from 'react-native-turbolinks'

export default class App extends Component {

  componentDidMount() {
    Turbolinks.addEventListener('turbolinksVisit', this.handleVisit)
    Turbolinks.addEventListener('turbolinksError', this.handleError)
    Turbolinks.startSingleScreenApp({url: 'http://192.168.1.104:3000'})
  }

  handleVisit = (data) => {
    Turbolinks.visit({url: data.url, action: data.action})
  }

  handleError = (data) => {
    if (data.code == Turbolinks.Constants.ErrorCode.httpFailure && data.statusCode == 401) {
      Turbolinks.visit({component: 'AuthenticationView', modal: true})
    }
  }

  render() { return null }
}

AuthenticationView.js

import React, { Component } from 'react'
import { SafeAreaView } from 'react-native'
import { WebView } from 'react-native-webview'
import Turbolinks from 'react-native-turbolinks'

const signInUrl = 'http://192.168.1.104:3000/users/sign_in'
const signedUrl = 'http://192.168.1.104:3000/'

export default class AuthenticationView extends Component {

  componentDidMount() { this.authenticatedFlag = false }

  handleAuthentication = (event) => {
    if (event.nativeEvent.url == signedUrl && this.authenticatedFlag == false) {
      this.authenticatedFlag = true

      this.webview.stopLoading()
      Turbolinks.dismiss().then(() => Turbolinks.reloadSession())
    }
  }

  render() {
    return (
      <SafeAreaView style={{flex: 1, backgroundColor: '#fff'}}>
        <WebView ref={webview => { this.webview = webview }}
                 source={{uri: signInUrl}}
                 onLoadStart={this.handleAuthentication}/>
      </SafeAreaView>
    )
  }
}
⚠️ **GitHub.com Fallback** ⚠️