How do I add a token to my request in Rails 4? - ruby-on-rails

I have an upload application that will be accessible by multiple other applications. I want user security from those applications to be federated to this upload hub. I'm doing it with HTTP tokens that are shared between the applications.
I have read 5 different articles on how to secure an API in Rails. I have everything set up properly in the upload hub app. I have keys set up in my User table, and I have shared this key with the user table in the other apps. I have the code in place to check for this key, and to locate the appropriate subscription. Upon failure, the user is redirected to the login page of the source application.
There's just one thing. I can't find anywhere how to create the actual request! I want the users in the other applications to click an "upload" link that will take them to this application. No problem creating the link, of course. The problem is, how do I add the token to the request?
See, I'm not making programmatic requests to an API... The user is actually going from one app to another. I want the upload hub app to check the request and say "Oh, there's this big long key. Let me see if I can find a user associated with that. I'm obviously not going to put it in a query string.
The only thing I've come up with on my own is to have a bit of jQuery code that constructs a form with a hidden item containing the key in it, which it would get from a hidden field on the current page, then do a POST against the upload hub app. That's not really a token, but it's doable. Surely there's a better way?
UPDATE
I went the jQuery "postGo" route where I construct a form with a hidden field and send it as a parameter to the other app. I had forgotten about protect_from_forgery though. However, even after turning protect_from_forgery off, Rails somehow still strips all parameters except for controller and action from outside requests. Undocumented feature? Or maybe I missed the documentation.
Really, I just want SSO on a couple of apps. It looks like I'm going to have to create an Oath provider. Sure are a lot of hoops to jump through to federate a simple session on some apps that I own.

Here's a possible way of doing this.
In the upload controller method of the app, instead of redirecting the user directly, make a request to the upload hub app.
In the upload hub app, add a controller method and generate a token, store it and a user's credential in redis, or other storage of your choice, then response the app with the token.
Your app's upload controller method receive the response from upload hub, then give 302 response back to the client, with the token included in the redirect url.
The client redirects to the upload hub
The upload hub router accepts this format, the token can be in a query string.
Check in redis and find it by the token and match the user's credential

Related

Dropbox OAuth2 API always prompts user for permission when a refresh token is requested

I'm writing an offline application that uses the Dropbox API. When a user comes to the application, I'm using the https://api.dropbox.com/oauth2/token (docs) to get a refresh_token, which I store for later use.
I am calling the same endpoint every time the user logs in (unless I've already got the user's data in a cookie). I'm not sure that this is the best way to go about it: I at least need to get the user's account_id, so that I can look up their refresh_token in the database if I already have it. But every time I call https://api.dropbox.com/oauth2/token, the user is redirected to the Dropbox app authorization interface, as if they've never approved the app before.
So I would either like to know how to stop Dropbox from forcing the user to re-authorize an app every time. Or, if that is just how https://api.dropbox.com/oauth2/token is supposed to work, I'd instead like to be able to get their account_id somehow when they visit my page.
(In case it's relevant, the app is still in development mode at this point.)
The https://api.dropbox.com/oauth2/token endpoint is an OAuth endpoint that the app can call to get an access token/refresh token. Being an API endpoint, it does not itself redirect the user to the Dropbox app authorization page.
The Dropbox app authorization page is at https://www.dropbox.com/oauth2/authorize (documented here), and the app decides if/when to direct the user there to authorize the app.
You generally only need to send the user through the app authorization flow (sending them to https://www.dropbox.com/oauth2/authorize and then calling https://api.dropbox.com/oauth2/token) once per user for an "offline" application. Once you do so, you should store the resulting refresh token for that user. You'll typically store the refresh token for that user tied to their user account in your own app.
Exactly how you manage the user accounts in your own app will depend on how it's built, but, as it sounds like this is a web app, typically you would use the user's browser cookies to identify the user when they return to your page so that you can look them up in your database and retrieve their details, such as their corresponding refresh token. (Or, if they're not already signed in to your web app, you would have them do so first.)
Greg's answer is very helpful, and very politely addresses my misunderstanding of the auth flow. (I was revisiting old code I'd written years previously—obviously I should have documented it better than I had!)
In the end I believe that Dropbox was forcing me to reauthorize because my application was in development mode, and had a small user base. When I used the identical code in an app set to production mode, it stopped forcing me to reauthorize. So the “problem” is really a Dropbox security feature, and the solution was just to use production mode.

Rails app Query params and Cookie based login

I am trying to find a way to distribute a Ruby on Rails app selectively by sending them an email with the link that logs them in. This should be the only way to get to the page hosted at a unique subdomain. Additionally, we don't plan on having a login wall so the access would need to be guarded by using Cookies or the Query URL params.
A couple of questions regarding this:
Is it possible to leverage cookies exclusively to achieve this? I.e Any way to embed cookies within the URL sent in the email itself?
An approach I felt that might work is to embed the user ID (encrypted) in the URL in the email. In order for the users to not need to bookmark this URL or go back to the email to access this link, I was planning to store their session ID via a browser cookie. Any issues with this approach?
How to avoid wandering users(i.e users who haven't received this email) to access this page (i.e a nice way to raise a 404 error)?
Any other cleaner ways to accomplish this task?
Is it possible to leverage cookies exclusively to achieve this? I.e
Any way to embed cookies within the URL sent in the email itself?
No. You cannot "embed cookes in a URL". Cookies are set via the SET-COOKIE header in a response or through JavaScript.
An approach I felt that might work is to embed the user ID (encrypted)
in the URL in the email. In order for the users to not need to
bookmark this URL or go back to the email to access this link, I was
planning to store their session ID via a browser cookie. Any issues
with this approach?
Yes. You should generate a random token thats not tied the users id.
How to avoid wandering users(i.e users who haven't received this
email) to access this page (i.e a nice way to raise a 404 error)?
Create time limited access tokens that can only be used once by the user. There really is no other way for you to actually know that the person requesting the URL is the recipient of the email.
What you are describing can be accomplished with Devise Invitable which is a pretty good community tested point of reference if you want to reinvent the wheel.
When you invite a user Invitable creates invitation tokens which are stored in the users table. This is just a random string. There are also timestamp columns that expire invitations automatically.
The token is included in the URL in the invitation email as a query parameters.
When the user clicks the link the controller looks up the user based on the token and nulls users.invitation_token so that it cannot be used again. This stores the user id in the session and takes the user to a screen where they edit and finalize their account by setting a password.

Sending custom data via OAuth (specifically LinkedIn API)

I'm using the LinkedIn API to authenticate users. After authentication LinkedIn posts data back to my app at /auth/linkedin/callback (as is the OAuth standard).
I also offer other auth methods (e.g. Google+, Twitter, etc..). If the user signs in with one of those, I want them to also be able to (on top of their existing auth) connect with LinkedIn to pull in additional data.
In those cases, I want the callback URL to be something different so I don't have to do some very custom if...then logic.
Is there a way to specify in an OAuth request where you want to call back to?
If #1 isn't possible, can I send some custom arbitrary data or flags with my initial request that are returned during the callback? That way when parsing the response I'll check those flags for the appropriate state of the User and redirect them as necessary.
Thanks!
The state parameter is guaranteed to be preserved across all interactions. You can store (encoded?) information in it, and when the final callback is called, you will get back the original value you sent.
A common use case is capturing the "end URL" for the user (like a deep link into your app that requires auth).
You are supposed to check that what you receive is exactly what you sent.

IOS application web-scraping dilemma

I have implemented a web-scraping iOS application for a service provider to get data usage information. This used to work fine using NSURLConnection to post the username and password to the site, and then scrape the response.
However, said service provider has recently upgraded their account website and the login step now also includes an account selection step. So you first provide a username and password, the response then includes a list of all your accounts. This is presented in a selectable list. Clicking an account causes another postback, the response is the data I want to scrape.
I am unsure how to programatically perform this second step, since no data is sent with the request, and no extra cookies are set. Each clickable account link causes a __dopostback javascript action, so it is all handled serverside. There seems no way for me to perform this postback using nsurlconnection as no data about which account was clicked is sent with the post.
Any ideas how I could get around this?

How to handle authorizing the same third-party application multiple times for a single user account?

I'm working on a cloud-storage API, authorized via OAuth. Users of third-party applications can permit said application to access their files/data via our RESTful API.
Currently, we are limiting a third-party app access to a users account once. E.g., the Access Token table has a UNIQUE on the consumer column and the user column. This makes sense at first glance, as the user should never be sent to our service to authorize a third-party application twice, since the third-party would already know their user is already tied to our service and wouldn't need to be re-authorized.
However, what if this user has two accounts on the third-party app, and they want said app to connect to their single account on our service twice? This seems likely, given the prevalence of multiple accounts on services such as Reddit.
Here are the possible solutions I've come up with so far, none of them being perfect:
Display an error during the second auth request: This seems like a frustrating experience for the user, a "cop out" of sorts.
Delete the previous token: This would likely annoy the user, as their previous accounts stop working. Even if we display a warning, it would likely be hard to explain what exactly is happening.
Return the same access token as the first request: Each time the access is requested, a set of permissions are also passed along. The permissions for the second request could be different than the permissions for the first request. Also, not sure if this will violate the OAuth spec, as the secondly generated Request Token isn't tied to the Access Token properly.
Allow two to be generated: This would be confusing, as when the user visits their screen full of authorized applications to revoke one, they don't know which authorization is tied to which third-party account. We could ask for an optional third-party username parameter when the Request Token is generated to identify the different auth's (we currently ask for a non-OAuth-standard permission parameter already). But, this seems like it wouldn't be used by 99% of developers and could make application development more confusing.
What is the best way to handle this situation? Is there a standardized practice for handling this use-case?
I think your last case is the right way to go - Allow two to be generated
When the user visits his screen full of authorized application, it's not necessary to show him one and the same Application twice - you just have to delete the tokens associated with the app if the user revokes application access. That is, all his authorizations to the app with all tokens will go away with the revoke, which is fine.

Resources