Parse-server + Auth0 integration
At AvoGenie.com we are currently building an iOS client that rest on top of a combination of technologies such as firebase and parse-server. As such I had to figure out how to authenticate users in a way that allows for easy integration with external services such as firebase, parse-server and layer.com .
I decided to use auth0.com to handle the user management for now, because they have a very nice iOS SDK and have endpoints for issuing JWT tokens through an API call. However, the issue with the parse-server is that I didn’t want to write a custom oAuth integration but somehow needed a way to establish a user identity on our parse-server that can be referenced to from auth0.
After numerous days of scouring the internet for solutions I realised that there is no definitive guide on how to do this, but eventually I figured it out.
The Guide
I am assuming that you have your parse-server running on heroku.com (I guess other providers should behave the same) and that you setup auth0.com to properly work with your iOS app (or other tech, they have pretty decent guides on most things). The important thing is that a user needs to be able to log in and show up in the auth0 dashboard.
Now the trick to get auth0 to work with parse-server is to create this rule.
function (user, context, callback) {
// run this only for the Parse application
// if (context.clientID !== ‘PARSE CLIENT ID IN AUTH0’) return callback(null, user, context);var PARSE_APP_ID = ‘xxx’;
var PARSE_API_KEY = ‘xxx’;
var PARSE_USER_PASSWORD = ‘xxx ‘; // you can use this to generate one http://www.random.org/strings/var username = user.user_id; // this is the Auth0 user prop that will be mapped to the username in the db
// var username = user.email || user.name || user.user_id;
var email = user.email;
request.get({
url: ‘https://secret-coast-23424.herokuapp.com/parse/login',
qs: {
username: username,
password: PARSE_USER_PASSWORD
},
headers: {
‘X-Parse-Application-Id’: PARSE_APP_ID,
‘X-Parse-REST-API-Key’: PARSE_API_KEY
}
},
function (err, response, body) {
if (err) return callback(err);// user was found, add sessionToken to user profile
if (response.statusCode === 200) {
user.parse_session_token = JSON.parse(body).sessionToken;
return callback(null, user, context);
}// Not found. Likely the user doesn’t exist, we provision one
if(response.statusCode === 404) {
request.post({
url: ‘https://xxx.herokuapp.com/parse/users/',
json: {
username: username,
password: PARSE_USER_PASSWORD,
email: email,
emailVerified: true
},
headers: {
‘X-Parse-Application-Id’: PARSE_APP_ID,
‘X-Parse-REST-API-Key’: PARSE_API_KEY,
‘Content-Type’: ‘application/json’
}
},
function (err, response, body) {
if (err) return callback(err);// user created, add sessionToken to user profile
if (response.statusCode === 201) {
user.parse_session_token = body.sessionToken;
return callback(null, user, context);
}
return callback(new Error(‘The user provisioning returned an unkonwn error. Body: ‘ + JSON.stringify(body)));
});
}
else
{
return callback(new Error(‘The login returned an unkonwn error. Status: ‘ + response.statusCode + ‘Body: ‘ + body));
}
});
}
Since this is the hardest part I will explain the rule piece to piece.
function (user, context, callback) {
This function is called whenever a user logs in or signs up. Generally whenever you are calling an API from the auth0 SDK that concerns logging in users. The user parameter contains the information of the user that executed the login. For example, if Bob creates an account on an iOS client with the email: bob@bob.com and name: bob bob, the user parameter will contain those two variables under user.email and user.name. The user object carries more information such as a uid generated by auth0.
The context object contains information about the context in which the sign up was executed. For example, the device type.
And the callback is a function that takes 3 parameters, an error object, the user object and the context. This callback gets returned to auth0 so that the system can handle the user further.
The first step of the rule is to check whether a user exists by simply trying to log in as the user. Since auth0 handles the user management I give the same password to all my parse users, hence this won’t work for existing parse users. If the user exists we get a 200 status code. Then the body of the response contains a sessionToken issued by the parse-server. We take this token and append it to our user, as this user will be returned to our iOS client when the login API call succeeds.
If the user doesn’t exist we get a 404 status code and assume that we need to create the user. Here the trick is not to use any custom domain because the parse-server doesn’t fully work then. Use the heroku domain. If the user was created successfully we get a 201 and can append the sessionToken to our user.
On an iOS client we would have called the method “onAuthenticationBlock()” and it takes a closure that will get called with 2 parameters. The profile and the token. The profile variable will contain the parse_session_token which you can then use to call PFUser.becomeInBackground(). Pass the the parse_session_token to that method and you will log in the correct user and the currentUser will be set on the iOS client.
I hope you enjoyed reading this quick explanation. If you would like to follow our progress at avogenie.com follow me here on medium. Also I will keep posting these explanation for problems that I faced.
Next article will be about the delegation api from auth0 and using it with firebase and layer.com.
If you liked this, consider following me on Twitter and Facebook.
Lastly, if you need help, post a comment or tweet me and I will gladly help.