Trusted Authentication

Trusted authentication is an optional extra layer of security for connecting to Grammarly. It helps prevent bad actors from impersonating your application and using your credentials to gain access to any SDK features that you may be paying for as part of your planopen in new window. It also allows you to control access to the Grammarly Text Editor Plugin on a per-user level.

How it works

Trusted authentication is based on OAuth 2.0 client authentication assertions (RFC 7523open in new window). It requires your application to have an assertion before it can connect to Grammarly. These assertions are vended by your servers and prove that the application is authorized.

A diagram showing the Text Editor SDK requesting an assertion from your endpoint and using it to obtain an access token from Grammarly's servers.

For the assertions, trusted authentication uses JSON Web Tokensopen in new window (JWTs), which are signed with a private key and validated with the corresponding public key.

The SDK will automatically request assertions and use them to obtain access tokens for Grammarly’s services. The only parts of this flow you need to handle are writing an assertion endpoint that returns signed JWTs, and setting the oauthAssertionProvider EditorConfig property to call that endpoint. We’ll walk you through how to do this in the following steps.

Step 1: Generate a public/private key pair for your app

The first step to implementing trusted authentication is to generate a public/private key pair for your app:

  1. Open the App Console by selecting your application from My Appsopen in new window.
  2. Select Trusted Authentication in the left menu.
  3. Click Generate new key.
  4. A pop-up will appear showing your private key in JSON Web Key (JWK) format. Copy and store it securely.
  5. Click Done.

You can now see the key ID and the date that it was created. Grammarly stores the public key but does not store your private key. If you lose it, you can generate a new public/private key pair using the instructions above.

Note that your key may not be usable for several minutes while our system updates.

WARNING

Don’t turn on "Require trusted authentication" until you complete steps 2, 3, and 4 below.

Step 2: Create an assertion endpoint that serves signed JWTs

The next step is to create an endpoint that serves assertions: JWTs signed with your private key. In order to be valid, the JWTs must follow certain requirements, which are described in this section. We provide sample assertion endpoints in a few languages as a starting point.

Note that depending on your use case, you may want to set up additional authentication logic as part of your endpoint. For instance, you can decide to only provide assertions to certain users or groups of users (e.g., only logged in users). Want help setting up more advanced assertion logic? Let us know!open in new window.

Examples

While the implementation details for the assertion endpoint will vary based on your tech stack, here are examples to help you get started:

JWT requirements

For your reference, the complete requirements for a JWT to be considered valid by Grammarly are described below.

JWT header

The following parameters are required in your JWT header:

NameValue
algThe algorithm of your private key (e.g. RS256), specified in the JSON for your key
verv1
kidThe key ID of your private key

JWT payload

The following parameters are required in your JWT payload:

NameValue
subYour client ID
issAgain, your client ID
iatIssued time in seconds since the Unix epoch
expExpiration time in seconds since the Unix epoch
audhttps://tokens.grammarly.com/oauth2/token

Note that the "validity window" of the assertion, defined as the time between the iat and exp fields, cannot be greater than 5 minutes. To account for possible clock skew, you might want to backdate your iat by a few seconds (e.g. 5 seconds).

JWT signing

Lastly, your JWT must be properly signed with your private key.

Using the JOSEopen in new window library in a Node.js server, here’s a basic assertion generation example (this example includes iat backdating by 5 seconds):

const now = Math.floor(new Date().getTime() / 1000); // Epoch time in seconds
const issuedAt = now - 5; // Backdate by 5 seconds to account for any clock skew
const expirationTime = issuedAt + 5 * 60; // Assertion will be valid for 5 minutes from the issuedAt
const assertion = await new jose.SignJWT({})
  .setProtectedHeader({ alg: "YOUR_KEY_ALGORITHM", ver: "v1", kid: "YOUR_KEY_ID" })
  .setSubject("YOUR_CLIENT_ID")
  .setIssuedAt(issuedAt)
  .setIssuer("YOUR_CLIENT_ID")
  .setAudience("https://tokens.grammarly.com/oauth2/token")
  .setExpirationTime(expirationTime)
  .sign(privateKey);

You’ll find other examples of generating assertions in the sample endpoints.

Step 3: Update your application code

Now that you’ve created your assertion endpoint, you need to update your application code so that the Text Editor SDK can start using it.

The SDK uses the oauthAssertionProvider EditorConfig property to retrieve an assertion for accessing Grammarly’s servers. As the simplest approach, you can set this property to the URL for your assertion endpoint.

​​<grammarly-editor-plugin client-id="YOUR_CLIENT_ID" config.oauthAssertionProvider="YOUR_SERVER_URL">
  <textarea></textarea>
</grammarly-editor-plugin>

TIP

Your assertion endpoint must include the client ID in the signed JWT. Common options for getting the client ID are storing the client ID in your endpoint code or passing it to your endpoint as a query parameter (e.g. config.oauthAssertionProvider="YOUR_SERVER_URL?clientId=YOUR_CLIENT_ID").

Your server will need to be able to receive an HTTP GET request at this URL and return an HTTP 200 application/json message with the following shape:

{
  assertion: "YOUR_SIGNED_JWT";
}

You may need to do something more advanced with your request—like send it as an HTTP POST with special headers or modify its return type (from XML or raw text). In this case, instead of setting the oauthAssertionProvider property to your assertion endpoint’s URL, you can set it to a function. This function should return a promise that resolves to a JSON object:

​​<grammarly-editor-plugin client-id="YOUR_CLIENT_ID">
  <textarea></textarea>
</grammarly-editor-plugin>
document.querySelectorAll("grammarly-editor-plugin").forEach((grammarlyEditor) => {
  grammarlyEditor.config = {
    oauthAssertionProvider: async () => {
      // Call your endpoint to get an assertion
      const response = await fetch("YOUR_SERVER_URL", {
        method: "POST",
        mode: "cors",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          clientId: "YOUR_CLIENT_ID",
        }),
      });
      // Handle errors
      if (!response.ok) {
        throw new Error("Error creating assertion");
      }
      // Return generated assertion
      return await response.json();
    },
  };
});

For a working example of how to set the oauthAssertionProvider to a custom function, see the CodeSandbox demoopen in new window. You’ll need a working assertion endpoint that accepts POST requests to test this example. The sample assertion endpoints we provide accept GET requests, but you can modify the code to accept POST requests.

Step 4: Test

We recommend testing the oauthAssertionProvider property with a small set of users at first while "Require trusted authentication" is off for your app. Don't worry, you can still send assertions to Grammarly while "Require trusted authentication" is off.

If there is any problem with the assertion returned by your assertion endpoint (even if trusted authentication isn’t required), the connection to Grammarly will be refused. The Grammarly button will show that Grammarly is not available, and you can find further error details in the Network tab of your browser’s developer toolsopen in new window.

Note that if your app has multiple clients, you should test that assertions work for each client and client ID. Once you turn on "Require trusted authentication" for your app, it will be required for all your app’s clients.

When you’re sure that everything is working properly and the Grammarly Text Editor plugin is successfully loading in your app, you can roll out the oauthAssertionProvider property to all your users.

Step 5: Turn on “Require trusted authentication” for your app

Once you’re confident that assertions are working properly for all your users, you’re ready to turn on "Require trusted authentication":

  1. Open the App Console by selecting your application from My Appsopen in new window.
  2. Select Trusted Authentication in the left menu.
  3. At the bottom of the screen, switch Require trusted authentication to ON.
  4. A pop-up will appear asking you to confirm your choice.

Note it may be several minutes before this change takes effect.

When trusted authentication is required, Grammarly will require a valid assertion for any connection made by any client of your app.

Last Updated: 5/31/2023, 3:59:28 PM