• Docs
  • Pricing
  • Support
  • Blog
  • Login

›Advanced

Intro

  • What's OneGraph?
  • How does it work?
  • Creating your first app
  • Making your first query
  • OneGraphiQL
  • Authentication & Security Overview

On the frontend

  • Using with Apollo
  • Log in to services
  • Example with create-react-app

Advanced

  • OneGraph JWT
  • Persisted Queries
  • Mailchimp Signup with Persisted Queries

Generating JWT for authentication

OneGraph generates JWTs for authentication with external services like Hasura, PostGraphile, Firebase, Netlify, or even your own apps.

What are JWTs?

JWT (or JSON Web Tokens) are an industry standard way of communicating authentication and authorization data openly and securely. You can use a JWT as a way of verifying the identity of your users, both for authentication (knowing who a user is) and authorization (knowing what a user is allowed to do).

Overview of the OneGraph JWT process

OneGraph's JWT flow looks like:

  1. App requests user signs in with $SERVICE (e.g. GitHub, Spotify)
  2. Upon the user successfully signing into $SERVICE, OneGraph will run the preflight query for the app (if any)
  3. After running the query, OneGraph will send the results to a webhook (if any) for processing
  4. The data that comes back from OneGraph is signed exactly as-is and stored as the JWT payload
  5. The JWT is given to the user, and the user is sent back to the app

For any app you write, authentication and authorization will be based off of two factors:

  1. Data: Information about this user, their organizations, etc.
  2. Logic: Deciding based off of #1 what - if any - permissions should be granted to this user.

The preflight query in step 2 allows you to collect all the relevant data you need about a user, and the webhook processing allows you to execute any of your custom logic!

Step 1. Authenticating with data via OneGraph's data access

Much of a user's identity is determined by data related to them. With OneGraph you can retrieve all the relevant data every time a user successfully logs into an external service (e.g. GitHub, Spotify, etc.) by settings a preflight query. The results of the query will be stored directly in the JWT for your application to rely on.

Let's say we want to identify our users by their GitHub id, and also store their GitHub email for use elsewhere (so we don't have to query later for it). In that case, we can simply set this preflight query:

query FindMe {
  me {
    github {
      email
      # Use this instead of `id`
      # for a permanent GitHub id.
      databaseId
    }
  }
}

Now every time a user logs in, OneGraph will generate and sign a JWT with the result of that query under the "https://onegraph.com/jwt/preflight-query" (one of our JWT claims), e.g.:

// This is the fullly decoded JWT payload
{
  "iss": "OneGraph",
  "aud": "https://serve.onegraph.com/dashboard/app/d1995c39-be74-4c23-868a-a263d9a54ac1",
  "iat": 1561589151,
  "exp": 1562798750,
  "https://onegraph.com/jwt/claims": {
    "access_token": "HX2sXDQZ5anYSa-zapGur3H7R3aB5n9lQaMVuvHZn2Y"
  },
  "https://onegraph.com/jwt/preflight-query": {
    "data": {
      "me": {
        "github": {
          "email": "docs@onegraph.com",
          // We can use this id in our app to authenticate this user!
          "databaseId": "35996",
        }
      }
    }
  }
}

As long as the JWT is signed correctly (see below for verifying) you can trust that whoever has this token definitely has a GitHub userId of 35996.

(Optional) Step 2: Authorizing via data and webhooks

Now that we have a user's identity (e.g. their GitHub userId), let's decide if they're an admin or just a regular user in order to restrict their access appropriately in our app. After OneGraph runs the preflight query above, it will (optionally) send it to a webhook we control for final processing.

For this example, we'll decide that this user is an admin for our app if they are a member of the "OneGraph" organization on GitHub, otherwise they're a normal user.

First, let's expand our preflight query to include the GitHub organization information like id and name:

query FindMe {
  me {
    github {
      email
      databaseId
      organizations(first: 100) {
        nodes {
          databaseId
          name
        }
      }
    }
  }
}

The result of that query for our hypothetical user will be:

// Example query result that we'll use
// for our authentication and authorization
{
  "data": {
    "me": {
      "github": {
        "email": "docs@onegraph.com",
        "id": 35996,
        "organizations": {
          "nodes": [
            {
              "databaseId": 3372922,
              "name": "HappyCodingCo"
            },
            {
              "databaseId": 29494709,
              "name": "OneGraph"
            }
          ]
        }
      }
    }
  }
}

OneGraph will now take that result and will POST it to our webhook url, which in our case is an express route handler:

app.post('/processOneGraphJwt', function(request, response) {
  const queryKey = 'https://onegraph.com/jwt/preflight-query';
  const data = request.body[queryKey] && request.body[queryKey].data;

  // Remove the query results from the JWT, we don't need them anymore
  delete request.body[queryKey];

  // Retrieve the data, being careful about nullable fields
  const me = data.me;
  const gitHub = !!me && me.github && me.github.databaseId && me.github;

  const isAdmin =
    gitHub && gitHub.organizations.nodes.find(org => org.name === 'OneGraph');

  // Generate data specific to our app based on this
  const ourAppData = {
    allowedRoles: ['user', isAdmin ? 'admin' : null].filter(Boolean),
    defaultRole: isAdmin ? 'admin' : 'user',
    userId: gitHub && gitHub.databaseId,
  };

  const finalToken = {
    ...request.body,
    ourAppData,
  };

  response.send(finalToken);
});

If this webhook returns a successful JSON response, OneGraph will take the exact output of this webhook, sign it, and use it as the final JWT. The result of our preflight query and the webhook would look like:

{
  "iss": "OneGraph",
  "aud": "https://serve.onegraph.com/dashboard/app/d1995c39-be74-4c23-868a-a263d9a54ac1",
  "iat": 1561591677,
  "exp": 1562801276,
  "https://onegraph.com/jwt/claims": {
    "access_token": "uIl4bpOPOvPVHqDwUWGkh1QZY_h0EyN5GsCJIv5gQEE"
  },
  "ourAppData": {
    "allowedRoles": [
      "user",
      "admin"
    ],
    "defaultRole": "admin",
    "userId": 35996
  }
}

Notice how your webhook can change any values passed to it, like the aud, exp, etc. This gives your webhook full control over the final JWT that's delivered to your users.

How can I consume OneGraph JWTs securely in my node.js app?

If you're working in node.js, you can simply drop in the this code from the package and change your <app-id> below:

var jwksClient = require('jwks-rsa');

var client = jwksClient({
  // Be sure to replace your <app-id> here
  jwksUri: 'https://serve.onegraph.com/app/<app-id>/.well-known/jwks.json',
});

function getKey(header, callback) {
  client.getSigningKey(header.kid, function(err, key) {
    var signingKey = key.publicKey || key.rsaPublicKey;
    callback(null, signingKey);
  });
}

jwt.verify(token, getKey, options, function(err, decoded) {
  console.log(decoded.foo);
  /* 
  If we have a `decoded` value, that means the token is valid and signed,
  we can trust the data inside.
  {
  "iss": "OneGraph",
  "aud": "https://serve.onegraph.com/dashboard/app/d1995c39-be74-4c23-868a-a263d9a54ac1",
  "iat": 1561591677,
  "exp": 1562801276,
  "https://onegraph.com/jwt/claims": {
    "access_token": "uIl4bpOPOvPVHqDwUWGkh1QZY_h0EyN5GsCJIv5gQEE"
  },
  "ourAppData": {
    "allowedRoles": [
      "user",
      "admin"
    ],
    "defaultRole": "admin",
    "userId": "35996
  }
}
  */
});

If you're interested in other options in jwks-rsa, see the excellent Auth0 node-jwks-rsa library for more examples on handling caching and other fun topics.

Technical details

What algorithms can OneGraph sign JWTs with?

OneGraph can generate:

  • RSA256, for public/private JWTs
  • HS256, or shared-secret JWTs

While we default to using RSA256, OneGraph supports both algorithms so that you can use OneGraph JWTs with every external system.

What are JWKs, and what url should I use for my OneGraph app?

When using the recommended RS256 (private/public) JWT signing, JWKs are used to automated the distribution of public keys in a nice JSON format. The only bit of information you need to use them with your app or an external service is their url - every app has their own public keys generated (and potentially rotated) by OneGraph and hosted at a known url:

https://serve.onegraph.com/app/<app-id>/.well-known/jwks.json

Here's an example of a real url with usable JWKs:

https://serve.onegraph.com/app/d1995c39-be74-4c23-868a-a263d9a54ac1/.well-known/jwks.json

← Example with create-react-appPersisted Queries →
Links
OneGraph Overview Example projectsOneGraphiQL Explorer
Support
Live chat on Spectrum> TwitterBlog
More
Terms of ServicePrivacy Policy
Copyright © 2019 OneGraph