I have searched around but cant find an a satisfactory answer to this question.
I have a meteor website where users login and create content. I also want to create a phone app, that is capable of interacting with the website, and I want the users to log into the phone app and access the same content on the website. Pretty normal.
I have created a basic REST API for accessing the collections using the meteorite package HTTP.publish
. It is working without any user info (no auth), but now I want to use the the userId
of the GET methods and in the Meteor.allow rules of the collections to access the current user.
So I am currently struggling with how to tell meteor on a REST request, the id of the user, even while just testing. I thought I could get the Accounts._storedLoginToken
of a valid user in the browser and use that to test with CURL. Something like
curl -H "X-Auth-Token: asdklfjasldfjlsadkjf" -H "Content-Type: application/json" -d '{"name":"A Name","description":"Testing description"}' http://localhost:3000/api/places
I tried this, but no joy, I get a 403 which is good at least.
My questions are this:
- Are the tokens created specific to the client (ie hashed with host url or something)?
- Has bcrypt change the way
X-Auth-Token
is used? If not what am I doing wrong in the curl command.
- Is DDP the ONLY way to create valid tokens or can I create a API call that will create a token on the server, even just passing plain text credentials for now?
eg /api/login?user=shane&pwd=qwerty
=> return token
I can use in curl request.
I am really stuck with this so anything pointing me in the right direction would be appreciated. I also note the http.publish
has not yet created the login/logout methods, so maybe it's not that easy.
A few days ago I started on an app with similar requirements regarding authentication. I found that Differential's RESTstop2 recently, in version 0.6.0, upgraded their authentication support to support the newly added Bcrypt encryption in Meteor.
You simply send username and password either as URL params or body like this:
curl --data "password=testpassword&user=test" http://localhost:3000/api/login/
and the server will return the following (if credentials are correct):
{ success: true, loginToken: "f2KpRW7KeN9aPmjSZ", userId: fbdpsNf4oHiX79vMJ }
On each request you make to the server, include the loginToken and userId as headers.
You should check it out:
Docs: http://github.differential.io/reststop2/
Github: https://github.com/Differential/reststop2
Another option (besides RESTstop2 mentioned in other answer), you can use stand-alone api-password package from Atmosphere, which does exactly what you need: authenticate REST calls on the server side.
It supports Meteor 0.8.2 (with bcrypt) as well.
Example for the server side
try {
if (ApiPassword.isPasswordValid(username, password)) {
console.log('password is valid for this user');
} else {
console.log('password is not valid');
}
} catch (exc) {
console.log(exc.message);
// possible causes: 'User is not found', 'User has no password set'
}
I published a package for writing REST APIs in Meteor 0.9.0+ that supports authentication. It is meant to replace RestStop2 (the accepted answer) now that it is deprecated, and has a similar API:
https://github.com/krose72205/meteor-restivus
It was inspired by RestStop2 and built with Iron Router's server-side routing.
UPDATE: I just wanted to include a code example for anyone that finds this. This is the Restivus Quick Start example from the GitHub README:
Items = new Mongo.Collection 'items'
if Meteor.isServer
# API must be configured and built after startup!
Meteor.startup ->
# Global API configuration
Restivus.configure
useAuth: true
prettyJson: true
# Generates: GET, POST, DELETE on /api/items and GET, PUT, DELETE on
# /api/items/:id for Items collection
Restivus.addCollection Items
# Generates: GET, POST on /api/users and GET, DELETE /api/users/:id for
# Meteor.users collection
Restivus.addCollection Meteor.users,
excludedEndpoints: ['deleteAll', 'put']
routeOptions:
authRequired: true
endpoints:
post:
authRequired: false
delete:
roleRequired: 'admin'
# Maps to: /api/posts/:id
Restivus.addRoute 'posts/:id', authRequired: true,
get: ->
post = Posts.findOne @urlParams.id
if post
status: 'success', data: post
else
statusCode: 404
body: status: 'fail', message: 'Post not found'
post:
roleRequired: ['author', 'admin']
action: ->
post = Posts.findOne @urlParams.id
if post
status: "success", data: post
else
statusCode: 400
body: status: "fail", message: "Unable to add post"
delete:
roleRequired: 'admin'
action: ->
if Posts.remove @urlParams.id
status: "success", data: message: "Item removed"
else
statusCode: 404
body: status: "fail", message: "Item not found"