Content security policy with grape-swagger-rails - swagger

I am trying to get my site work with grape-swagger-rails, on the initialiser I have the following.
GrapeSwaggerRails.options.tap do |options|
options.url = '/api/swagger_doc.json'
options.app_name = 'MyApp'
options.api_auth = 'bearer'
options.api_key_name = 'Authorization'
options.api_key_type = 'header'
options.doc_expansion = 'list'
end
GrapeSwaggerRails.options.before_action do
GrapeSwaggerRails.options.app_url = request.protocol + request.host_with_port
end
Im my routes.rb I have mount GrapeSwaggerRails::Engine => '/swagger'
Swagger-rails generates the swagger_doc.json endpoint and I can see the json file.
But when I am visiting /swagger endpoint I am getting an error from Firefox
Content Security Policy: The page’s settings blocked the loading of a resource at inline (“script-src”).
This is on the inline script that is been generated on the swagger file.

Related

How can I prefix cookies with __Host or __Secure?

I am trying to add prefix to session cookies in rails 6.0.3 app but couldn't find a way to get it done. I have tried adding key to options hash in session store but it didn't help and breaks my application. I am using auth-logic gem for authentication, I find no way to get it done gracefully but hopping on that there is some way.
conf/initalizers/session_store.rb
opts = {}
if Rails.configuration.host == "myapplication.com"
opts = {expire_after: 2.months, domain: :all}
end
unless Rails.env.test?
opts[:secure] = true
opts[:same_site] = :none
end
opts[:key] = '__Host-'
Rails.application.config.session_store :active_record_store, **opts
Attached is the screenshot of github cookies. I want my session headers as like in the image (prefixed with __Host-).
As per your link...
Cookies with the __Host- prefix must have a path of /
(meaning any path at the host) and must not have a Domain attribute.
So I would presume you need to remove the domain attribute and add the path. e.g.
opts = {}
if Rails.configuration.host == "myapplication.com"
opts = {expire_after: 2.months}
end
unless Rails.env.test?
opts[:secure] = true
opts[:same_site] = :none
opts[:path] = '/'
end
opts[:key] = '__Host-'
Rails.application.config.session_store :active_record_store, **opts

How to fix SSL error thrown by omniauth LinkedIn

I'm trying to set up an authentication via LinkedIn in the rails 5.2 application, for the same I'm referring to the documentation given by devise but I am getting the following error:
ERROR -- omniauth: (linkedin) Authentication failure! Connection reset by peer: Faraday::SSLError, Connection reset by peer
I have added these using the following gems for the configuration
devise ~> 4.8.0
omniauth-linkedin-oauth2 ~> 1.0.0
omniauth ~> 2.0.4
I even tried running on the active domain in the production server which contains the valid SSL certificate but still, the same error is thrown.
Some informations about LinkedIn for you:
LinkedIn no longer supports the JavaScript SDK. The recommended approach is to use OAuth 2.0 and LinkedIn's Auth APIs.
And:
LinkedIn does not support TLS 1.0. Support for TLS 1.1 has been marked for deprecation starting 02/01/2020. Please use TLS 1.2 when calling LinkedIn APIs. All API requests to api.linkedin.com must be made over HTTPS. Calls made over HTTP will fail.
Step 1: Add Jquery for Javascript library, run command:
$ yarn add jquery
Then, set content of config/webpack/environment.js:
const { environment } = require('#rails/webpacker')
const webpack = require('webpack')
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery/src/jquery',
jQuery: 'jquery/src/jquery'
})
)
module.exports = environment
Step 2: Create ssl connection by add thin gem
gem 'thin'
$ bundle install
Edit config/application.rb and add:
config.force_ssl = true
In project command line, type:
$ openssl genrsa 2048 > host.key
$ chmod 400 host.key
$ openssl req -new -x509 -nodes -sha256 -days 365 -key host.key -out host.cert
After these commands will create two file: host.key and host.cert. Then run:
$ thin start --ssl --ssl-key-file=./host.key --ssl-cert-file=./host.cert
It will run project in default address: https://0.0.0.0:3000. If you want to run on https://localhost:3000, just type:
$ thin start -a localhost --ssl --ssl-key-file=./host.key --ssl-cert-file=./host.cert
Step 3: Create Linkedin oauth2 app.
Go to link: https://www.linkedin.com/developers/
Click the button Create app, then fill the informations to App name, LinkedIn Page (have to finish it by a custom page), App logo, Terms tick box. Then click to Create app to register your app.
At Settings tab, set the domain of your app, I run with localhost so I will set https://localhost:3000.
At Auth tab, save the Client ID and Client Secret to config/application.yml (remember to run commands $ bundle exec figaro install before this) like these:
LINKEDIN_APP_ID: 86g3...sfjm
LINKEDIN_APP_SECRET: OKnb...jzSL
Then edit, type and save to part Authorized redirect URLs for your app:
https://localhost:3000/auth/linkedin/callback
Check available scopes to use in this page! Mine is r_emailaddress r_liteprofile.
At Products tab, select Sign In with LinkedIn, status will change to Review in progress. All is ok if this status disappear after refresh F5 by a while!
Step 4: Set all codes like mine. With simple config/routes.rb:
Rails.application.routes.draw do
devise_for :users, :controllers => { :omniauth_callbacks => "omniauth_callbacks" }
get '/auth/linkedin/callback', to: "linkedin#callback"
post '/auth/linkedin/url', to: "linkedin#popup"
post '/auth/linkedin/token', to: "linkedin#get_token"
post '/auth/linkedin/info', to: "linkedin#get_info"
post '/auth/linkedin/out', to: "linkedin#stop"
root to: "linkedin#home"
end
Create app/controllers/linkedin_controller.rb with content:
class LinkedinController < ApplicationController
# Lib to get token
require "uri"
require "net/http"
# Data variables set/get
attr_accessor :client_id, :client_secret, :redirect_uri, :scope, :raise_error
# Class variable with 2#
##token = ""
# Return view linkedins/home page
def home
render 'linkedins/home'
end
# Call back from popup login window of LinkedIn site
def callback
Rails.logger.debug "Callback called! Params:"
Rails.logger.debug params
#code = params[:code]
#state = params[:state]
#redirect = '/auth/linkedin/callback'
# Get token
url = URI("https://www.linkedin.com/oauth/v2/accessToken")
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = "application/x-www-form-urlencoded"
host_uri = ENV['HOST']+#redirect
request.body = "grant_type=authorization_code&code=#{#code}&client_id=#{ENV['LINKEDIN_APP_ID']}&client_secret=#{ENV['LINKEDIN_APP_SECRET']}&redirect_uri=#{host_uri}"
response = https.request(request)
Rails.logger.debug "response.read_body:"
# Rails.logger.debug response.read_body
r = JSON.parse(response.read_body)
Rails.logger.debug r["access_token"]
##token = r["access_token"]
render 'linkedins/callback'
end
# Config init values
def start
#client_id = ENV['LINKEDIN_APP_ID']
#client_secret = ENV['LINKEDIN_APP_SECRET']
#raise_error = 'true'
#redirect = '/auth/linkedin/callback'
#redirect_uri = ENV['HOST']+#redirect
#scope = 'r_emailaddress r_liteprofile'
#state = generate_csrf_token
end
# Return popup url for sign in by LinkedIn, method = POST
def popup
self.start
#url = "https://www.linkedin.com/uas/oauth2/authorization?client_id=#{#client_id}&raise_errors=#{#raise_error}&redirect_uri=#{#redirect_uri}&response_type=code&scope=#{#scope}&state=#{#state}"
# return #url
render json: { status: 'Success', message: 'Load url for popup finished!', link: #url},status: :ok
end
# Get token of current account Linkedin logged
def get_token
Rails.logger.debug 'From get_token, ##token cache:'
Rails.logger.debug ##token
render json: { status: 'Success', message: 'Load token finished!', token: ##token},status: :ok
end
# Get basic info
def get_info
Rails.logger.debug 'From get_info!'
# Create custom api linking
fields = ['id', 'firstName', 'lastName', 'profilePicture']
link = "https://api.linkedin.com/v2/me?projection=(#{ fields.join(',') })"
url = URI(link)
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = "Bearer #{##token}"
response = https.request(request)
Rails.logger.debug "From get_info, variable response:"
Rails.logger.debug response
r = JSON.parse(response.read_body)
# r = JSON.parse(response.body)
first_name = r['firstName']['localized']['en_US'].to_s
last_name = r['lastName']['localized']['en_US'].to_s
full_name = first_name + " " + last_name
render json: { status: 'Success',message: 'Load link basic info finished!', name: full_name},status: :ok
end
# For logout LinkedIn, by method revoke
def stop
link = 'https://www.linkedin.com/oauth/v2/revoke'
url = URI(link)
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = "application/x-www-form-urlencoded"
request.body = "client_id=#{ENV['LINKEDIN_APP_ID']}&client_secret=#{ENV['LINKEDIN_APP_SECRET']}&token=#{##token}"
response = https.request(request)
Rails.logger.debug "Test logout linkedin!"
render json: { status: 'Success',message: 'Log out finished!'},status: :ok
end
# Genereate random state
def generate_csrf_token
SecureRandom.base64(32)
end
end
Note to install these gems, and we don't need any oauth2 linkedin libs:
gem 'uri'
gem 'net-http'
$ bundle install
We will exit popup LinkedIn login by this callback view app/views/linkedins/callback.html.erb:
<script>
// Close this popup show from LinkedIn window open
close();
</script>
Create this main view app/views/linkedins/home.html.erb:
<p>Linkedin Login Home page</p>
<button id="linkedin-login" type="button">Login</button>
<p id="linkedin-informations">Token here!</p>
<button id="linkedin-logout" type="button">Logout</button>
<p id="linkedin-results">Results here!</p>
<script>
$('#linkedin-login').on('click', function(e){
// e.preventDefault()
var url_popup = ""
var ltoken = ""
var lurl = ""
$.post('/auth/linkedin/url', function(json) {
console.log(json)
url_popup = json.link
if (url_popup != "") {
console.log('Successful to load url popup!')
const w = 600
const h = 600
const top = (screen.height - h) / 4, left = (screen.width - w) / 2
var child = window.open(url_popup, "popupWindow", `width=${w}, height=${h}, top=${top}, left=${left}, scrollbars=yes`)
function checkChild() {
if (child.closed) {
clearInterval(timer);
$.post('/auth/linkedin/token', function(json) {
console.log('Load token link successful!')
$('#linkedin-informations').html('Token is comming ...')
ltoken = json.token
console.log(json.token)
$('#linkedin-informations').html(json.token)
})
$.post('/auth/linkedin/info', function(json) {
console.log('Load info link successful!')
$('#linkedin-results').html('Information is comming ...')
console.log(json)
$('#linkedin-results').html(`Your login account: ${json.name}`)
})
}
}
var timer = setInterval(checkChild, 500);
}
})
})
$('#linkedin-logout').on('click', function(e){
e.preventDefault()
$.post('/auth/linkedin/out', function(json) {
console.log('Log out successful!')
$('#linkedin-results').html(`You logged out!`)
})
})
</script>
Successful screen:

Getting authorization from iNaturalist for API

I'm trying to use iNaturalist's API via Ruby on Rails. I'm new to Ruby and iNaturalist's documentation is pretty sparse. As a first step, I need to figure out how to get authorization from their site.
iNaturalist provides the sample code below. I set up a project with iNaturalist and tried running the sample code in Rails Console with my credentials. #{url} in the following line is replaced with a url that the user is supposed to go to in order to log in to iNat:
puts "Go to #{url}, approve the app, and you should be redirected to your " +
"redirect_uri. Copy and paste the 'code' param here."
I went to the resulting url and logged in:
https://www.inaturalist.org/oauth/authorize?client_id=[my client id]&redirect_uri=https://ruby_on_rails--timrobinson41199691.codeanyapp.com/login/&response_type=code
iNaturalist responds with "The redirect uri included is not valid."
If I leave off &response_type=code, it responds with "The authorization server does not support this response type."
My website is on codeanywhere.com. The url of the main page is "https://ruby_on_rails--timrobinson41199691.codeanyapp.com/". Part of the problem is that I don't understand what kind of page I'm supposed to create for redirect_uri, since I'm still kind of new at this.
require 'rubygems'
require 'rest_client'
require 'json'
site = "https://www.inaturalist.org"
app_id = 'YOUR APP ID'
app_secret = 'YOUR APP SECRET'
redirect_uri = 'YOUR APP REDIRECT URI' # you can set this to some URL you control for testing
# REQUEST AN AUTHORIZATION CODE
# Your web app should redirect the user to this url. They should see a screen
# offering them the choice to authorize your app. If they aggree, they will be
# redirected to your redirect_uri with a "code" parameter
url = "#{site}/oauth/authorize?client_id=#{app_id}&redirect_uri=#{redirect_uri}&response_type=code"
# REQUEST AN AUTH TOKEN
# Once your app has that code parameter, you can exchange it for an access token:
puts "Go to #{url}, approve the app, and you should be redirected to your " +
"redirect_uri. Copy and paste the 'code' param here."
print "Code: "
auth_code = gets.strip
puts
payload = {
:client_id => app_id,
:client_secret => app_secret,
:code => auth_code,
:redirect_uri => redirect_uri,
:grant_type => "authorization_code"
}
puts "POST #{site}/oauth/token, payload: #{payload.inspect}"
puts response = RestClient.post("#{site}/oauth/token", payload)
puts
# response will be a chunk of JSON looking like
# {
# "access_token":"xxx",
# "token_type":"bearer",
# "expires_in":null,
# "refresh_token":null,
# "scope":"write"
# }
# Store the token (access_token) in your web app. You can now use it to make authorized
# requests on behalf of the user, like retrieving profile data:
token = JSON.parse(response)["access_token"]
headers = {"Authorization" => "Bearer #{token}"}
puts "GET /users/edit.json, headers: #{headers.inspect}"
puts RestClient.get("#{site}/users/edit.json", headers)
puts
After the user logs in to iNat, he should be redirected back to my website with the authorization code provided in the data. In routes.rb, my login route is set as:
post '/login', to: 'organisms#login'
I've tried using get, as well.
iNat is returned the error mentioned above and not redirecting back to my site.
OAuth can be a bit daunting at first. And that guide really just shows the equivalent of using cURL to test your API.
In an actual application redirect_uri is whatever endpoint in your application that handles the response when the provider redirects back from authorization.
So lets setup a minimal real rails app.
1. Register your app
Register a new application or edit your existing app.
Use http://localhost:3000/oauth/inaturalist/callback for the callback url (adjust the host as needed).
Keep the window open as you will need the client_id and secret in a moment.
2. Setup your routes
# /config/routes.rb
Rails.application.routes.draw do
# just make sure you have a root path defined.
root to: 'pages#home'
namespace :oauth do
namespace :inaturalist, controller: :callbacks do
# This is just a simple redirect route
get '/', action: :passthru, as: :authorize
# This is the route that handles the actual callback
get 'callback'
end
end
end
You can actually do this without the redirect route and just plant a link to the https://www.inaturalist.org/oauth/authorize... url in your view. But having it isolates your application against the craziness that is OAuth and its how OmniAuth does it.
3. Add your credentials to the Rails app.
In Rails 5 use the encrypted credentials to store your client_id and secret.
Run $ bin/rails credentials:edit from your shell.
inaturalist:
client_id: <from the inaturalist site>
secret: <from the inaturalist site>
In earlier versions use ENV vars instead.
4. Install the oauth2 gem
# Place this in your gemfile outside any groups
gem 'oauth2', '~> 1.4', '>= 1.4.1'
Then run bundle install.
4. Setup the controller
# app/controllers/oauth/inaturalist/callbacks_controller.rb
require 'oauth2'
module Oauth
module Inaturalist
class CallbacksController < ::ActionController::Base
# GET /oauth/inaturalist
def passthru
redirect_to client.auth_code.authorize_url
end
# This endpoint is where the provider redirects the user back to
# after authorization.
# GET /oauth/inaturalist/callback
def callback
# Fetch an auth token from the access code
token = client.auth_code.get_token(params[:code])
# Perform an authenticated request to get the users data
api_response = token.get("/users/edit.json")
#user_data = JSON.parse(api_response.body)
# This is just an example of how you can use the user data from
# the provider
#user = {
uid: #user_data["id"],
nickname: #user_data["nickname"]
}
session[:user_id] = #user[:uid]
session[:token] = token.to_hash
redirect_to root_path, success: "Hello #{#user[:nickname]}"
end
private
# Change this if you are not using Rails 5 credentials.
def client
OAuth2::Client.new(
credentials.fetch(:client_id),
credentials.fetch(:secret),
site: "https://www.inaturalist.org",
redirect_uri: oauth_inaturalist_callback_url
)
end
def credentials
Rails.application.credentials.fetch(:inaturalist)
end
end
end
end
token here is actually a new OAuth2::AccessToken instance that can be called to call endpoints with the fetched credentials.
This example stores the token in the session. You can retrieve it in subsequent requests with:
token = OAuth2::AccessToken.from_hash( session[:token] )
The docs kind of mention trading the oauth access token for an api token for api.inaturalist.org. But the details are kind of sparse.
5 Add a link to sign in:
<%= link_to 'Sign in to iNaturalist.org', oauth_inaturalist_authorize_path %>

JSON::ParserError Exception: 757: unexpected token at 'Token scope not set. This request does not have the required privilege.'

I am integrating Autodesk with Ruby on Rails using adn_viewer gem (https://github.com/GetSomeRest/adn_viewer). While creating bucket using the following code:
Adn_Viewer.create_bucket(token, name, policy)
The function definition in adn_viewer.rb is as follows:
def self.create_bucket(token, name, policy)
url = URI("https://developer.api.autodesk.com/oss/v1/buckets")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["content-type"] = 'application/json'
request["authorization"] = 'Bearer ' + token
request.body = "{\"bucketKey\":\"" + name + "\",\"policy\":\"" + policy + "\"}"
JSON.parse(http.request(request).read_body)
end
The error which I am getting is:
JSON::ParserError Exception: 757: unexpected token at 'Token scope not set. This request does not have the required privilege.'
This sample is obsolete and not maintained, nor the Ruby package that was produced last year.
We are about to release a new package and sample to match the latest state of the API which was officially release last June. In the meantime, you need to add at least the bucket:create scope for this API as document here: https://developer.autodesk.com/en/docs/oauth/v2/overview/scopes/ and here: https://developer.autodesk.com/en/docs/data/v2/reference/http/buckets-POST/
Change the authenticate call with the appropriate scope you need. Usually data:read data:write data:update bucket:read bucket:create bucket:update
Change this line: https://github.com/GetSomeRest/adn_viewer/blob/master/lib/adn_viewer.rb#L9
with:
JSON.parse(CurbFu.post({:host => 'developer.api.autodesk.com', :path => '/authentication/v1/authenticate', :protocol => "https"}, { :client_id => key, :client_secret => secret, :grant_type => 'client_credentials', :scope=> 'put your scopes here' }).body)
Hope that helps,

Problems understanding a Ruby example file for Google API Client Authentication

I am trying to access Google APIs from my Rails App.
I understand the basic flow for OAuth2.0 for Web Applications.
And I consider myself having a basic understanding of Ruby and Rails.
Yet, I can't make sense of the example Google shows in the "API Client Library for Ruby (Alpha)" Guides.
require 'google/apis/calendar_v3'
require 'google/api_client/client_secrets'
require 'sinatra'
require 'logger'
enable :sessions
def logger; settings.logger end
def calendar; settings.calendar; end
def user_credentials
# Build a per-request oauth credential based on token stored in session
# which allows us to use a shared API client.
#authorization ||= (
auth = settings.authorization.dup
auth.redirect_uri = to('/oauth2callback')
auth.update_token!(session)
auth
)
end
configure do
log_file = File.open('calendar.log', 'a+')
log_file.sync = true
logger = Logger.new(log_file)
logger.level = Logger::DEBUG
Google::Apis::ClientOptions.default.application_name = 'Ruby Calendar sample'
Google::Apis::ClientOptions.default.application_version = '1.0.0'
calendar_api = Google::Apis::CalendarV3::CalendarService.new
client_secrets = Google::APIClient::ClientSecrets.load
authorization = client_secrets.to_authorization
authorization.scope = 'https://www.googleapis.com/auth/calendar'
set :authorization, authorization
set :logger, logger
set :calendar, calendar_api
end
before do
# Ensure user has authorized the app
unless user_credentials.access_token || request.path_info =~ /^\/oauth2/
redirect to('/oauth2authorize')
end
end
after do
# Serialize the access/refresh token to the session and credential store.
session[:access_token] = user_credentials.access_token
session[:refresh_token] = user_credentials.refresh_token
session[:expires_in] = user_credentials.expires_in
session[:issued_at] = user_credentials.issued_at
end
get '/oauth2authorize' do
# Request authorization
redirect user_credentials.authorization_uri.to_s, 303
end
get '/oauth2callback' do
# Exchange token
user_credentials.code = params[:code] if params[:code]
user_credentials.fetch_access_token!
redirect to('/')
end
get '/' do
# Fetch list of events on the user's default calandar
events = calendar.list_events('primary', options: { authorization: user_credentials })
[200, {'Content-Type' => 'application/json'}, events.to_h.to_json]
end
Of course this looks like Ruby, but I never saw some of the constructs used here, and I am not sure where in a Rails app that code would go.
Is it Ruby? If so, how do I use it?

Resources