Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API for oAuth plugin #75

Open
zoepage opened this issue Jul 10, 2015 · 18 comments
Open

API for oAuth plugin #75

zoepage opened this issue Jul 10, 2015 · 18 comments

Comments

@zoepage
Copy link

zoepage commented Jul 10, 2015

I'd love start the discussion on the oAuth plugin including scope, dependencies and API.
What are your thoughts already?

  • server (e.g. hapi plugins, what do we need to implement / refactor?)
  • dashboard (which credentials do we need to add? which services? store with hoodie.account.set("twitter") )
  • frontend (how should we interact with the frontend / API?)

How much will this plugin depend on the new https://github.com/hoodiehq/hoodie-account-server/tree/api?

repo: https://github.com/hoodiehq/hoodie-plugin-oAuth

docs:
Github: https://developer.github.com/v3/oauth/
Twitter: https://dev.twitter.com/oauth/overview/introduction
Facebook: https://developers.facebook.com/docs/facebook-login/login-flow-for-web/v2.4
Google: https://developers.google.com/identity/protocols/OAuth2

cc @janl @christophwitzko @espy @boennemann @gr2m

@espy
Copy link
Member

espy commented Jul 10, 2015

I may be misremembering this, but I think what we discussed… 2 years ago 0_o was something like:

  1. install the plugin

  2. in the admin panel, select which auth providers you want via checkboxes, so the server can show you the callback endpoints

  3. register your app with the oauth providers and enter the endpoint you got from the admin panel

  4. in the frontend, do

    hoodie.account.signInWith('github').done(function(response){
      console.log('oauth token: ', response.token);
    });
    
  5. auth window pops up, user auths, promise gets fulfilled, client has auth token, can do requests with it.

That's what we want, right?

@gr2m
Copy link
Member

gr2m commented Jul 10, 2015

Ideally, you wouldn't need to handle any tokens yourself.

hoodie.account.signInWith('github').done(function(username){
  console.log('Hey there, ', username)
})

And options can be passed as a second parameter, to request only a certain scope

hoodie.account.signInWith('github', {scopes: ['user:email']}).done(function(username){
  console.log('Hey there, ', username)
})

@espy
Copy link
Member

espy commented Jul 10, 2015

Ok, but it definitely should be available in the user object in case the dev wants to do anything with it in the client, for whatever reason.

Anway, the two main contenders for hapi libraries to build this upon seem to be:

  1. Grant (or rather, grant-hapi), has lots of providers (100+)
  2. Bell, is maintaned by hapi

(Also updated my previous post with the provider registration step)

@zoepage
Copy link
Author

zoepage commented Jul 14, 2015

@janl you mentioned, we already decided which hapi plugin we want to use?

@zoepage
Copy link
Author

zoepage commented Jul 14, 2015

We'll use Bell.

@christophwitzko and I talked the flow through.

  1. The user in the app wants to signInWith('github'). The button has a bind of the plugin method.
  2. We have checkboxes in the pocket pocket (Admin Dashboard Frontend) with possible services, where you also can add the app_token and secret after you registered your application for a specific service.
  3. We are making a request to the service_auth_server with the user_id and optional other parameters.
    The most important param here would be the callback URL we'll generate out of app_domain/dynamic_user_id/ so we can get the callback here and just attach the params in the callback from the auth_server. Because of leaving the application, we're losing scope. So we can't just fire a callback / promise. We need to solve this in another way and this is the solution we came up with.
  4. The service_auth_server magic happens here and the params get attached to callback URL, so we get app_domain/dynamic_user_id/params back.
  5. We can set the params like auth_token and secret to hoodie.account.user.set('github')
    The set is an internal method. We are still thinking about making the get external. Would it make sense or would we write a wrapper maybe? (especially @gr2m?)
  6. fire back a callback and reload on success or show an error including a message for the most common errors (like wrong credentials).

imag4799

We came up with some suggestions for hoodie.account.user JSON scheme, because we have 3 ways to verify the application.

  1. hoodie.account.signIn(usr, pwd) or hoodie.account.signUp(usr, pwd, pwd2)
    This works as we already know.
  2. hoodie.account.signInWith(service)
    Here the user is logged in already and can connect a service to the existing account.
    We would just save the service credentials to the user account (like auth-token and secret).
  3. hoodie.account.signInWith(service)
    Here it's the very first sign-up from the user including the creation of the account and verification by a service. It would be nice, if we could save more than just the auth-token and the secret here, e.g. the name, the e-mail address, user_id from the service etc.

We thought about also saving things like name or e-mail within the accout.user obj, if the signUp is initial and the name is undefined.

imag4800

@espy
Copy link
Member

espy commented Jul 14, 2015

Maybe we want signUpWith() for the last example, since it's possible that some apps (like ubersicht) would be fine with just oAuth. So signUpWith() would create a new user object and could also take custom user data.

@gabrielmancini
Copy link

we can get some ideas from here: https://oauth.io/home

@gr2m
Copy link
Member

gr2m commented Jul 14, 2015

Because of leaving the application, we're losing scope

Can't we open it in a new window, so that we can inform the host about error / success? Does it need to be a redirect?

@gr2m
Copy link
Member

gr2m commented Jul 14, 2015

The .set() is an internal method

See the dream api for .get and .set and how to restrict them on a property level over at https://github.com/hoodiehq/hoodie-account-server/tree/api#custom-user-data

Basically, we could define user accounts using JSON Schema, with an additional "access" property, see https://github.com/hoodiehq/hoodie-account-server/tree/api#configuring-the-server

@gr2m
Copy link
Member

gr2m commented Jul 14, 2015

hoodie.account.signUp(usr, pwd, pwd2)

it's hoodie.account.signUp(usr, pwd). Checking password confirmation is app specific, out of scope for hoodie

@gr2m
Copy link
Member

gr2m commented Jul 14, 2015

We thought about also saving things like name or e-mail within the accout.user obj, if the signUp is initial and the name is undefined.

Yeah, I wonder if this is app-specific, or if we can pre-configure on what to do with it. We could also store meta data coming from 3rd parties like GitHub, Facebook in special user properties?

If I don't have a an account yet, and I do hoodie.account.signInWith("github", options), a user account needs to be created in our app, so we need to set the username from something, right? I wonder if

How about something like this?

hoodie.account.signInWith("github", {
  // available properties for each provider should be documented somewhere :)
  matchProperties: {
    username: 'email',
    fullname: 'name',
    avatarUrl: 'avatar'
  },
  // other options
})

?

@gr2m
Copy link
Member

gr2m commented Jul 14, 2015

Here the user is logged in already and can connect a service to the existing account.
We would just save the service credentials to the user account (like auth-token and secret).

That is very interesting, I haven't thought about this use case yet, I thought it might be out of scope. I thought we only think about signing the user in, without any background integrations.

But thist makes totally sense, and we should think it trough. I guess we need to somehow expose them to plugins? E.g. I could imagine to have a plugin that checks all user accounts every few hours, and if they are "connected" to dropbox, I'd load their data and back it up to dropbox? Something like that? Maybe we should discuss the back-end / plugin api as well?

@zoepage
Copy link
Author

zoepage commented Jul 16, 2015

Can't we open it in a new window, so that we can inform the host about error / success? Does it need to be a redirect?

Not found another solution for this yet.
Where are the issues you see in the solution we came up with?

it's hoodie.account.signUp(usr, pwd). Checking password confirmation is app specific, out of scope for hoodie

cool!

We thought about also saving things like name or e-mail within the accout.user obj, if the signUp is initial and the name is undefined.

Yeah, I wonder if this is app-specific, or if we can pre-configure on what to do with it.

I think, we could do this for signInWith() when we don't have an account already. Here a second method signUpWith() would make sense, so we would strictly differentiate the behavior.

This would mean:
signInWith('github') -> usual signIn behavoir
signUpWith('github') -> adding additional information to general user account

hoodie.account.signInWith("github", {
  // available properties for each provider should be documented somewhere :)
  matchProperties: {
    username: 'email',
    fullname: 'name',
    avatarUrl: 'avatar'
  },
  // other options
})

+1 on that

But thist makes totally sense, and we should think it trough. I guess we need to somehow expose them to plugins? E.g. I could imagine to have a plugin that checks all user accounts every few hours, and if they are "connected" to dropbox, I'd load their data and back it up to dropbox? Something like that? Maybe we should discuss the back-end / plugin api as well?

For the schema we thought about:

hoodie.account.signInWith("github", {
  // available properties for each provider should be documented somewhere :)
  service: {
    github : {
      username: 'email',
      fullname: 'name',
      avatarUrl: 'avatar'
    },
    facebook: {
      username: 'email',
      fullname: 'name',
      avatarUrl: 'avatar'
    } ...
  },
})

We should generalize the service data, we want to store.

@gr2m
Copy link
Member

gr2m commented Jul 16, 2015

I'm still not convinced that we need both, signInWith and signUpWith. From an app user perspective, here's a typical example from buffer.com

screen shot 2015-07-16 at 15 34 24

There is only one button to sign in with Twitter / Facebook / LinkedIn. And that buttons works no matter whether the account exists already or not.

signInWith('github') -> usual signIn behavoir
signUpWith('github') -> adding additional information to general user account

I'd only take information from github when the account is created initially, not when it already exists. So again, I think only one .signInWith(provide, options) method will suffice.

@espy
Copy link
Member

espy commented Jul 17, 2015

Ok, I'm re-convinced that signUpWith() might not make any sense, sorry for introducing confusion :D We'll still be able to pass in user data with signInWith(), so that should be enough.

Adding custom data to an oAuth user would have to be a two-step process anyway, first auth, then add custom data, and at that point the app logic can decide whether to even show the inputs for the custom data based on whether it exists in the user doc already. So that should work.

@zoepage
Copy link
Author

zoepage commented Jul 17, 2015

... the inputs for the custom data based on whether it exists in the user doc already. So that should work.

I don't think, we should have any inputs for the custom service data. I think a getter would be enough for that.

@espy
Copy link
Member

espy commented Jul 17, 2015

Absolutely, inputs would be app logic too, was just an example :)

@zoepage
Copy link
Author

zoepage commented Jul 17, 2015

Cool :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants