Docs
Security

Security Best Practices

Exposing your email templates to the world, can be risky if not properly secured. If credentials or authentication are mismanaged, others may be able to view or even send emails on your behalf.

Like any full-stack application, Brail may require authentication or authorisation, at different levels of the stack. This document provides a few tips, recipes and considerations to help you achieve the level of security you need.

Security concerns can be grouped as:

  1. Viewing Templates
  2. Interacting with templates
💡

The below best practices are not mutually exclusive. You can use any combination of them to achieve the level of security you need.

Viewing Templates

The following discusses protecting the frontend portion of your Brail apps. If you are only using Brail as an API, you can skip this section.

If you have deployed your Brail frontend, your templates will be visible via the internet. For some use cases, this may be perfectly fine. However, there are several ways to protect your templates from being viewed by unauthorized users.

For example, you might surround your frontend code with an authentication layer:

// Next.js: _app.tsx
export default (props) => {
	const { Component, pageProps } = props
	
	return <AuthGuard> {/* 👈 User-supplied guard that authenticates users before revealing templates */}
		<Component {...pageProps}/>
	</AuthGuard>
}

You could also just entirely obscure the frontend aspects of Brail in production:

// Next.js: _app.tsx
export default (props) => {
	const { Component, pageProps } = props
	
	return <>
		{process.env.NODE_ENV !== "production" && <Component {...pageProps}/>}
	</>
}

Interacting with Templates

Protecting how people interact with your templates is very important, as it can prevent unauthorized users from sending emails on your behalf.

User-provided API Keys Strongly Recommended

Instead of storing your credentials within the Brail project, a very secure approach is to require API keys with every request. This way, your Brail project is kept ignorant of any sensitive credentials, and only credential-holders can ever send emails.

One way to achieve this is to add an apiKey property to your metadata object when sending a template. This can then be forwarded in your send function.

const metaSchema = z.object({
	// ... other properties
	apiKey: z.string()
})
 
template
	.meta(metaSchema)
	.onSend(async args => {
		// Optionally add validation logic here
		await sendEmail(args.html, {
			apiKey: args.meta.apiKey // 👈 Use the provided API key
			to: args.meta.to
		})
	})

API Middleware

If your Brail templates are exposed via an API, you can implement authentication via middleware provided by your the API framework you're using. We provide an example of this in the tRPC section. This is useful for protecting any form of unauthorised interaction with your API.

SDK Environment Variables

If you are publishing your templates as an SDK, it's best to store any API keys or other sensitive credentials in environment variables. This way SDK users cannot send emails using your credentials.

Devtools

If you're not using the Brail devtools, you can skip this section.

By default, the Brail devtools are disabled in production environments. However they can be re-enabled via the isEnabled option, passed to the initDevtools function.

Because devtools uses it's own internal server to support it's sending functionality, any middleware you may have applied to your own API will not apply to the devtools.

However, user-provided keys will still be respected, and can be used to protect from sending emails in production.

💡

In future releases, the Devtools will have support for more granular security.