I'm trying to do the custom authentication, but got confused through the insufficient docs.
http://docs.parseplatform.org/parse-server/guide/#custom-authentication
Here is my cloud code:
const authmyapp = require('./authmyapp')
Parse.Cloud.define('sign', function(req, res) {
const user = new Parse.User()
const authData = {
myapp: {
module: authmyapp,
openid: 'test',
},
}
user._linkWith('weapp', authData).then(userInfo => {
res.success(userInfo)
})
})
And to eliminate interference, I made the auth logic like this:
// authmyapp.js
function validateAuthData(authData) {
return Promise.resolve()
}
function validateAppId() {
return Promise.resolve()
}
module.exports = {
validateAppId,
validateAuthData,
}
A new user signed up with my custom auth-info linked.
When running this cloud code, it threw this error on the log:
error: Error generating response. TypeError: Cannot read property 'authenticate' of undefined
at ParseUser.value (***/node_modules/parse-server/node_modules/parse/lib/node/ParseUser.js:170:18)
at ***/cloud/main.js:18:8
at ***/node_modules/parse-server/lib/Routers/FunctionsRouter.js:176:9
at new Promise (<anonymous>)
.....
Correct me if I did something wrong. Thanks. And actually the docs lack many example code to help us figure out.
I think you should try to config custom authentication module in the ParseServer config when initialising it (instead of put it in the authData):
new ParseServer({
appId: {your-app-id},
...
auth: {
'weapp': {module: authmyapp}
}
})
This makes ParseServer recognise the custom auth method and invoke the custom validation logic when doing user. _linkWith ('weapp', authData)
@6thfdwp
Hi. Thank you! I found the doc on the README.md in this repository (https://github.com/parse-community/parse-server#advanced-options). And I've tried what you said while still getting the same error output.
Here's my config:
const authmyapp = require('./utils/authmyapp')
new ParseServer({
appId: 'myapp',
...
auth: {
weapp: {
module: authmyapp,
option1: 'openid',
},
},
})
And in the cloud code:
Parse.Cloud.define('sign', function(req, res) {
const user = new Parse.User()
const authData = {
weapp: {
openid: 'test',
},
}
user._linkWith('weapp', authData).then(userInfo => {
res.success(userInfo)
})
})
BTW, I tried to understand the source code of Parse.User._linkWith in the JS-SDK:
https://github.com/parse-community/Parse-SDK-JS/blob/a209282de1a2106ce8453cb3c985c77cee678304/src/ParseUser.js#L30
var authProviders = {};
_linkWith(provider: any, options: { authData?: AuthData }): ParsePromise {
var authType;
if (typeof provider === 'string') {
authType = provider;
provider = authProviders[provider]; // this line causes errors
} else {
authType = provider.getAuthType();
}
So, authProviders is set to an empty object, and there seems not to be any evidence that makes it changed. It should be undefined absolutely? Where do we inject our custom auth config into the object? Did I miss anything or do something wrong? Please give me some advice. Thanks a lot!~
In the JS SDK , you need to register an authenticationProvider聽 through: https://github.com/parse-community/Parse-SDK-JS/blob/a209282de1a2106ce8453cb3c985c77cee678304/src/ParseUser.js#L757
See also an example here: https://github.com/parse-community/Parse-SDK-JS/blob/a209282de1a2106ce8453cb3c985c77cee678304/src/FacebookUtils.js#L138
OK, by looking at your error log again, it's from the Parse client, not server. The ParseUser _linkWith second parameter expects an options object which has authData as its key. So it will look like:
user._linkWith('weapp', {authData})
If it cannot find this key, it will ge to this else block: ParseUser.js#L102
BTW, you could directly use static method on ParseUser, ParseUser.loginWith which essentially call internal _linkWith
From @flovilmart comment above, my understanding is linkWith logic is to mainly look at if options already got authData, if it's true, it will be directly sent to Parse server, otherwise it tries to look up the registered authProvider, call authenticate to get authData first. It fails in this case as we don't have one
Yes, you just need to register a simple one, that implements the same methods as the Facebook one. If you鈥檙e passing the authData directly, you may not need to implement the body of all methods, but probably just the getAuthType() that returns your authProvuder name (weapp)
Thank you guys!
Having looked your explanations and examples, I made my code like this (with the above auth config in the ParseServer Initialization kept):
...
const user = new Parse.User()
const authData = {
weapp: {
openid: 'test',
},
}
const provider = {
authenticate(options) {
options.success(this, {
openid: 'test',
})
},
getAuthType() {
return 'weapp'
},
}
user._linkWith(provider, authData).then(userInfo => {
res.success(userInfo)
})
...
where I only need to passing the authData directly.
But the error log said
Error generating response. ParseError {
code: 252,
message: 'This authentication method is unsupported.' }
After searching for the error generator:
https://github.com/parse-community/parse-server/blob/11c40dce97da72271e362e50016500e9f3e3a1ec/src/RestWrite.js#L230
I realized that I need to changed the 'openid' to 'id'. And it works. But I'm not sure if it's a good way.
And here's something I still don't understand:
_registerAuthenticationProvider(provider) if I want to make a one-time initialization?(Like what you said to 'make the first parameter a string key'.) Do I need to write a file like FacebookUtils.js and import it to some config?authData) in _linkWith used for if I've got the provider.authenticate({ success }) { success(this, authData) } in the first parameter to give the auth data? (If I keep the second an empty object, it works as well. But I can't keep it undefined, otherwise it throw a error.)You should out the registerProvider alongside the Parse.initialize call.
If your provider is providing the authData, then the second parameter is not read nor used. You can safely ignore it.
We don鈥檛 have any plans to update the documentation at the moment, but yes, it could be better.
On my side i鈥檒l Try to update the JS SDK so it鈥檚 easier and self documenting. But you can very well open a PR on the docs repository :)
@flovilmart Also, how are you setting up the username during oAuth. I see a random username generated. Any support for customizing username? Or should we just let it be and use a different field like appUsername all together?
Once the user is logged in with the 3rd party auth, you鈥檙e free to set the username to whatever you want, i鈥檇 Like to keep it in 2 steps as they both are different flows that may conflict. The login with Facebook for example can either create a new user or pull one existing in the DB. And usually at login time, you are unlikely to have prompted your user for a username or an email.
What would be your use case?
Well my requirements are like this:
authDataData from oAuth provider.username field from the user.logInWith and then become followed by re-writing username field and then finally followed by a save.The issue with this approach is that any failure will happen very late in the process, at the last step as you鈥檒l gather all infos befor making a single call to your API. Which can lead to hard to debug scenarios. You should probably just do the logIn first, calling parse-server to ensure the auth data is valid, then try to set the username and save immediately, if the username is already taken, you can prompt for another one.
Cool. I was at this. Thanks.
@flovilmart
Why is there a check that the authData object has a key named id?
Shouldn't it be up to the developer of a custom auth provider to handle whatever is passed in the authData object? I've struggled with this for quite some time and eventually stumbled upon this issue. Haven't found anything in the documentation on this. I would happily make a pull request. But first I wonder if:
A. The check should be removed?
B. The check should be left in the code but clarified in the documentation. And if so, why it there.
I have no opinion on this. Check with the maintained of this project, open a new issue or a pull request (I guess)