micro-analytics / micro-analytics-cli

Public analytics as a Node.js microservice. No sysadmin experience required! 📈
MIT License
734 stars 39 forks source link

Token Authentication #26

Open sean-roberts opened 7 years ago

sean-roberts commented 7 years ago

As a first pass, I don't think we need to go full blown user management here. But allowing the service to only be reachable from an authenticated user might make this approachable for people who don't want their data visible to the world.

Perhaps we allow an environment variable that sets a token (user supplied) and, if set, we require the token to be passed before returning anything from the endpoint. The endpoints would still do incrementing without the token, but no data returned.

Thoughts?

mxstbr commented 7 years ago

Depends on how much code is needed to do this, in my opinion. If this is done with very little complexity, I'm a fan!

I'm no an expert on this, should we use something like jwt.io to make sure it's secure or something?

sean-roberts commented 7 years ago

I agree and let me restate how I think this could work in a super easy fashion:

ACCESS_TOKEN='aTokenIDecidedOn' && micro-analytics

Then, if that exists on start up, you are required to send the token as a param in order to get any count or analytics data back.

GET https://service.com/my/path?inc=false&access_token=aTokenIDecidedOn

Note, incrementing or write does not change here, just the ability to get the counts.

Having such a pattern would allow the end user of the service to handle deciding their tokens and how to rotate them

mxstbr commented 7 years ago

That sounds great, I like it! Adds useful functionality without making core bigger, nice one.

The one thing I'm concerned about is that you'd have to start and stop the app to rotate tokens, which might be a bit annoying? Can we somehow work around this/change the env variable while the app is running?

We can probably do this to get the new env variable everytime a new request comes in:

const getToken = () => {
  return process.env.ACCESS_TOKEN
}

// ...

if (getToken() === token) { }

But how do we switch the env var? Or is there maybe a better way?

sean-roberts commented 7 years ago

I agree, I am not entirely stoked on restart to refresh token, but perhaps that's a v2 of tokens? I don't know of an immediately obvious solution to it as all of the bits we use are "stamped" on creation of the process and we aren't tracking any other data in the db besides the metrics.

Perhaps we have an endpoint that can roll the in memory token but the persisted version would need to be on them to set if they do restarts.

For example, we could reserve underscore routes like service.com/_version and service.com/_exchangeToken?token={}&&newToken={}

I still think that could be accomplished in v2 though

sean-roberts commented 7 years ago

on this same note, if we added a write token that needed to be present in order for incrementing to happen, that could help solve #28 and #11 as only explicit calls containing ?write_token= because chrome wouldn't auto have it and crawlers wouldn't have it either. Rotating is easy if they do. Though, if user is just blanket sends any request without also checking IPs it wont prevent crawlers that execute js and trigger views

sean-roberts commented 7 years ago

also, if we went the path of https://github.com/micro-analytics/micro-analytics/issues/11#issuecomment-272712468 we could allow users to expose a canWrite and canRead function from the extension module that will receive the request and could handle telling us to proceed. That would allow them to do something dynamic like jwt and have it expire and identify sessions etc. But importantly it would allow them fine grained control over it if they want.

But we should still do the simple tokens that are static for now to let people take advantage of the lockdown but with a super simple change to turn it on