I have been trying to implement FineUploader in Rails and I am running into the following error:
Invalid according to Policy: Extra input fields: success_action_status
I am using the example from the FineUploader docs and my signature and policy signing are coming through properly. It looks like FineUploader is passing "success_action_status" in the POST to S3 and that is causing the issue?
Does anyone know if I need to add something additional to my bucket policy on S3 or do I need to change a parameter on FineUploader?
Here is the implementation that I am using for the FineUploader JS control:
var uploader = new qq.s3.FineUploader({
element: document.getElementById('fine-uploader'),
request: {
endpoint: 'https://MyBUCKET.s3.amazonaws.com/',
accessKey: 'MY_ACCESS_KEY'
},
signature: {
endpoint: 'home/generatesignature'
},
uploadSuccess: {
endpoint: '/s3/success'
},
iframeSupport: {
localBlankPagePath: '/success.html'
}
});
Update
After going around with trying to get Rails to work with FineUploader I was finally able to get it to work. For anyone that runs into issues with implementing FineUploader/Rails the key is that the S3 policy is sent in the post body to the server and that is what needs to be encoded and signed before returning. Here is the action to make everything work serverside in Rails
def generatesignature
policy = Base64.encode64(request.raw_post).gsub("\n","")
s3_signature = Base64.encode64(
OpenSSL::HMAC.digest(
OpenSSL::Digest::Digest.new('sha1'),
'YOUR_SECRET_KEY', policy)
).gsub("\n","")
params[:signature]= s3_signature
params[:policy] = policy
render :json => params, :status => 200 and return
end
Related
I created a Rails API with a JWT authentication system and deployed it to Heroku. When I request the endpoints locally, all seems to be working fine but when I make requests to the live endpoints (i.e the Heroku deployed app) I get a: 422 Unprocessable Entity server error and the response body looks like this:
{
"message": "No verification key available"
}
The class responsible for encoding and decoding the auth token is defined as follows:
class JsonWebToken
# secret to encode and decode token
HMAC_SECRET = Rails.application.secrets.secret_key_base
def self.encode(payload, exp = 24.hours.from_now)
# set expiry to 24 hours from the creation time.
payload[:exp] = exp.to_i
# sign token with application secret
JWT.encode(payload, HMAC_SECRET)
end
def self.decode(token)
# get payload, first index in decoded Array
body = JWT.decode(token, HMAC_SECRET)[0]
HashWithIndifferentAccess.new body
# rescue from all decode errors
rescue JWT::DecodeError => e
# raise custom error to be handled by custom handler
raise ExceptionHandler::InvalidToken, e.message
end
end
I have an endpoint /signup where I can make a POST request to register a new user and POST /todos which is accessible and available only to registered users. Making a registration request works perfectly fine, but when I try to make the POST request to the /todos endpoint it raises an error.
The association between user and suit is 1:m respectively.
Please if you have any idea on how I can fix this, I'll be very grateful, thanks : ).
I finally figured a way out by altering the Rails.application.secrets.secret_key_base to Rails.application.secret_key_base. For a more detailed review on this please check out this link. Hopefully, this will help someone facing a similar issue.
This was also my problem. After checking out my json_web_token.rb file, I figured out that I had written the following line:
HMAC_SECRET = Rails.application.secrets.secret_key_base
There is an extra secrets reference, which is causing the problem. It should be:
HMAC_SECRET = Rails.application.secret_key_base
But as far as I'm concerned, you managed to figure it out yourself!
I can not figure out what I'm doing wrong. I'm developing an App for BigCommerce and can not get the simple oAuth exchange to work correctly.
The initial get request is being made to https://www.my-app.com/oauth/bigcommerce/auth. This is the code in the controller for that request. It's a Laravel 5.6 app:
use Illuminate\Http\Request;
use Bigcommerce\Api\Client as Bigcommerce;
class BigcommerceOAuthController extends Controller
{
public function auth(Request $request)
{
$object = new \stdClass();
$object->client_id = 'my-client-id';
$object->client_secret = 'my-client-secret';
$object->redirect_uri = 'https://my-app.com/oauth/bigcommerce/auth';
$object->code = $request->get('code');
$object->context = $request->get('context');
$object->scope = $request->get('scope');
$authTokenResponse = Bigcommerce::getAuthToken($object);
$storeHash = str_replace('stores/', '', $request->get('context'));
Bigcommerce::configure(array(
'client_id' => 'my-client-id',
'auth_token' => $authTokenResponse->access_token,
'store_hash' => $storeHash
));
echo "<pre>";
print_r($authTokenResponse);
print_r(Bigcommerce::getTime());
echo "</pre>";
}
}
Every time I try to install my draft app from the BigCommerce control panel, I get an error because $authTokenResponse is not an object. When I debug further into the Bigcommerce\Api\Connection class, I can see that the response from the server is empty, and the status is a 401, which means "Unauthorized".
I can't figure out why I am getting this error. As far as I can see, I'm doing everything right. I've tried urlencoding the string retrieved from $request->get('scope'), since that string becomes unencoded by Laravel, but that didn't seem to help.
I am also confused how this is even supposed to work at all. In the BigCommerce docs, they show this example POST request, which uses application/x-www-form-urlencoded Content-Type and passes the request body as a url encoded string:
POST /oauth2/token HTTP/1.1 Host: login.bigcommerce.com Content-Type:
application/x-www-form-urlencoded Content-Length: 186
client_id={CLIENT_ID}&client_secret={CLIENT_SECRET}&code=qr6h3thvbvag2ffq&scope=store_v2_orders&grant_type=authorization_code&redirect_uri=https://app.example.com/oauth&context=stores/{STORE_HASH}
However, if you inspect what's going on in the Connection class, you can see that the Content-Type is being set to application/x-www-form-urlencoded as the docs say, but the request body is being passed in as a json string, not a url string. Shouldn't the request be a url encoded string as the docs suggest?
A couple of things here to check:
Do you have a public URL where you can receive the Auth Callback?
If so, did the store owner registered the app successfully? https://developer.bigcommerce.com/api/registration
When you have the client_id and secret_id. You should have all of the details needed to send a POST request to the BC Auth Token Service at https://login.bigcommerce.com/oauth2/token
The content uses URL encode Make sure to URL encode your content. Be careful of of the encoding of & and = signs when those are actually being used as separators.
More details can be found in this post:
Can BigCommerce Private Apps use OAuth
I want to get unauthenticated access token from vimeo api in my rails app. However the post request made using HTTParty gem returns following response from API
{"error"=>"You must provide a valid authenticated access token."}
The code to send request is
header = "basic " + Base64.encode64("****07974be" + ":" + "****ygYBI7I")
token = HTTParty.post("https://api.vimeo.com/oauth/authorize/client",
:body => {:grant_type => 'client_credentials'},
:header => {'Authorization' => header}
)
json=JSON.parse(token)
I have checked that credentials are correct and also tried replacing :header with :headers, and various combinations of using string instead of symbol in the header hash. But none of them works.
However, the call to same URL, using same credentials is successful through Postman.
Edit As mentioned in a answer, we need to use headers (plural) while making the call. However, I had already tried that but problem persists. Using basic_auth, instead of sending headers do seems to work, however I can not figure out why sending headers through HTTParty is not working but similar call is working through Postman.
The :headers option is definitely plural, but since you are using basic auth, you can also use HTTParty's basic auth option. So your request would become:
username = "YOUR-USER-HERE"
password = "YOUR-PASSWORD-HERE"
token = HTTParty.post("https://api.vimeo.com/oauth/authorize/client",
body: {:grant_type => 'client_credentials'},
basic_auth: { username: username, password: password }
)
Using your creds (did you mean to post real creds?) I got
{"access_token"=>"REDACTED", "token_type"=>"bearer", "scope"=>"public", "app"=>{"name"=>"Fable", "uri"=>"/apps/REDACTED"}}
I'm building Slack integration for my Ruby on Rails application and I'm trying to get an access_token from the Slack API for my Slack App when a user clicks the Add to Slack button.
From Postman, I can successfully post the following:
https://slack.com/api/oauth.access?client_id=idgoes.here&client_secret=secretgoeshere&code=12345&pretty=1
However, within Rails I always get a response with invalid_client_id, regardless of the way I call the API. I have checked my ID is correct (a lot) and tried regenerating it, but I don't think that is the issue due to the postman success.
Within my get_oauth_access_token method I have tried the following implementations:
1.
rc = JSON.parse(HTTP.post('https://slack.com/api/oauth.access',
params: {
client_id: 'idgoes.here',
client_secret: 'secretgoeshere',
code: '12345'
}))
2.
response = Excon.post('https://slack.com/api/oauth.access',
headers: { 'Content-Type' => 'application/json; charset=utf-8' },
user: client_id, password: client_secret,
body: oauth_request_body.to_json)
Any implementation I try always ends up getting a invalid_client_id response.
I'm aware it may be something to do with environment config, but I'm not sure what would be helpful to debug, so please let me know what other information I can share. I'm running on localhost.
Update:
I just found out that many (maybe all) of the Slack APIs do not accept a JSON format body (which seems crazy seeing as they send a response in JSON.
Make sure to use x-www-form-urlencoded format body on your request or it will not work properly.
"Content-Type" => "application/x-www-form-urlencoded"
I use oauth2 gem to authorize. So I was able to get this to work by reading the slack documentation and using oauth2 in my controller:
class OauthController < ApplicationController
def authorize
options = {
site: 'https://slack.com/oauth/authorize'
}
client ||= OAuth2::Client.new(
'client-id',
'client-secret',
options
)
params = {
scope: 'incoming-webhook, commands',
redirect_uri: 'https://localhost:3000/oauth/callback'
}
redirect_to client.auth_code.authorize_url(params)
end
def authorize_callback
puts params["code"]
redirect_to root_url
end
end
Routes file:
get '/authorize', to: 'oauth#authorize'
get '/oauth/callback', to: 'oauth#authorize_callback'
Don't forget to set your callback url at Oauth settings on api.slack.com, I used localhost for testing purposes as you can see.
I have a Rails app in which I want users to be able to sign in / up with Google. I'm using the following gem:
#gemfile
gem 'omniauth-google-oauth2'
I've almost got it to work (I actually received the access token once, not sure why) - but before getting the access_token I constantly get the following error:
"error"=>"invalid_grant", "error_description"=>"Invalid code."
I have checked so that the code is unique for each request and that it isn't nil. The relevant parts of the method where I try to get the access_token after I've received the one time authorisation code looks like this:
def google_authentication
respond_to do |format|
# authorisation code
code = params[:code]
unless code.blank?
client_id = ENV["GOOGLE_CLIENT_ID"]
client_secret = ENV["GOOGLE_CLIENT_SECRET"]
redirect_uri = 'postmessage'
grant_type = 'authorization_code'
load = {client_id: client_id, client_secret: client_secret, redirect_uri: redirect_uri, grant_type: grant_type, code: code}
payload = load.to_json
url = "https://www.googleapis.com/oauth2/v3/token"
response = HTTParty.post(url, :query => load)
json = JSON.parse(response.body)
unless json.nil?
unless json[:error].present?
# TODO: Handle data
format.json { render :json => {:message => "Success"} }
else
# ERROR "Invalid code" always happen
end
end
end
end
end
In Google's developer console I have the following credentials:
Client ID [CLient ID]
Email address [Email]
Client secret [Secret]
Redirect URIs http://127.0.0.1:3000/
JavaScript origins http://127.0.0.1:3000
Would be thankful for any ideas or tips.
Update for completion
This is how I set up omniauth-google-oauth2:
Rails.application.config.middleware.use OmniAuth::Builder do
provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_CLIENT_SECRET"],
{
:scope => "email, profile",
:prompt => "select_account",
:provider_ignores_state => true
}
end
Update 2
As mentioned above I once managed to get the access token once, I managed to reproduce it again. I did it by clicking my sign in button three times.The first time I got:
"error"=>"invalid_grant", "error_description"=>"Invalid code."
The second click resulted in:
"error"=>"invalid_grant", "error_description"=>"Code was already redeemed."
And the third time I successfully got the access_token. I find it really strange that I sometimes get the access_token, but most of the time get:
"error"=>"invalid_grant", "error_description"=>"Invalid code."
And the success / error "rate" isn't 100% consistent. Sometimes it takes more than three clicks. I find it quite strange that it sometimes works and that I sometimes get different error responses without changing anything in my code.
Could it be related to time / expiration date of the code?
Update 3
For additional completion. This is how my Javascript (or CoffeeScript) looks like when the user clicks the Google Sign in button:
$(document).ready ->
$.ajax
url: 'https://apis.google.com/js/client:plus.js?onload=gpAsyncInit'
dataType: 'script'
cache: true
window.gpAsyncInit = ->
$('.googleplus-login').click (e) ->
e.preventDefault()
gapi.auth.authorize {
immediate: false
response_type: 'code'
cookie_policy: 'single_host_origin'
client_id: '[id]'
scope: 'email profile'
}, (response) ->
if response and !response.error
jQuery.ajax
type: 'POST'
url: '/auth/google_oauth2/callback'
dataType: 'json'
data: response
success: (json) ->
# response from server
console.log "JSON: " + json
return
else
# google authentication failed
So ...as it is wrote:
Authorized redirect
URIs One URI per line. Needs to have a protocol,
no URL fragments, and no relative paths. Can't be a public IP Address.
Your setting:
Redirect URIs http://127.0.0.1:3000/
JavaScript origins http://127.0.0.1:3000/
...is wrong.
It should be:
Redirect URIs: http://my.true.domain/users/auth/google/callback
and
provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_CLIENT_SECRET"],
{
:scope => "email, profile",
:name => "google",
...
I hope it help!
I cant help with the ruby part but I may be able to help you figure out what's wrong.
There are in fact 3 codes returned by Google's authentication server. Authorization code, Access token, and refresh token.
Authorization code can only be used once to get the first refresh token.
Access Token used to access data on the apis, expires after an hour.
Refresh Token used to get a new access token when it expires. good until the user removes access.
Authorization code
This is the code that gets returned when the user hits accept to your application.
Example:
Change the client id, secret, and scope in this URI to the ones you are using. then paste it into a browser location bar.
https://accounts.google.com/o/oauth2/auth?client_id={clientid}.apps.googleusercontent.com&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope={scope}&response_type=code
It will prompt you for authentication. If you click except you get another window with a long code in it looking something like this.
That is the Authentication code, its only purpose in life is for you to use it to get an access token and a refresh token. It can only be used once, and its probably short lived though I have never tested how long they are good for.
In the uri above Note: the response type code.
Exchange:
Once you have that authentication code you need to exchange it for an access token and a refresh token. this is a HTTP POST so cant be placed in a browser window.
https://accounts.google.com/o/oauth2/token
code=4/X9lG6uWd8-MMJPElWggHZRzyFKtp.QubAT_P-GEwePvB8fYmgkJzntDnaiAI&client_id={ClientId}.apps.googleusercontent.com&client_secret={ClientSecret}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&grant_type=authorization_code
Note: grant_type=authorization_code this tells the server you are sending it an authorization code.
response
{
"access_token" : "ya29.1.AADtN_VSBMC2Ga2lhxsTKjVQ_ROco8VbD6h01aj4PcKHLm6qvHbNtn-_BIzXMw",
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : "1/J-3zPA8XR1o_cXebV9sDKn_f5MTqaFhKFxH-3PUPiJ4"
}
You now have an access token that can be used to access the Google APIs, it is short live lasts only 3600 seconds or 1 hour. After that you must use the refresh token to get access again.
Use refreshtoken
https://accounts.google.com/o/oauth2/token
client_id={ClientId}.apps.googleusercontent.com&client_secret={ClientSecret}&refresh_token=1/ffYmfI0sjR54Ft9oupubLzrJhD1hZS5tWQcyAvNECCA&grant_type=refresh_token
response
{
"access_token" : "ya29.1.AADtN_XK16As2ZHlScqOxGtntIlevNcasMSPwGiE3pe5ANZfrmJTcsI3ZtAjv4sDrPDRnQ",
"token_type" : "Bearer",
"expires_in" : 3600
}
Now that you understand all of that
"error"=>"invalid_grant", "error_description"=>"Code was already redeemed."
means that you are sending the authorization code again you can only use it once you should be sending the refresh token again. There is something up with your authentication flow. Again sorry I cant help with the ruby part.
code ripped from Google 3 legged oauth2 flow