Help Scout Developers

Help Scout Developers

Docs API
  • Overview
    • Overview
    • Status Codes
  • Restricted Docs
    • Overview
    • Examples
      • Show login screen when signing in
      • Single Sign-On (SSO) experience
  • Endpoints
    • Articles
      • List Articles
      • Search Articles
      • List Related Articles
      • List Revisions
      • Get Article
      • Get Revision
      • Create Article
      • Update Article
      • Upload Article
      • Update View Count
      • Delete Article
      • Save Article Draft
      • Delete Article Draft
    • Assets
      • Create Article Assets
      • Create Settings Assets
    • Categories
      • List Categories
      • Get Category
      • Create Category
      • Update Category
      • Update Category Order
      • Delete Category
    • Collections
      • List Collections
      • Get Collection
      • Create Collection
      • Update Collection
      • Delete Collection
    • Redirects
      • List Redirects
      • Get Redirect
      • Find Redirect
      • Create Redirect
      • Update Redirect
      • Delete Redirect
    • Sites
      • List Sites
      • Get Site
      • Create Site
      • Update Site
      • Delete Site
      • Get Site Restrictions
      • Update Site Restrictions
  • Objects
    • Article
    • ArticleRef
    • ArticleRevision
    • ArticleRevisionRef
    • ArticleSearch
    • CallbackConfiguration
    • Category
    • Collection
    • Person
    • Redirect
    • Site
    • SiteRestriction

Show login screen when signing in

This example demonstrates a flow where you will always present a sign-in screen to the visitor. This makes sense in cases where you can’t re-use an existing authentication for your Docs site visitors, such as a case where you don’t have an existing experience that your visitors sign in to.

Overview

We will implement a simple endpoint on http://localhost:3000/signin. This URL is what we will use when configuring site restrictions later on. When the visitor tries to access the restricted site, they will be redirected to this URL.

The code for this example can be found on GitHub.

Project setup

To begin, we will create a new project using npm:

Copy
mkdir custom-callback-login-example
cd custom-callback-login-example
npm init -y

This will create a new NPM project in a folder named custom-callback-login-example.

Open package.json in your code editor and add the following to the scripts section:

Copy
...
"scripts": {
  "start": "nodemon index.js"
}
...

Install dependencies

We will start by adding a few dependencies that will make building the endpoint much easier.

  • ExpressJS - Web application framework to ease the implementation of an HTTP endpoint.
  • jsonwebtoken - One of the implementations of JWT for NodeJS. See jwt.io for libraries for most languages.
  • nodemon - Makes development easier as it automatically restarts your web server whenever you make updates to the code.
Copy
npm install --save express jsonwebtoken
npm install --save-dev nodemon

Create sign-in page

We will start by implementing the sign-in page that visitors will see when redirected from your Docs site. Create a new file called index.js and add the following to it:

Copy
const express = require('express')

const app = express()
const port = 3000

app.get('/signin', (req, res) => {
    // Help Scout will always include the path that the visitor was requesting
    // as a query parameter
    const returnTo = req.query.returnTo || '/'

    // This could be a redirect to your already existing sign-in page
    // In this example we render a simple HTML sign-in page
    res.send(`
        <html>
            <head><title>Sign in</title></head>
            <body>
                <form method="post" action="/signin">
                    <input type="hidden" name="returnTo" value="${returnTo}" />
                    <p>
                        Email:<br />
                        <input type="text" name="email" required />
                    </p>
                    <p>
                        Password:<br />
                        <input type="password" name="password" required />
                    </p>
                    <p>
                        <input type="submit" value="Sign in" />
                    </p>
                </form>
            </body>
        </html>
    `)
})

app.listen(port, () => {
    console.log(`Server listening on port ${port}`)
})

This will expose a GET endpoint on /signin that shows a simple HTML form.

You can see this in action by starting the server using this command:

Copy
npm start

and opening http://localhost:3000/signin in a browser.

Handle sign-in form submission

Before adding the code for handling the form submission, we will add code that handles the authentication of the visitors:

Copy
const isValidCredentials = (email, password) => {
    // Here you can integrate into your user backend to validate the credentials
    // For now we just do a simple implementation to show the flow
    return email === 'john@example.com' && password === '12345678'
}

In our example, we keep it simple but feel free to try to use a real user backend that can verify the credentials instead.

Next, we will add the code for creating the JSON Web Token (JWT). This code will use the shared secret that you get when enabling the site restriction through the Docs API.

Copy
const jwt = require('jsonwebtoken')

const sharedSecret = 'ENTER SHARED SECRET HERE'

const createToken = email => {
    const tokenPayload = {
        // Expires in 1 minute, resulting in a new call to /signin
        // In your own implementation you will likely  want a longer expiration
        exp: Math.floor(Date.now() / 1000) + (60),
        sub: email,
    }

    // Create signed JSON Web Token
    return jwt.sign(tokenPayload,
                    sharedSecret,
                    {algorithm: 'HS512'})
}

We have some recommendations for what to include as claims in the token which can be seen here. We use the HS512 algorithm for generating and verifying signatures.

Lastly, we want to implement the form submission code. Here we need the base URL for the restricted Docs site so we can redirect back to it.

Copy
const docsSiteUrl = 'ENTER BASE URL FOR YOUR RESTRICTED DOCS SITE'

app.use(express.urlencoded({extended: true}))

app.post('/signin', (req, res) => {
    const returnTo = req.body.returnTo
    const email = req.body.email
    const password = req.body.password

    if (isValidCredentials(email, password)) {
        const token = createToken(email)

        const redirectUrl = `${docsSiteUrl}/authcallback?token=${token}&returnTo=${returnTo}`
        res.redirect(redirectUrl)
    } else {

        res.send(`
            <html>
                <body><p>Invalid email and password, got back and try again. (Psst, it's actually "john@example.com" and "12345678" but don't tell anyone!)</p></body>
            </html>
        `)
    }
})

We start by validating the credentials using the function we created earlier on. Then we create a signed token, that we will use in the redirect URL when redirecting back to the Docs site.

We have now implemented the entire authentication endpoint. You can see the full index.js here.

Testing the flow

To test the flow you can use one of your own sites that you have set up restrictions on. If you want to just try this sample code, we also host an example site that is restricted and we have made the shared secret public for this specific site.

Remember to keep the shared secret private as it guarantees only your endpoint can authenticate site visitors.

Once you have the Docs site URL and the shared secret, you only need to change these two constants:

Copy
const docsSiteUrl = 'ENTER BASE URL FOR YOUR RESTRICTED DOCS SITE'
const sharedSecret = 'ENTER SHARED SECRET HERE'

Test against our sample site

We have created an example site that you can find on:

https://restricted-docs-example-site.helpscoutdocs.com/

It is restricted using the following Docs API call:

Copy
PUT /v1/sites/[SITE_ID]/restricted
Authorization: Basic [AUTHENTICATION]
Content-Type: application/json

{
    "enabled": true,
    "authentication": "CALLBACK",
    "callbackConfiguration": {
        "signInUrl": "http://localhost:3000/signin"
    }
}

You can configure your local instance by setting the two constants to these values:

Copy
const docsSiteUrl = 'https://restricted-docs-example-site.helpscoutdocs.com'
const sharedSecret = 'pQKpsnVo3TS7efPYC9FcSRoCCyB3aI+MYpI+omktNzE='

Contents

    ↑Back to top

    • Customer Login
    • Visit Help Scout
    • Status
    © Help Scout 2025