silverstripe / silverstripe-graphql

Serves Silverstripe data as GraphQL representations
BSD 3-Clause "New" or "Revised" License
52 stars 61 forks source link

OAuth-based Authentication #6

Open chillu opened 8 years ago

chillu commented 8 years ago

Session-based authentication relies on a manual mechanism for signing in (CMS login form). While we can already use programmatic authentication with Basic Auth over SSL, it's not very friendly to typical third party integrations. Implement an OAuth provider to make this more flexible, e.g. with http://oauth2.thephpleague.com/. It's unclear which flow we'd use (since we don't want any further user action beyond logging in to the session-based CMS).

Related: https://medium.com/the-graphqlhub/graphql-and-authentication-b73aed34bbeb#.ys9c7gc32 https://dev-blog.apollodata.com/a-guide-to-authentication-in-graphql-e002a4039d1 https://dev-blog.apollodata.com/auth-in-graphql-part-2-c6441bcc4302 http://graphql.org/graphql-js/authentication-and-express-middleware/

robbieaverill commented 7 years ago

@chillu I'm happy to help out with this if you need some extra resource.

Re: the two auth flows - perhaps you could distinguish the authentication method from a request header. Either match the OAuth header or a custom SS session header.

chillu commented 7 years ago

@robbieaverill Thanks for your offer, we can absolutely use the help! :)

At the moment, the GraphiQL browser with the SilverStripe Content API will return empty results filtered by canView(). A quick fix for this would be allowing basic auth passed in through HTTP headers - which is still a bit finnicky since you need to base64 encode them ready for the HTTP header (can't set user and password separately in GraphiQL). That's more of a missing feature in GraphiQL though, and basic auth is good enough for development work. Plus it already works out of the box in SilverStripe.

I don't think we need a viewer root node, but maybe I haven't understood the concept completely :)

Regarding an OAuth 2.0 server, this would implicitly add OAuth support to SS core, which is a fairly large security surface - and hence would need some form of RFC. Ideally we'd avoid running an XHR from the client for this, but rather auto-generate a token on the server, stored against the member. I'd rather not reuse the "remember me" token for this. It would have to be removed on logout, so a simple 1:1 relationship with sessions. And should have an expiry date which matches the PHP session. A separate module might also allow multiple API keys (and a UI to manage them), but that's out of scope for now.

The Oauth 2.0 server implementation should be a separate module, which can be applied through the SilverStripe\Control\RequestFilter middleware - so the GraphQL module should only need some configuration to apply it to the /graphql endpoint.

Apollo authorisation is easy through middleware.

Note that there's https://github.com/bigfork/silverstripe-oauth, which is an OAuth client, not server.

Does that give you enough to get started Robbie? This is really about "OAuth server in SS" more than in silverstripe/graphql.

chillu commented 7 years ago

As a simple start, you could just describe how to add basic auth to GraphiQL in the README?

robbieaverill commented 7 years ago

Hey @chillu - sounds good, just a couple of things to clarify:

So the context for this is authenticating frontend calls to the GraphQL endpoints from within a SilverStripe site - i.e. a decoupled web app, rather than external applications accessing a SilverStripe GraphQL endpoint?

Ideally we'd avoid running an XHR from the client for this, but rather auto-generate a token on the server, stored against the member.

You're talking about a sort of pre/automatically authorized OAuth token set which the frontend can use to communicate with the backend? Seems sensible to me. It could also be augmented at a later date if required to provide full OAuth support for SilverStripe.

As a simple start, you could just describe how to add basic auth to GraphiQL in the README?

I agree - this would be worth doing to start with to establish the authentication integration process and ensure that it works. Then chuck something a bit meatier over the top.


I assume there's not a huge rush for this, but I'll work on this a little over the break and will aim to have it complete when we get back in.

Cheers!

chillu commented 7 years ago

So the context for this is authenticating frontend calls to the GraphQL endpoints from within a SilverStripe site - i.e. a decoupled web app, rather than external applications accessing a SilverStripe GraphQL endpoint?

Both. Dev will want to test their queries in external tools like GraphiQL.

You're talking about a sort of pre/automatically authorized OAuth token set which the frontend can use to communicate with the backend?

Yes, so for the SS4-internal authentication we'd only use one particular flow (auto generated token). I wouldn't start the OAuth implementation from scratch though, even for this relatively simple use case - I hope that the phpleague module would work for us, but needs more research since it'll add yet another core dependency (amount of code pulled in, amount of libraries)

I assume there's not a huge rush for this, but I'll work on this a little over the break and will aim to have it complete when we get back in.

Yep, we have a working solution at the moment (session-based), and you can use dev/graphiql built into the silverstripe/graphql module for running queries.

robbieaverill commented 7 years ago

Status

44 introduces an AuthenticatorInterface and a BasicAuthAuthenticator which can be enabled via YAML configuration to authenticate via the native SS BasicAuth::requireLogin method, and provide a Member to GraphQL for use within query contexts.

The interface is simple and easy enough to add custom authenticators to (dev only example).

I have made a start on implementing the PHP League OAuth2 server, however it requires PSR-7 compatible request and response interfaces, which SilverStripe doesn't have at the moment (related: silverstripe/silverstripe-framework#4484, mailing list discussion).

I spent a bit of time working on a HTTPResponse adapter that implements the appropriate PSR-7 interfaces, but it is a big job and I'm not sure I'll have time to finish it in a reasonable timeframe outside of work commitments.

The actual implementation of the OAuth2 server shouldn't take too long since it comes with traits that provide most of the standard functionality - it's mostly just a case of connecting the interfaces repositories and entities to the appropriate SilverStripe DataObjects.

I'll keep the work I've done somewhere local and can chip away at it gradually, but perhaps we should roll-our-own OAuth based token authenticator in the meantime...?

cc @chillu / @sminnee

sminnee commented 7 years ago

💯 ❤️ for a PSR-7 bridge

robbieaverill commented 7 years ago

I've put together a simple PSR-7 adapter set, so the OAuth implementation seems to be coming along a bit better now.

@sminnee's API key module might be worth upgrading for SS4 and implementing in the meantime too.

robbieaverill commented 7 years ago

sminnee/silverstripe-apikey#8 has a compatible authenticator added for GraphQL.

Merged: this adds a lightweight option for token based authentication in this GraphQL module until OAuth is available. cc @PapaBearNZ

maxime-rainville commented 4 years ago

Looks like other people already had a go at doing an integration with PHP League OAuth2 package https://github.com/advanced-learning/silverstripe-oauth2-server

I had a got at getting it running locally last night, but it's targeted to GraphQL 1, so it might need a bit of refactor to work with our latest version.