npm package: @sgrove/onegraph-apollo-server-auth
Description: Auth helpers for apollo server using OneGraph AuthGuardian
Latest version: 2.0.3-alpha-2

## OneGraph Auth for Apollo Server

You can use AuthGuardian by OneGraph to handle all of your authentication and
permission needs in your Apollo server.

### Just give me an example project to try!
Good point! We have a basic [example apollo-server app with OneGraph
AuthGuardian](https://github.com/oneGraph/onegraph-apollo-server-auth-example)
you can clone and try out! Just clone the repo, follow the instructions, and
you'll have a fully secured GraphQL API!

### At a glance
AuthGuardian is the easiest way to allow someone to access your API based on
information in GitHub, Salesforce, Gmail, etc. It lets you graphically configure
sophisticated rules that will run every time a user logs in to any service
(GitHub, Salesforce, Quickbooks, etc.) via OneGraph and will produce a JWT that
contains everything you need to know *who* a user is and *what* they're allowed
to do in your API.

Each rule consists of two parts: conditions and effects.

An example of a rule condition might be,

> "When this user is a member of organization X on GitHub AND this user has made at least one commit to the repository repo-owner/repo-name".

OneGraph knows how to query each of the services to find out if the condition
has been met automatically!

When all of the conditions for a rule are met, the effects are run. An effect
might be:

> Set the `user.id` to the user's GitHub user-id, and add "admin" to the list of `user.roles`

So the full rule would read:

> "When this user is a member of organization X on GitHub AND this user has made at least one commit to the repository repo-owner/repo-name".
> Then set the `user.id` to the user's GitHub user-id AND add "admin" to the list of `user.roles` AND add a Netlify role of "developer"

If this rule passed after a user logged in via GitHub, OneGraph would generate a
full, signed JWT for use in your GraphQL resolvers:

```
{
  "iss": "OneGraph",
  "aud": "https://serve.onegraph.com/dashboard/app/00000000-0000-0000-0000-000000000000",
  "iat": 1566594200,
  "exp": 1566680600,
  "user": {
    "id": 35296,
    "roles": [
      "admin"
    ]
  },
  "app_metadata": {
    "authorization": {
      "roles": [
        "developer"
      ]
    }
  }
}
```

### Using the OneGraph JWT in Apollo Server
1. First, install the auth package:

```
npm install --save onegraph-apollo-server-auth
# or 
yarn add onegraph-apollo-server-auth
```

2. Use the custom directives
There are two customer directives implemented by `onegraph-apollo-server-auth`, `@isAuthencated` and `@hasRoles`:

```
directive @isAuthenticated on QUERY | FIELD_DEFINITION
directive @hasRole(oneOf: [String!]) on QUERY | FIELD_DEFINITION
```

#### @isAuthenticated
Any field that has this directive added to is will always check in the JWT that
a value is present at `user.id`. If not, the user has not authenticated (that
is, has not logged into any service), and the field will return null, and an
error message will be added to the response.

```
type Query {
  companies : [Company] @isAuthenticated
}
```

#### @hasRole
Any field with this directive added will check in the JWT at the path
`user.roles` to make sure that the user has been granted a role that's required
to view this field.

```
type Company {
  id: String!
  name: String @hasRole(oneOf: ["visitor"])
  accountBalance: Int! @hasRole(oneOf: ["admin"])
}
```

In this case, any user will be able to query for Company `id`s, but the
AuthGuardian rules must have granted this user the `visitor` role to view the
Company `name`, and the `admin` role to view the Company `accountBalance`.

#### Put it all together!

```
const { ApolloServer } = require("apollo-server-express");
const { schema } = require("./schema.js");
const {
  extractBearerToken,
  hasRoleDirective,
  isAuthenticatedDirective,
  makeOneGraphJwtVerifier
} = require("onegraph-apollo-server-auth");

const verifyJwt = makeOneGraphJwtVerifier(appId);

const server = new ApolloServer({
  typeDefs: schema,
  resolvers,
  schemaDirectives: {
    hasRole: hasRoleDirective,
    isAuthenticated: isAuthenticatedDirective
  },
  context: async incoming => {
    const token = extractBearerToken(incoming.req);

    if (!token) {
      return { jwt: null };
    }

    try {
      const decoded = await verifyJwt(token).catch(rejection =>
        console.warn(`JWT verification failed: `, rejection)
      );
      return { jwt: decoded };
    } catch (rejection) {
      console.warn(rejection);
      return { jwt: null };
    }
  }
});
```

And that's it! With just a few bits of annotation to your schema and a few
minutes to configure the AuthGuardian rules, your entire authentication and
permissions system can be taken care of securely!

### Logging in your users via the OneGraph Auth Client
See the instructions on how to quickly get your users logging in via [Onegraph
Auth](https://www.onegraph.com/docs/logging_users_in_and_out.html). Here's a
rough summary:

Install the `onegraph-auth` package on your client:

```
npm install --save onegraph-auth
# or
yarn add onegraph-auth
```

Instantiate the auth client in the browser (with the same `APP_ID` you used to
configure AuthGuardian):

```
import OneGraphAuth from 'onegraph-auth';

const APP_ID = YOUR_APP_ID;

const auth = new OneGraphAuth({
  appId: APP_ID,
});
```

And log in your user (in this case via `github`):

```
auth
  .login('github')
  .then(() => {
    auth.isLoggedIn('github').then(isLoggedIn => {
      if (isLoggedIn) {
        console.log('Successfully logged in to GitHub');
      } else {
        console.log('Did not grant auth for GitHub');
      }
    });
  })
  .catch(e => console.error('Problem logging in', e));
```

That's it! At the end of that flow, all of your AuthGuardian rules will have
run, and the user will have a JWT that reflects their authentication and
permissions for your API!