jad-b / Torque

Server-side work for the Crank app
0 stars 0 forks source link

Implement Authentication #4

Open moogar0880 opened 9 years ago

moogar0880 commented 9 years ago

I'm voting that our initial pass at auth uses a stupid access token implementation. It's similar to the auth method other popular Apis (Twitter, Trakt, etc) have implemented for single script and developer use. It will also be easier to throw OAuth in its place once we're mature enough to need something like that.

Current Requirements

https://dev.twitter.com/oauth/overview

jad-b commented 9 years ago

So, this is what I'm picturing:

Wrap the RouteByMethod with an authenticate method for those that need it

authenticate validates the auth_token header

Resources which need to know which user they're acting upon will use GetUserFromRequest to exchange the auth token for a user ID

Method names, as always, are negotiable.

moogar0880 commented 9 years ago

Yup, that's pretty much all it is. We'll probably want to flush out the authenticate decorator before we write it though.

Things will get kind of tricky when we need to convert a username-password combination into an auth token though. Still haven't quite wrapped my head around that yet, but I'm still reading up on auth mechanisms

tl;dr auth is weirdly simple, except for when it isn't

jad-b commented 9 years ago

Will it be more than running an auth endpoint that swaps username and password for an auth token?

Also, after reading this https://codingkilledthecat.wordpress.com/2012/09/04/some-best-practices-for-web-app-authentication/

...I'm thinking it wouldn't really be any harder now to pull PII into a separate table, and have a table like:

Password hash User ID Current Auth token Auth token last seen time .... Maybe more auth token details

And then real name, email, address, ask that stuff lives elsewhere in the PII table. Some questions about salting password hashes, mainly how you can reliably do so.

moogar0880 commented 9 years ago

So, username/passwords are actually more complex than that. You need to hash them before you send the request from the client (no distinguishable credentials get sent on the wire), and then you need a way to reliably unhash them on the server, so you can check the encrypted value against the one in the database before you can validate the users credentials. And that's actually where, eventually, the notion of applications will come into play. One hashing option would be to use a known application key on the client side to hash the provided credentials and then send those out. The other semi-popular solution that I've seen people suggest you hash with is a known timestamp.

As for reliably salting password hashes, I found this to be a pretty informative intro to how password hash salting works: (http://security.stackexchange.com/questions/91447/understanding-salted-password-hashing-using-java) The commenters to do a decent job of expanding on the tutorial that OP originally linked to, which was also a pretty decent read.

And sure, separating password stuff from the user record that they're linked to sounds pretty reasonable

moogar0880 commented 9 years ago

Re: Our earlier conversation, here's an excerpt from the tutorial linked in my above SO post.

To check if a password is correct, we need the salt, so it is usually stored in the user account database along with the hash, or as part of the hash string itself. The salt does not need to be secret. Just by randomizing the hashes, lookup tables, reverse lookup tables, and rainbow tables become ineffective. An attacker won't know in advance what the salt will be, so they can't pre-compute a lookup table or rainbow table. If each user's password is hashed with a different salt, the reverse lookup table attack won't work either.

I think it's probably premature optimization (or maybe just overkill?) to not just store per-user PasswordSalt's in the db

jad-b commented 9 years ago

Handling client auth (from our gym talk):

tAPI := ConnectToTorque(url, options)
user := Authenticate(tAPI, username, password)
bw := metrics.NewBodyweight(179.0, "Morning after a highlighter party")
bw.Post(tAPI, user)

tAPI holds server configuration. It may not be necessary to pass through to calls. user holds session state via the auth token. We may want to make some of the User fields private.

Alternatively, you could use the User as a conduit:

user.Post(tAPI, bw)

...maybe. Depends how generic the REST methods are.

jad-b commented 9 years ago

Actually, I'm thinking we could take a page from json.Marshaller. The torque.API object can provide Post, Get, Put, Delete, Patch methods with signature: func Post(u UserAuth, v interface{}) (http.Response, error).

Within these calls, the torque.API can check if v implements, say, the ClientPOSTer interface. If so, let v POST on behalf of v. Otherwise, handle it generically.