parse-community / Parse-SDK-JS

The JavaScript SDK for Parse Platform
https://parseplatform.org
Apache License 2.0
1.32k stars 596 forks source link

Any sample on loginWith #246

Closed zhouhao27 closed 5 years ago

zhouhao27 commented 8 years ago

I'd like to use third party login, for example, Facebook, Twitter. But not very sure how to call logInWith function. For example, my code:

 Parse.User.logInWith("Facebook",{})
      .then(user => {
        console.log(user);
      },error => {
        alert(error.message);
      });

But got the following error:

Cannot read property 'authenticate' of undefined

My other Parse function works fine. I'm using 1.8.1 JavaScript SDK.

zhouhao27 commented 8 years ago

By going through the source code for ParseUser testing code, I'm wondering do we need to write the Provider by ourselves?

flovilmart commented 8 years ago

Here's for a sample provider for Facebook:

https://github.com/ParsePlatform/Parse-SDK-JS/blob/1fe307c38edb4381023d7737821461fbc4bee74e/src/FacebookUtils.js

zhouhao27 commented 8 years ago

@flovilmart Thanks for your code. But I'm a bit confusing about this part. I'm wondering where the code for login to FB is in server side or client side? If it's in client side then we can call FB SDK directly without using parse-sdk-js, am I right?

And I saw some codes in authDataManager, what's this for ?

Can anybody explain the workflow of oauth (client side and server side), take Facebook as an example?

zhouhao27 commented 8 years ago

@benitech Agree with you. A sample will be a great help.

davidruisinger commented 8 years ago

I'm struggling here as well...

On the server I've configured the twitter oauth module as explained in the docs:

var parseApi = new ParseServer({
  ...
  oauth: {
    instagram: {} // From what I can see in the src, no options needed
  },
  ...

And on the JS Client side I'm trying to login like this (after getting an auth_token from instagram):

// Create a instagram provider
var provider = {
  authenticate(options) {
    if (options.success) {
      options.success(this, {
        auth_token: 'MY_AUTH_TOKEN'
      });
    }
  },

  restoreAuthentication(authData) {},

  getAuthType() {
    return 'instagram';
  },

  deauthenticate() {}
};

// Login the user with the instagram provider
var user = new Parse.User();
Parse.User.logInWith(provider, {}).then((user) => {
  console.log(user);
}, (error) => {
  console.log(error);
});

But I'm always getting back a 400 error:

ParseError {code: 252, message: "This authentication method is unsupported."}
flovilmart commented 8 years ago

If you remove Instagram from your Oauth hash in your config? What's it doing?

Le 8 avr. 2016 10:19 -0400, flavordaaavenotifications@github.com, a écrit :

I'm struggling here as well...

On the server I've configured the twitter oauth module as explained in the docs:

var parseApi = new ParseServer({ ... oauth: { instagram: {} // From what I can see in the src, no options needed }, ...

And on the JS Client side I'm trying to login like this (after getting an auth_token from instagram):

// Create a instagram provider var provider = { authenticate(options) { if (options.success) { options.success(this, { auth_token: 'MY_AUTH_TOKEN' }); } }, restoreAuthentication(authData) {}, getAuthType() { return 'instagram'; }, deauthenticate() {} }; // Login the user with the instagram provider var user = new Parse.User(); Parse.User.logInWith(provider, {}).then((user) =>{ console.log(user); }, (error) =>{ console.log(error); });

But I'm always getting back a 400 error:

ParseError {code: 252, message: "This authentication method is unsupported."}

— You are receiving this because you were mentioned. Reply to this email directly orview it on GitHub(https://github.com/ParsePlatform/Parse-SDK-JS/issues/246#issuecomment-207449657)

davidruisinger commented 8 years ago

You mean removing the whole

  oauth: {
    instagram: {} // From what I can see in the src, no options needed
  },

thing in the Parse Server configuration? Then I get exactly the same error...

flovilmart commented 8 years ago

Can you gimme the logs from your login attempt when running with env VERBOSE=1

davidruisinger commented 8 years ago
verbose: POST /api/users { host: 'localhost:1337',
  connection: 'keep-alive',
  'content-length': '186',
  origin: 'http://evil.com/',
  'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1',
  'content-type': 'text/plain',
  accept: '*/*',
  referer: 'http://localhost:8100/',
  'accept-encoding': 'gzip, deflate',
  'accept-language': 'en' } {
  "authData": {
    "instagram": {
      "auth_token": "MY_AUTH_TOKEN"
    }
  }
}
verbose: error: code=252, message=This authentication method is unsupported.

(I'm currently running the server AND my JavaScript App locally)

davidruisinger commented 8 years ago

I finally managed to get it working. I was missing the auth_data object which requires the ID. Here's my sample code:

// Create a instagram provider
var provider = {
  authenticate(options) {
    if (options.success) {
      options.success(this, {});
    }
  },

  restoreAuthentication(authData) {},

  getAuthType() {
    return 'instagram';
  },

  deauthenticate() {}
};

// Create the authData object
let authData = {
  authData: {
    auth_token: 'MY_AUTH_TOKEN',
    id: 123456 // IMPORTANT: This key is a must because pare-server checks for it
  }
}

// Login the user with the instagram provider
var user = new Parse.User();
Parse.User.logInWith(provider, authData).then(user => {
  console.log(user);
}, error => {
  console.log(ErrorConstructor);
});
benitech commented 8 years ago

Hi, would it be possible for you to break the code down into a server side and a client side part, so it is easier for other devs struggling with similar implementation. More like "Parse oAuth for Dummies" tutorial would be great.

As in - This is what you do on the server side in this "file"(/cloud/main.js or different?). This is where you add the keys and start Parse Server. And this is what you do on the client side SDK.

You would save a lot of people a lot of effort.

Thanks.

davidruisinger commented 8 years ago

@benitech The complete snippet above is client side. On the server side the only thing you need to do is configure parse-server to use your preferred oauth module (only possible with the open source parse server). In my case I just use instagram as already noted further above:

var parseApi = new ParseServer({
  ...
  oauth: {
    instagram: {}
  },
  ...

That's all.

zhouhao27 commented 8 years ago

@flavordaaave Where do you get the id and auth_token?

davidruisinger commented 8 years ago

@zhouhao27 Those you need to get on client side (e.g. using hellojs). Depending on what oauth provider you choose, you might need some different keys sent with the authData object. For instance the instagram providers needs to get an access_token besides the ID (NOT auth_token, I had this wrong in my previous posts) .

neekey commented 8 years ago

@flavordaaave thanks, your example make it much clearer for me now.

Quite agree with @benitech, the document is so unclear and without a good example of the workflow, I thought the OAuth was implementing from the server-side and so the configuration in here makes no sense to me.

@flavordaaave do you have any idea that if I can store the access_token passed to the server to be stored elsewhere for the user by myself?

neekey commented 8 years ago

I think if we make the OAuth implemented on the server-side it will be much clearer for both sides.

The server side can perform different OAuth types using plugins. And the client side doesn't need to worries about much of the difference between OAuth workflows, the only thing client side SDK need to implement is just a generic OAuth login workflow ( some generic UI, like open a new window for login ).

flovilmart commented 8 years ago

You're free to implement Oauth serverside, but most of the client SDK offer a seamless, clientside Authenticaton flow, which is better than the server danses, as well as implementing all different token formats, id fetch etc...

We'll consider a PR on parse-server for that.

Another benefit of client side OAuth is that it works wether you're developing on the simulator, localhost etc, without changing the server redirect or the provider configuration.

benitech commented 8 years ago

@neekey i feel the current implementation is quite proper from a security standpoint, though it would have helped if there was some initial documentation to begin with.

You will note that even in JS, the most popular NodeJS Auth library - PassportJS - does not have a proper social oauth with JWT token implementation.

Also as i mentioned in this comment to your question (https://github.com/ParsePlatform/parse-server/issues/2106#issuecomment-227090113), this is all you need to implement it both server and client side.

Definitely still needs cleaner docs.

neekey commented 8 years ago

@flovilmart I think you got a point about the benefit for Client OAuth, I haven't thought about that.

@benitech For security concern, I think client side of OAuth is apparently worse than server side since you have to pass your "access_token" through the network. And some of the Platform like LinkedIn and Github, they support only OAuth1 or OAuth2 with Explicit Grant, the authentication flow needs to be signed with a secret key that may not be exposed in the browser. The popular client-side OAuth library HelloJS gets around this problem by using their proxy server, which means you need to sign up your application info into its server, I am not doubting the author's intention, but relying on another server to implement an OAuth sounds just not good.

And I don't think implementing an Auth process on server will be a problem, it's not perfect for PassportJS to do this is maybe because it wants to cover all the platforms.

Anyway, just some basic thoughts about this issue, quite new here, will learn more about parse-server : )

benitech commented 8 years ago

@neekey the main purpose of the server side implementation is to re-authenticate the "token" and do seamless integration into Parse SDK functions. Sending token etc is immaterial cos of https.

You may be right. I just have not seen a better implementation yet that works across iOS, Android and JS(?).

EDIT: My comments are solely based on the Android /iOS implementation. For JS, yes, it should definitely be server side ONLY.

As an example, using @flovilmart 's gist for iOS Twitter implementation and using Fabric.io SDK plugin, you could have the Twitter oauth working both client / server side in less than 30 mins.

flovilmart commented 8 years ago

The oAuth process is split in 2 parts,

At this time, I don't believe this is the responsibility of the client SDK, nor parse-server to acquire this token. The second part, is already implemented and it makes sense for many reasons to keep it that way.

On the 1st part, as many of you mention, passport is doing a great job at it, and not all oAuth require serverside redirects. Having the auth_token in the client is no more insecure than basic authentication. He insecure part is storing that token in clear in mongo, but we don't have much choice here as this token is not only used for identity verification but also interacting with the services.

I agree that the documentation for the client side auth handling could be better, the gist I provided could be improved, but implementing passport in parse-server don't seem to be the right solution at that time as you can very easily integrate it if you need it.

That being said, I'm all open for improvements.

PS: In the past 5 years, I've build many social logins, and never relied on passport to do so, implementing a generic serverside oAuth handler is pretty straightforward.

AndrewLane commented 8 years ago

So you can (currently) use loginWith for anything auth aside from Facebook right now?

benitech commented 8 years ago

@AndrewLane you can use any of the following or create your own.

https://github.com/ParsePlatform/parse-server/tree/master/src/authDataManager

AndrewLane commented 8 years ago

Thanks. Does unlink work? I was having success with link but not unlink.

On Thursday, August 4, 2016, benitech notifications@github.com wrote:

@AndrewLane https://github.com/AndrewLane you can use any of the following or create your own.

https://github.com/ParsePlatform/parse-server/tree/master/src/ authDataManager

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ParsePlatform/Parse-SDK-JS/issues/246#issuecomment-237743887, or mute the thread https://github.com/notifications/unsubscribe-auth/AAdQAGnwwLAXt9CrMOVLXZzU-FlYurqNks5qcqfigaJpZM4H7cBk .

jonas-db commented 7 years ago

This is still not clear at all. How do we have to use loginWith together with Twitter, Google etc. On the server-side there are several oauth managers (https://github.com/ParsePlatform/parse-server/wiki/OAuth) which seem to do the required work. On the client-side the only documented and available methods are focussed on Facebook (e.g. FacebookUtils), but there are no pointers on how to use other services.

What are the exact steps on the client-side using the SDK to login with other services? Why is there no TwitterUtils for example?

junal commented 7 years ago

@jonas-db Did you find any solution for other services?

jonas-db commented 7 years ago

@junal I ended up using hello.js (https://adodson.com/hello.js/) to obtain a token from the specific provider (Twitter, Google, etc) and passed that auth_token and auth_token_secret to Parse.User.logInWith(twitterProvider, { authData : authData })

where twitterProvider is an instance of:

export default {
  authenticate(options) {
    if (options.success) {
      options.success(this, {});
    }           
  },

  restoreAuthentication(authData) {
    console.log(arguments)
  },

  getAuthType() {
    return 'twitter';
  },

  deauthenticate() {}
};

Hope it helps

flovilmart commented 7 years ago

@jonas-db that's a very nice solution, can you make a PR on the docs repository as an example on the JS docs? That could be slick

junal commented 7 years ago

@jonas-db @flovilmart Thank you so much! Here's my full code. I'm getting "This authentication method is unsupported." error. Where am I making mistake?

    var accessToken = "....";
    var id = "....";
    var provider = {
        authenticate(options) {
            if (options.success) {
                options.success(this, {});
            }
        },
        restoreAuthentication(authData) {},
        getAuthType() {
            return 'facebook';
        },
        deauthenticate() {}
    };
    let authData = {
        authData: {
            id: id,
            access_token: accessToken
        }
    };
    Parse.User.logInWith(provider, authData).done(function (result) {
        console.log(result);
    }).fail(function (error) {
        console.log(error);
    });
ahmedengu commented 6 years ago

Hi @junal i was able to get your code snippet working by setting: options.success(this, {}) to options.success(this, authData) also i set the username to instagram username to prevent random username from being set to new users

    var accessToken = "....";
    var id = "....";
    var username="...."; // instagram username
    let authData = {
        authData: {
            id: id,
            access_token: accessToken
        }
    };
    var provider = {
        authenticate(options) {
            if (options.success) {
                options.success(this, authData);
            }
        },
        restoreAuthentication(authData) {},
        getAuthType() {
            return 'instagram';
        },
        deauthenticate() {}
    };
    let user = new Parse.User();
    user.set("username", username);
    user.logInWith(provider, authData).done(function (result) {
        console.log(result);
    }).fail(function (error) {
        console.log(error);
    });

parse config:

var parseApi = new ParseServer({
  ...
  oauth: {
    instagram: {}
  },
  ...
stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.