Setting up a custom backend

Set up an custom login screen for visitors to your docs.

This guide walks you through setting up a protected sign-in screen for your GitBook documentation site using your own custom authentication backend.

If you are using one of the authentication providers we support or have an OpenID Connect (OIDC) compliant backend, check out our integration guides for a more streamlined setup: Auth0 | Azure AD | Okta | AWS Cognito | OIDC

Overview

To setup a custom authentication system for your GitBook site, follow these key steps:

1

Implement a backend that prompts users to login and authenticate them.

2

Create a JWT token and sign it with your site’s private key.

3

Configure a URL to be used when an unauthenticated visitor access your site.

4

Configure your backend to handle authentication across multiple GitBook sites.

5

Configure your backend to work with adaptive content in GitBook.

1. Create a custom backend to authenticate your users

In order to start authenticating users before they can visit your documentation, you’ll need to set up a server that can handle login and authentication of users.

Your backend should:

  • Prompt users to log in using your preferred authentication method.

  • Validate user credentials and authenticate them.

  • Generate and sign a JSON Web Token (JWT) upon successful authentication.

  • Redirect users to GitBook with the JWT included in the URL.

2. Sign and pass a JWT token to GitBook

Once your backend authenticates a user, it must generate a JWT and pass it to GitBook when redirecting them to your site. The token should be signed using the private key provided in your site's audience settings after enabling Visitor Authentication.

The following example should demonstrate how a login request handler in your custom backend could look like:

index.ts
import { Request, Response } from 'express';
import * as jose from 'jose';

import { getUserInfo } from '../services/user-info-service';
import { getFeatureFlags } from '../services/feature-flags-service';

const GITBOOK_VISITOR_SIGNING_KEY = process.env.GITBOOK_VISITOR_SIGNING_KEY!;
const GITBOOK_DOCS_URL = 'https://mycompany.gitbook.io/myspace';

export async function handleAppLoginRequest(req: Request, res: Response) {
    // Your business logic for handling the login request
    // For example, checking credentials and authenticating the user
    //
    // e.g.:
    // const loggedInUser = await authenticateUser(req.body.username, req.body.password);
    
    // Generate a signed JWT
    const gitbookVisitorJWT = await new jose.SignJWT({})
        .setProtectedHeader({ alg: 'HS256' })
        .setIssuedAt()
        .setExpirationTime('2h') // Arbitrary 2-hour expiration
        .sign(new TextEncoder().encode(GITBOOK_VISITOR_SIGNING_KEY));
    
    // Redirect the user to GitBook with the JWT token in the URL
    const redirectURL = `${GITBOOK_DOCS_URL}/?jwt_token=${gitbookVisitorJWT}`;
    res.redirect(redirectURL);
}

3. Configure a fallback URL

The fallback URL is used when an unauthenticated visitor tries to access your protected site. GitBook will then redirect them to this URL.

This URL should point to a handler in your custom backend, where you can prompt them to login, authenticate and then redirect them back to your site with the JWT included in the URL.

For instance, if your login screen is located at https://example.com/login, you should include this value as the fallback URL.

You can configure this fallback URL within your site's audience settings under the “Visitor authentication” tab.

Configure a fallback URL

When redirecting to the fallback URL, GitBook includes a location query parameter to the fallback URL that you can leverage in your handler to redirect the user to the original location of the user:

const gitbookVisitorJWT = await new jose.SignJWT({})
    .setProtectedHeader({ alg: 'HS256' })
    .setIssuedAt()
    .setExpirationTime('2h') // Arbitrary 2-hour expiration
    .sign(new TextEncoder().encode(GITBOOK_VISITOR_SIGNING_KEY));
    
// Redirect to the original GitBook docs URL with the JWT included as jwt_token query parameter
// If a location is provided, the user will be redirected back to their original destination
const redirectURL = `${GITBOOK_DOCS_URL}/${req.query.location || ''}?jwt_token=${gitbookVisitorJWT}`;
res.redirect(redirectURL);

4. Set up multi-tenant Visitor Authentication (optional)

If you're using GitBook as a platform to provide content to your different customers, you probably need to set up multi-tenant visitor authentication. Your authentication backend needs to be responsible for handling authentication across multiple different sites. This is possible in GitBook with a few small tweaks to your custom authentication backend code.

Adding all tenants to your authentication server

Your authentication backend will need to know the JWT signing keys and the URLs of all the GitBook sites you expect it to handle. If you have two sites in your organization for Customer A and Customer B, you can imagine your authentication code storing such mapping:

const CUSTOMER_A = {
  jwtSigningKey: 'aaa-aaa-aaa-aaa',
  url: 'https://mycompany.gitbook.io/customer-a'
};

const CUSTOMER_B = {
  jwtSigningKey: 'bbb-bbb-bbb-bbb',
  url: 'https://mycompany.gitbook.io/customer-b'
};

Giving your authentication server additional context

When GitBook is unable to authenticate a user's request, it redirects them to the fallback URL. This URL points to your authentication backend, which is responsible for authenticating the user and redirecting them back to the requested content.

To support multiple tenants, your authentication backend needs to know which GitBook site the user is meant to access. This information can be passed in the fallback URL.

So for example, you could setup the fallback URLs for each sites as follow:

GitBook Site
Fallback URL

Customer A site

https://auth-backend.acme.org/login?site=customer-a

Customer B site

https://auth-backend.acme.org/login?site=customer-b

Your authentication backend can then check this information and handle the redirection to the correct site accordingly:

const customerInfo = req.query.site === 'customer-a' ? CUSTOMER_A : CUSTOMER_B;
  
const gitbookVisitorJWT = await new jose.SignJWT({})
    .setProtectedHeader({ alg: 'HS256' })
    .setIssuedAt()
    .setExpirationTime('2h') // Arbitrary 2-hour expiration
    .sign(new TextEncoder().encode(customerInfo.jwtSigningKey));
    
// Redirect to the original GitBook docs URL with the JWT included as jwt_token query parameter
// If a location is provided, the user will be redirected back to their original destination
const redirectURL = `${customerInfo.url}/${req.query.location || ''}?jwt_token=${gitbookVisitorJWT}`;
res.redirect(redirectURL);

5. Configure your backend for adaptive content (optional)

To leverage the Adaptive Content capability in your visitor authentication setup, you can include additional user attributes (claims) in the payload of the JWT that your custom backend generates and include in the URL when redirecting the user to the site.

These claims when included in the JWT are used by GitBook to adapt content dynamically for your site visitors.

Putting it all together, the following code example demonstrates how you could include these claims in the JWT, which can then be used by GitBook to adapt content for your visitors:

index.ts
import { Request, Response } from 'express';
import * as jose from 'jose';

import { getUserInfo } from '../services/user-info-service';
import { getFeatureFlags } from '../services/feature-flags-service';

const GITBOOK_VISITOR_SIGNING_KEY = process.env.GITBOOK_VISITOR_SIGNING_KEY!;
const GITBOOK_DOCS_URL = 'https://mycompany.gitbook.io/myspace';

export async function handleAppLoginRequest(req: Request, res: Response) {
    // Your business logic for handling the login request
    // For example, checking credentials and authenticating the user
    //
    // e.g.:
    // const loggedInUser = await authenticateUser(req.body.username, req.body.password);
    
    // For the purpose of this example, assume a logged-in user object
    const loggedInUser = { id: '12345' }; // Replace with actual authentication logic

    // Retrieve user information to pass to GitBook
    const userInfo = await getUserInfo(loggedInUser.id);
    
    // Generate a signed JWT and include the user attributes as claims
    const gitbookVisitorClaims = {
        firstName: userInfo.firstName,
        lastName: userInfo.lastName,
        isBetaUser: userInfo.isBetaUser,
        products: userInfo.products.map((product) => product.name),
        featureFlags: await getFeatureFlags({ userId: loggedInUser.id })
    };
    
    const gitbookVisitorJWT = await new jose.SignJWT(gitbookVisitorClaims)
        .setProtectedHeader({ alg: 'HS256' })
        .setIssuedAt()
        .setExpirationTime('2h') // Arbitrary 2-hour expiration
        .sign(new TextEncoder().encode(GITBOOK_VISITOR_SIGNING_KEY));
    
    // Redirect the user to GitBook with the JWT token in the URL
    const redirectURL = `${GITBOOK_DOCS_URL}/?jwt_token=${gitbookVisitorJWT}`;
    res.redirect(redirectURL);
}

After setting up and configuring the right claims to send to GitBook, head to “Adapting your content” to continue configuring your site.

Last updated

Was this helpful?