Send SMS Directly from the Browser (No Backend Code Required!)
Tags: react, frontend, security, authorization
Published: 2023-06-01 09:24:06

---
title: Send SMS Directly from the Browser (No Backend Code Required!)
published: true
date: 2023-05-31 00:00:00 UTC
tags: ReactJS, React, Frontend, Security, Authorization
canonical_url: https://www.permit.io/blog/send-sms-from-the-browser-no-backend-code-required
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rog3hm67n9xexszsoun2.png
---

## Do More With Frontend

With just frontend code, frontend developers today have a great deal more power. It's not just about new browser APIs, but also about backendless tools that let you build complete applications without any backend code. However, there's still one area where backend code is required: backend-oriented APIs that need access control enforcement. The reason for that is that those APIs need to be authenticated with a secret key that cannot be exposed in the frontend. But guess what? There’s a solution for that. 

In this article, we'll explore an awesome new way to use backend APIs that are often needed as a feature in frontend applications. We'll learn how to send SMS (or messages on WhatsApp, Viber, and Facebook Messenger) directly from the browser. To authenticate our users in the browser, we'll use [<u>Clerk.dev</u>](https://clerk.dev/ "https://clerk.dev/"), and for managing their permissions to send messages using [<u>Vonage</u>](https://www.vonage.com/ "https://www.vonage.com/") APIs, we'll rely on Permit.io. So let's dive in and see how it's done!

## Frontend Only Authorization

The approach we will take in this article to authorize our users to send messages is called [<u>Frontend Only Authorization (FoAz)</u>](http://permit.io/foaz "http://permit.io/foaz"), an open standard created by Permit.io. FoAz is based on a proxy service that verifies the user's frontend JWT token, checks if they have proper permissions to perform the API call, and then forwards the call to the backend API.

 ![foaz_diagram.png](https://media.graphassets.com/m5Cu3kKUSzye36DvSU2Q "foaz\_diagram.png")

By using FoAz, we can authenticate our users in the frontend, and then use the same JWT token to authorize them to perform API calls. Let's start the tutorial by setting up our application.

## Setup the Application

For building our application, we will use the React framework. You don't need any prior knowledge of react to follow along, as the code is short, and we will explain it as we go. You'll need a local [installation of node.js and npm](https://nodejs.org/en/download "https://nodejs.org/en/download") to continue with the tutorial.

[_Tutorial code repository_](https://github.com/permitio/foaz-vonage-sample "https://github.com/permitio/foaz-vonage-sample")

First, let's create a new React app by using [<u>Vite</u>](https://vitejs.dev/ "https://vitejs.dev/"), a fast-build tool for modern web apps. Open your terminal and run the following command in your desired projects directory:

```bash
npm create vite @latest frontend-messages-demo--template react \
    && cd frontend - messages - demo \
    && npm install
```

This will create a new react app in the frontend-messages-demo directory.

We can see our app by running the following command:

```bash
npm run dev
```

This will start a local development server and open a browser window with our app.

## Setup Authentication in the App

As a first step to access control, we will need a way to authenticate our application users, so we can verify their identity. For this, we will use [<u>Clerk.dev</u>](https://clerk.dev/ "https://clerk.dev/"), a frontend-first authentication platform that lets you add authentication to your application in minutes. Clerk offers a free tier that is more than enough for our needs, let's go to [https://dashboard.clerk.com/sign-up](https://dashboard.clerk.com/sign-up "https://dashboard.clerk.com/sign-up") and create an account there. Let's set up our newly created Vite app to use Clerk.dev for authentication.

1. Go to [Clerk.com](https://dashboard.clerk.com "https://dashboard.clerk.com") dashboard

2. In the Clerk dashboard, click on Add Application
 ![clerk_add_application.png](https://media.graphassets.com/Q1XGH8VTXmNTLgNobsRx "clerk\_add\_application.png")
3. Give your application a name, and click Create Application (I named mine `Frontend Messages Demo`)
 ![clerk_create_application.png](https://media.graphassets.com/Mzm3TNuVRkSZiRjujIsI "clerk\_create\_application.png")
4. In the next screen, choose the React option, and copy the key that shows up.
 ![clerk_react.png](https://media.graphassets.com/LFDWiVTwOY77KbKDioAz "clerk\_react.png")
5. In our React app, create a new file in the root folder called .env and add the following line to it (It is important to add the VITE\_ prefix to the variable name, as Vite will use it to inject the variable into our app):
    ```bash
    VITE_CLERK_FRONTEND_API_KEY=<YOUR_API_KEY_HERE>
    ```

6. In the same folder, run the following command on your terminal to install the Clerk SDK:
    ```bash
    npm install @clerk/clerk-react
    ```

Now that we have setup the Clerk account in our application, it is time to test the user authentication.

1. In the App.tsx file, remove the whole content of the return section of the App function and replace it with the following code:
    ```tsx
    function App() {
        return (
            <>
                <ClerkProvider publishableKey={clerkPubKey}>
                    <SignedIn>
                        <SendMessage />
                    </SignedIn>
                    <SignedOut>
                        <RedirectToSignIn />
                    </SignedOut>
                </ClerkProvider>
            </>
        )
    }
    ```

2. At the top of the file, before the App function, let's paste the follwoing code that will create an empty component to verify our users' login status and add the required imports to the file:

    ```tsx
    import { ClerkProvider, RedirectToSignIn, SignedIn, SignedOut, useAuth, useClerk } from '@clerk/clerk-react'
    
    const clerkPubKey = import.meta.env.VITE_REACT_APP_CLERK_PUBLISHABLE_KEY;
    
    function SendMessage() {
        const { isSignedIn, getToken } = useAuth();
        const { signOut } = useClerk();
    
        if (!isSignedIn) {
            return null;
        }
    
        return (
            <div>
                <h1>Hello</h1>
                <button onClick={() => signOut()}>Sign Out</button>
            </div>
        )
    }
    ```

3. Going now back to the browser, we can see that the app is now showing a login screen.

 ![clerk_login.png](https://media.graphassets.com/ySUTHAnzTLWXlh8H0vF7 "clerk\_login.png")
4. Let's go back to Clerk and create two users in the `Users` screen to mock our permission model with two users. For the example, let's used the following emails: `admin@messaging-foaz.app `and `user@messaging-foaz.app` (choose any password you want).
 ![clerk_users.png](https://media.graphassets.com/o8lJlPMlT2aO6aIJV8XV "clerk\_users.png")
5. With the users created, let's go back to our newly created app and login with one of the users, and check that we see the correct name in the app.

At this point, we have our users authenticated, and we can start working on the permissions model.

## Setting Up Permissions for Sending SMS

Now that we have our users authenticated, we need to setup the permissions for sending messages. Let's say that our application has two kinds of users: admins and regular users. We want to allow admins to send messages and deny regular users from sending messages.

Let's configure the API call to Vonage in Permit.io to allow admins to send messages.

### Get API Secret from Vonage

Sending an SMS is something that requires a service provider, and in this tutorial, we will use Vonage. Vonage is a cloud communications platform that offers APIs for SMS, voice, and video.

After signing up, you will need to create a new API key and secret. You can find your Vonage API key and secret in the [Vonage dashboard](https://dashboard.nexmo.com/ "https://dashboard.nexmo.com/").

 ![vonage_dashboard.png](https://media.graphassets.com/MgDCUhQTp6onmLWuYRlz "vonage\_dashboard.png")
### Configure the API Call Permissions in Permit.io

To use FoAz, we will need to setup the particular SMS sending API call to Vonage in Permit.io, and let users perform that call only if they have the right permissions. If you are not a Permit.io user yet, go to app.permit.io and create a free account.

After signing in, you'll have to go to FoAz Proxy page and click Add Configuration.

This is how the configuration page should look like:

 ![proxy_config.png](https://media.graphassets.com/Zm6QcASISGKPmJ0UU64D "proxy\_config.png")

Let's go over the fields to make sure we understand what we are doing here:

1. In the URL field, enter `api.nexmo.com/v1/messages`. This is the API endpoint we will use to send messages.

2. In the `HTTP Method` field, select `POST`. This is the HTTP method we will use to send messages.

3. In the `Select Resource` field, choose to create a new resource and call it `Message`. This is the name of the resource we will use to give permissions.

4. When you create the resource, also assign one action to it. Let's call it `Send`.

5. After creating the resource, Choose the `Send` action in the `Select Action` field.

6. In the `Secret Configuration` field, choose `Basic` and insert the API key and secret you got from Vonage in the relevant fields.

At this point, we configure the FoAz proxy to support our call to Vonage, we can now configure the permissions for our users.

### Assigning Permissions to Users

One of the easiest methods to check if a user has permission to perform an action is to use the Role-Based Access Control (RBAC) model. In RBAC, we assign role(s) to users, and then we can define which roles can perform which actions on which resources.

1. In the Permit.io Policy page, go to the Roles tab and create two roles: Admin and User.
 ![roles_permit.png](https://media.graphassets.com/0XbGhU81QGKUNgEvczCH "roles\_permit.png")
2. Let's go to the Clerk dashboard, and find the User ID of the admin user we created earlier. You can find the User ID in the User Profile page.
 ![copy_user_id.png](https://media.graphassets.com/InQfAExCTk2XD8OIiSIP "copy\_user\_id.png")
3. In Permit.io, go to the Users tab and create a new user with the User ID of the admin user we found in the previous step, and assign the Admin role to the user.
 ![permit_create_user.png](https://media.graphassets.com/ZRa305T6TD2x2ZRetInE "permit\_create\_user.png")
4. Repeat the previous two steps for the regular user we created earlier, but assign the User role to the user.

Now that we have our users and roles, we can assign permissions to them. In Permit.io, go to the Policy Editor. In the Admin check the box next to the Send action we created earlier for the Message resource.

 ![Screenshot 2023-05-31 at 16.48.37.png](https://media.graphassets.com/L6BddS5R2aKdEywfFVki "Screenshot 2023-05-31 at 16.48.37.png")
### Configure Clerk JWKs in Permit.io

Now, that we have all our permissions configured, we need to configure the way that Permit.io will verify the JWT token that Clerk generates for our users. With our users authenticated, Clerk generates a JWT token for them. Permit.io is using Clerk JWKs to verify the JWT token, so we need to configure the JWKs in Permit.io.

1. In the Clerk dashboard, go to `JWT Template` page and click `New Template`. This template will use to configure JWTs that are generated for FoAz.
 ![clerk_add_application.png](https://media.graphassets.com/Q1XGH8VTXmNTLgNobsRx "clerk\_add\_application.png")
2. In the popup opened, click `Blank`
 ![jwt_template_blank.png](https://media.graphassets.com/zRL6ptzQQqGHfoBolgaQ "jwt\_template\_blank.png")
3. Give our token a name and leave all the other fields with their default values.
 ![jwt_template_new.png](https://media.graphassets.com/kLBuf2EcRbm2jVUoPMhR "jwt\_template\_new.png")
4. Copy the `JWKS Endpoint` URL and open it in a new tab.
5. In Permit.io, Go to the `Settings` screen, and then in the `JWKs Config` tab, click `Configure` on the Environment you use (you can see the active environment in the left sidebar)
 ![configure_jwk_permit.png](https://media.graphassets.com/a7L4IRtkT9eqysPc12L3 "configure\_jwk\_permit.png")
6. In the popup, paste the content from the JWKS Endpoint page we opened in the previous step, and click `Save`.
 ![configure_jwks_popup.png](https://media.graphassets.com/bAtkcLQHRe6eu85YrS7C "configure\_jwks\_popup.png")

Now that we are done will the configuration, let's go back to our application code and send our first message!

## Sending Messages from the Browser

First, to send a message, let's create the UI components that will use as a form for sending messages. To make our UI look nicer, we will install Material UI, a popular React UI framework for fancy UI components.

In the terminal, run the following command to install Material UI:

```bash
npm install @material-ui/core
```

Now, let's create the UI components for sending messages. In the SendMessage function in App.tsx file, replace the return statement of the function with the following code:

```tsx
<>
    <Container maxWidth="sm">
        <Paper component={'form'} sx={{ p: 1 }} onSubmit={send}>
            {error && <Alert severity='error' sx={{ mb: 1 }} onClose={() => (setError(''))}>{error}</Alert>}
            {success && <Alert severity='success' sx={{ mb: 1 }} onClose={() => (setSuccess(''))}>{success}</Alert>}
            <Box sx={{ display: 'flex' }}>
                <FormControl sx={{ mb: 1, flex: 1 }}>
                    <TextField label='To' id="to" value={to} onChange={(e) => setTo(e.target.value)} />
                </FormControl>
            </Box>
            <Box sx={{ display: 'flex' }}>
                <FormControl sx={{ m: 0, minWidth: 200, flex: 1 }}>
                    <TextField label='message' id="message" multiline rows={4} value={message} onChange={(e) => setMessage(e.target.value)} />
                </FormControl>
            </Box>
            <Button variant="contained" type="submit" fullWidth sx={{ mt: 1 }}>Send</Button>
        </Paper>
        <Button variant='outlined' fullWidth sx={{ mt: 1 }} onClick={() => (signOut())}>Sign Out</Button>
    </Container>
</>
```

In the top of the function, add the following assignemetns and an empty send function:

```tsx
const [error, setError] = useState < string | null > ('');
const [success, setSuccess] = useState < string | null > ('');
const [to, setTo] = useState < string > ('');
const [message, setMessage] = useState < string > ('');

const send = async (e: FormEvent<HTMLFormElement>) => {};
```

Going back to the browser, you should see a message sending form like this:

 ![message_form.png](https://media.graphassets.com/K0B3sjpjSjSrmEsrrSMB "message\_form.png")

Now, let's fill the code that will send the message. In the App.tsx file, add the following code to the SendMessage function ([you can see the whole file version here](https://github.com/permitio/foaz-vonage-sample/blob/main/src/App.tsx "https://github.com/permitio/foaz-vonage-sample/blob/main/src/App.tsx")):

```tsx
e.preventDefault();
const token = await getToken();
if (!token) {
    setError('No token');
    return;
}
try {
    const res = await fetch('PASTE_YOUR_URL_FROM_FOAZ_CONFIG', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${token}`,
        },
        body: JSON.stringify({
            from: '14157386102',
            to,
            text: message,
            channel: 'sms',
            message_type: 'text',
        }),
    });
    if (res.status !== 202) {
        const body = await res.json();
        setError(body?.detail || 'Error sending message');
        return;
    }
    setSuccess('Message sent');
} catch (error: any) {
    setError(error.message);
    return;
}
```

As you can see, the fetch function we added, is empty. We should grab the fetch config from Permit.io proxy settings. You can get the code easily by clicking the copy button on the FoAz proxy page, and pasting it in the code.

 ![copy_sample.png](https://media.graphassets.com/OJkIg0HgSGytRcSxI9k7 "copy\_sample.png")

Returning to the browser, let's try to send a message.

 ![success_message.png](https://media.graphassets.com/ncY7zbTQtit60vHRXMNn "success\_message.png")

And, it works!

To ensure our safety with FoAz permissions, let's log out with the admin user and log in with another user who doesn't have a role in Permit.io (you can use the user@foaz-messaging.app user we created earlier).

Now, when we try to send a message, it fails!

 ![failed_message.png](https://media.graphassets.com/ilR2dn5lQzKaaShwYbsZ "failed\_message.png")

🥳 Congratulations! You just sent your first message from the browser without any backend code!

## What Next?

In this article, we touched on just the tip of what FoAz can do. Even if we continue with the SMS-sending example, there are many things we can do to improve our application. Sending messages in different channels and limiting the send only for particular users, limiting roles to send only to particular countries, and more.

But, FoAz is not limited to SMS sending. You can use FoAz to access any API that requires authentication, and you can use it to build a complete application without any backend code. And, you can use it with any frontend framework, not just React.

We hope your enjoyed this article and already thinking about how you can use FoAz in your next project. To stay in touch and contribute to the revolution of frontend development, join our community at [io.permit.io/foaz-community](https://io.permit.io/foaz-community "https://io.permit.io/foaz-community")