Skip to content

Aquent | DEV6

Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages

Securing Your JavaScript Application and Static Content on Netlify

Written by: Traian Rusu

Security is an important part of web applications and something that almost every application needs. In this blog post we look at how to secure an application hosted on Netlify using SAML 2.0, Json Web Tokens (JWT) and redirects. First, we take a high level look at how to implement authentication into your application. Then we go into how to secure your static content on the Netlify server side.

Authentication with SAML 2.0 and JWT’s

We can implement authentication on Netlify a number of different ways but the one we focus on today is using SAML 2.0 and JWT’s. This method involves using Netlify functions, the passport library and passport-saml library. Once these are installed we need to do the following things:

1. SAML strategy

Set up the configurations using the SAML strategy from passport-saml. In here we set up things like our entry point (which is the identity provider login URL), certificates, keys, and our callback URL (which is where the user is sent once they have successfully logged in). The configuration should look something like this:

const samlStrategy = new saml.Strategy(
  {
    // config options here

    // IdP parameters from environment variables
    entryPoint: idpLoginUrl,
    logoutUrl: idpLogoutUrl,
    cert: idpCert,
    callbackUrl: `${spBaseUrl}/.netlify/functions/login-cb`,
    logoutCallbackUrl: `${spBaseUrl}/`,
    issuer: `${spBaseUrl}/.netlify/functions/login`,
    signatureAlgorithm: 'sha512',
    privateCert: spKey,
    signingCert: spCert,
    decryptionPvk: spKey,
    decryptionCert: spCert,
  },
  (profile, done) => {
    return done(null, profile);
  },
);

passport.use('samlStrategy', samlStrategy);

For more details check out passport and passport-saml

2. Login Route Function

Next  we need to set up a Netlify function to be our login route. When this route is hit it initiates the SAML strategy authentication using passport. It redirects the user to the identity provider where they log in and then on success, they are redirected to the callback URL we provided in the initial setup.

passport.authenticate('samlStrategy', {
    failureRedirect: '/',
    failureFlash: true,
  }),

3. Callback Route Function

 Now we need to create another Netlify function which is the callback route. In this function we use the jsonwebtoken library to create a JWT and sign it using our secret (which will be saved in an environment variable to keep it secure). We can structure this JWT in any way we want and add any data to it we may need, like user data, role data etc. We can also give it an expiry time. We then set a cookie on the response with this JWT. It is a good idea to use a few attributes to make the cookie more secure, namely httpOnly and secure. HttpOnly makes the cookie inaccessible from the JavaScript api which helps prevent cross site scripting attacks. The secure attribute makes sure the cookie can only be sent over https and not over http. This helps prevent man in the middle attacks.

4. Validity Check Function

Now that we have the authentication set up, we need to create a utility function or middleware that checks if the JWT is valid. This is also done using the jsonwebtoken library and our secret.

    const tokenInfo = jwt.verify(idToken, jwtSecret);
    return tokenInfo;

This function or middleware is called for any request that comes in and we will only proceed with the request if it is a valid JWT, otherwise we respond with a 401 unauthorized code. For more information on these topics and details on how to set them up look into the passport library, passport-saml, SAML, JWT and the jsonwebtoken library.

Our authentication flow now looks like this:

authentication flow

Implementing the Front End

On the front end there are a lot of different patterns and ways to implement authentication but here’s a quick overview of one way using React with context, custom hooks, and private routes.

  1. Create a context and wrap our application in it so every component has access to the user information.
  2. Create a custom hook which manages our authentication and has some user data like whether they are logged in or not.
  3. Set this hook on the context.  This makes the same instance accessible from anywhere that has access to the context.
  4. Use that hook to provide access control.

We can use it in many ways: with private routes to protect our different routes against unauthenticated users, to provide more fine-grained access control and protect against specific actions, and to display different UI within a page, etc.

Netlify Server Side Auth with JWT’s and Redirects

This setup works well in securing our JavaScript application. However, if we are using Netlify, any static assets that live in our repo are still accessible directly via the URL. Netlify will bypass our authentication flow and serve them up. This is especially problematic for an application using Netlify CMS as all the articles and assets that they use are stored in our repo. We can solve this problem by adding server side auth and using JWT and role-based access control. Let’s look at how to set this up.

First, we need to add our JWT secret to Netlify. We can do this by going to the Netlify dashboard and navigating to Access control > Visitor Access > Password/JWT Secret and adding our JWT secret that we used earlier. Then in our login callback Netlify function where we create the JWT, we need to do 2 things:

  1. Name the cookie we were setting to match what Netlify uses (Netlify uses the name nf_jwt), and
  2.  Format the JWT to match what Netlify is expecting.

The format looks like this:

    {
        app_metadata: {
          authorization: {
            roles: ['user'],
          },
        },
... any other data you want
      },

The roles can be whatever you want to use for your app, but the important thing is to have the app_metadata property with an object in the format above.

Now that we have our cookie and JWT in the format that Netlify expects it, and we have given Netlify our JWT secret, we can go ahead with the next part which is securing our application using role-based redirects. To do this we need to create a _redirects file in the publish directory of our site. If you are using Gatsby this will need to be added in the static folder. The redirects look like this:

/gated/* 200! Role=user
/gated/* /.netlify/functions/login 401!

This tells Netlify to grant access to the gated folder and anything under it only to users who have a valid JWT token and who have the role “user”. This is the role we set above in the JWT. The second line tells Netlify to redirect anyone who does not have a valid JWT or does not have the role “user” to the route for our Netlify function which starts the SAML login process and redirects them to the identity provider. Alternatively, this can redirect them to a login page on your site. To protect our pages, we need to move all of them into a folder named “gated”. This will differ depending on what framework you are using. In Gatsby, this means adding a gated folder under the pages folder and moving everything under pages into pages/gated. We also need to add a redirect for / to /gated so that the landing page of the site is also gated. Finally, we add role-based redirects for all the static assets we have that we want to protect for example /assets/*, /icons/*, /static/*. That’s it! We now have all of our static content protected as well. We can also go further and add authorization and set up multiple roles and only allow access to certain parts of our application to certain roles.

Our authentication flow for our static content now looks like this. Note that the initial login flow is the same, but any subsequent requests are protected at the Netlify server level and that resource is only served if the JWT is valid and has the proper role.

new authentication flow

Now we have a better understanding of how to implement authentication in an application hosted on Netlify and how to make sure that all of our content is secure, including our static content. This is especially useful for applications using sensitive static content such as images or pdf’s and also for applications using Netlify CMS where all of the articles and all of their assets are stored inside the repo. Using these patterns, we are able to secure our JavaScript application and prevent our server from serving any static content to unauthenticated users as well.