Empowering Restaurants through Integration

Follow

Open ID Authentication

Before a user of your integration can order delivery or pickup as a diner through Grubhub, they need to authenticate via the Grubhub API and your client application needs to authenticate to access the Grubhub API. This verifies that you should have access to our API, creates a diner record and attaches it to your user, and creates an access token that the client application can use to make Grubhub API calls on behalf of this diner.

Note that this process creates a diner account that is only accessible through your integration. If you need to log into existing Grubhub diner accounts, we offer standard Oauth2 endpoints. These work exactly like any other Oauth2 endpoints, so you can implement them like you would other Oauth2 systems.

There are some instances where you can call some Grubhub API methods without attaching those calls to a diner; for example, if the client does not have a user logged in. However, in order to attach payment and begin the checkout process, your user will need to log in and authenticate as a diner on a valid third-party application.

Our authorization system is bi-directional; that is, it authorizes your user in the Grubhub system and the associated Grubhub diner as a user in your system. This allows your diner to access our API functionality and our system to understand your diner's location and payment information.

To use our authentication schema (and, by extension, our API) you must have a client_id provided by your Grubhub account representative. This client_id must be included in all authorization calls, as shown in the examples below. Your client_id will be different in our production and pre-production environments.

OpenID Tokens

Our authentication method uses OpenID tokens to both identify your users logged into your client application and to identify the corresponding diner in the Grubhub system.

While our authentication system follows this standard, there are some specifics that you'll need to include.

All tokens that you send to the Grubhub API must be signed using JSON Web Signature (JWS) using the same shared secret used for authentication. While the JWS standard allows many hash algorithms, we only initially support signatures using HS256 (HMAC using SHA-256).

In the JWT JOSE header, you must use the kid (key identifier) field to identify the secret key used to sign the token. Multiple keys can be in use at any given time and can be changed or disabled by either party. This kid and the associated secret key will be provided by your Grubhub representative, and may also be called a server-to-server token.

We support the following claims in an OpenID token:

  • iss: The issuer identifier for the party issuing the response. This should be your server address, ie "www.example.com".
  • sub: Unique identifier for the user in your system.
  • aud: Your client ID.
  • exp: The expiration time in epoch seconds.
  • iat: The token issuance time in epoch seconds.
  • nonce: A string value designed to identify a client session and prevent replay attacks.
  • email: User email address.
  • given_name: User first name.
  • family_name: User last name.
  • anonymous_token: If you are connecting a previously-used anonymous session to a diner, this must be the access_token that you used to authenticate that anonymous session.

Anonymous Authentication

Some requests will allow for anonymous authentication. This allows you to serve restaurant information and even begin an order without the diner needing to verify their identity on your site.

However, once the diner logs into your system, we recommend that you authenticate them to a diner (see below). Otherwise, you could lose payment or other order information.

To authenticate anonymously, call POST /oauth2/direct/auth. Set the scope in the body of your request to anonymous and the grant_type to token. Here's an example from the of what that looks like:

POST /oauth2/direct/auth HTTP/1.1
Cache-Control: no-cache
Content-Type: application/json

{
  "grant_type" : "token",
  "client_id" : "3rdpartyclientidstring12345",
  "scope": "anonymous"
}

You'll receive an access token that can be used to perform any call except checkout.

Here's what that response looks like:

{
    "access_token": "xxxxxxx-xxxx-xxxx-xxxxxx",
    "token_type": "bearer",
    "expires_in": 1800,
    "refresh_token": "xxxxxxx-xxxx-xxxx-xxxxxx",
    "id_token": null
}

Before checkout or if your user logs in, you'll need to connect to the anonymous session.

Authenticating a Diner

At some point before checkout, you will have to authenticate your user with the Grubhub API. This ensures that order and payment information links to a known user and prevents unauthorized access to financial information.

Our authentication system receives an OpenID token that identifies your user in your system, then passes back a token that identifies them in ours. This uses a single API call and, unlike standard OAuth2, requires no endpoints on your end. However, because it exposes the user's OpenID token, we strongly recommend that you do not call this method directly in any client application.

Here's how this process works:

Simple_Direct_Lite_Diagram_Numbers.png

  1. Your server requests an access token by passing the OpenID token that identifies your current user.
  2. The Grubhub API creates a new session based on this token.
  3. That session returns an access token to the Grubhub API.
  4. Grubhub API responds to the initial auth request with the session access token and an OpenID token that identifies the diner in our system.

To implement this, your server needs to make a single call to POST /oauth2/direct/auth. The token that you send must adhere to the OpenID token specification.

If the current user previously made API calls on an anonymous session, you'll need to include the anonymous access token in the construction of the OpenID token that you pass. If you start a diner with an anonymous session and do not pass the token here, you may lose the connection to any previous cart.

Note: The anonymous token must not be expired, as any connected authorizations will fail.

The OpenID token contains several claims, then signed with JWS. In addition to the standard claims, you'll also need to include the anonymous access token as anonymous_token. Here's an example of an OpenID token value set:

{
  "iss": "http://server.example.com",
  "sub": "248289761001",
  "aud": "s6BhdRkqt3",
  "nonce": "n-0S6_WzA2Mj",
  "exp": 1311281970,
  "iat": 1311280970,
  "anonymous_token": "xxxxxxx-xxxx-xxxx-xxxxxx"
}

Here's an example of this call:

POST /oauth2/direct/auth HTTP/1.1
Cache-Control: no-cache
Content-Type: application/json

{
  "grant_type" : "token",
  "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOWdkazcifQ.ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3FfrbgebrY2UiOiAibi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAKfQ.ggW8hZ1EuVLuxNuuIJKX_V8a_OMXzR0EHR9R6jgdqrOOF4daGU96Sr_P6qJp6IcmD3HP99Obi1PRs-cwh3LO-p146waJ8IhehcwL7F09JdijmBqkvPeB2T9CJNqeGpe-gccMg4vfKjkM8FcGvnzZUN4_KSP0aAp1tOJ1zZwgjxqGByKHiOtX7TpdQyHE5lcMiKPXfEIQILVq0pc_E2DzL7emopWoaoZTF_m0_N0YzFC6g6EJbOEoRoSK5hoDalrcvRYLSrQAZZKflyuVCyixEoV9GfNQC3_osjzw2PAithfubEEBLuVVk4XUVrWOLrLl0nx7RkKU8NXNHq-rvKMzqg",
  "client_id" : "3rdpartyclientidstring12345",
  "scope" : "openid diner"
}

The grant_type must be "token" and the scope must be "openid diner". We will provide you with a client_id at the time when we initially grant you access to our API.

The response should look like this:

{
   "access_token": "SlAV32hkKG",
   "token_type": "Bearer",
   "refresh_token": "8xLOxBtZp8",
   "expires_in": 3600,
   "id_token": "buyBYhQiOiI0N2RjMTBiMS04ZjZhLTExZTgtOTI4YS1kOTY0NTExMzk2YzEiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI0MmE0M2ZjYS02YTgzLTRlYjEtYTRiYy1iYzUyOTkyZWY0N2IiLCJpc3MiOiJodHRwOlwvXdvsrWx1cC5jb20iLCJleHAiOjE1NDA4MjY1ODYsImdpdmVuX25hbWUiOiJ2aWtyYW0iLCJpYXQiOjE1NDA4MjI5ODYsImZhbWlseV9uYW1lIjoiY2hhbiIsImVtYWlsIjoidmlrcmFtLmNoYW5AdmlraW5ncy5jb20ifQ.5096-y-dL3Vb4PcsSn3XnlCG-M4OxUBPT0UtXRytHKk" 
  }

The access_token needs to be included in the header of every request you make to the Grubhub API.

You do not need to implement any calls in your client application.

Refreshing Your Tokens

Instead of getting a new authentication token every time it expires, you can use a refresh token and renew it before it expires. This process uses the standard OAuth2 method for refresh tokens. The refresh token itself comes from your original authorization call.

NOTE: When we refresh a token, it creates a new session with the same tracking ID.

Refresh tokens can provide extra security in that they require the client_id and the shared secret, while standard authorization tokens, if compromised, can be used by themselves to access the API.

To refresh an existing token, pass it to the POST /auth endpoint on the Grubhub server. You need to pass the following fields:

  • refresh_token - The refresh_token from a previous authorization response payload.

  • brand - The brand that the user belongs to - "GRUBHUB", "SEAMLESS", "TAPINGO", etc.

  • client_id - The client ID that Grubhub assigned to you.

You should use this endpoint for refreshes regardless of which endpoint you use to initially authorize a user. Here's an example of what that looks like:

POST /auth HTTP/1.1
Host: api-pp.grubhub.com
Content-Type: application/json
{ 
	"brand":"GRUBHUB", 
	"client_id":"xxxxxxx-xxxx-xxxxx-xxxxxxx-xxxx-xxx",
	"refresh_token": "xxxxxxx-xxxx-xxxx-xxxxxx"

We prefer this method to using the OAuth2 endpoint for session refreshes, as it does not pass a shared secret, which, if exposed, could potentially compromise your entire integration.

You'll get back diner credentials and the details of the new session.

Here's an example of the response:

{
   "credential": {
       "email": "fakeuser@mail.com",
       "gh_login_id": "28141590",
       "first_name": "Fake",
       "last_name": "Azheck",
       "brand": "GRUBHUB",
       "ud_id": "98155900-7d24-11e7-b965-41398ca65b4c",
       "created_date": 1502298178644,
       "disable_password": false
   },
   "claims": [
       {
           "ud_id": "98155900-7d24-11e7-b965-41398ca65b4c",
           "claim_id": "75",
           "claim": "application_id",
           "claim_type": "temporary"
       },
       {
           "ud_id": "98155900-7d24-11e7-b965-41398ca65b4c",
           "claim_id": "Grubhub Angular Mobile",
           "claim": "application_name",
           "claim_type": "temporary"
       },
       {
           "ud_id": "98155900-7d24-11e7-b965-41398ca65b4c",
           "claim_id": "1.0",
           "claim": "application_version",
           "claim_type": "temporary"
       },
       {
           "ud_id": "98155900-7d24-11e7-b965-41398ca65b4c",
           "claim_id": "GRUBHUB",
           "claim": "brand",
           "claim_type": "temporary"
       },
       {
           "ud_id": "98155900-7d24-11e7-b965-41398ca65b4c",
           "claim_id": "2",
           "claim": "channel_id",
           "claim_type": "temporary"
       },
       {
           "ud_id": "98155900-7d24-11e7-b965-41398ca65b4c",
           "claim_id": "ghmobile_Tksvct4r0d9QXjTLU40W60x1grJ7LgOTu6KAsloR",
           "claim": "client_id",
           "claim_type": "temporary"
       },
       {
           "ud_id": "98155900-7d24-11e7-b965-41398ca65b4c",
           "claim_id": "98155900-7d24-11e7-b965-41398ca65b4c",
           "claim": "diner",
           "claim_type": "permanent"
       },
       {
           "ud_id": "98155900-7d24-11e7-b965-41398ca65b4c",
           "claim_id": "28141590",
           "claim": "gh_login_id",
           "claim_type": "permanent"
       },
       {
           "ud_id": "98155900-7d24-11e7-b965-41398ca65b4c",
           "claim_id": "738ae784-c551-4698-af09-ca723eb36961",
           "claim": "gh_token",
           "claim_type": "temporary"
       },
       {
           "ud_id": "98155900-7d24-11e7-b965-41398ca65b4c",
           "claim_id": "*",
           "claim": "pos.test",
           "claim_type": "permanent"
       }
   ],
   "session_handle": {
       "access_token": "xxxxxxx-xxxx-xxxx-xxxxxx",
       "token_type": null,
       "expire_in": 30,
       "refresh_token": "xxxxxxx-xxxx-xxxx-xxxxxx",
       "refresh_expire_in": 1440,
       "token_created": "2019-05-08T17:09:29.601Z",
       "refresh_token_created": "2019-05-08T17:09:29.601Z",
       "grubhub_token": null,
       "token_created_time": 1557335369601,
       "refresh_token_created_time": 1557335369601,
       "token_expire_time": 1557337169601,
       "refresh_token_expire_time": 1557421769601,
       "tracking_id": "82134ed3-0629-4707-9941-288b718ecffb",
       "last_login_time": "2019-05-08T17:03:30.940Z",
       "login_session_id": "7016c2bb-add6-4997-8c07-8385b62ee4e5",
       "disabled": false
   }
}

Use the session_handle.access_token to authorize future API calls. Use the new session_handle.refresh_token to re-authorize the session.

A refresh token will eventually expire. We recommend that you attempt to refresh with this process first, then if that fails, get a new access_token to continue.