Skip to main content

Authentication

There are two different types of authentication for the Light APIs. Both types utilize tokens passed as Bearer tokens in the Authorization header of HTTP requests. For example, call GET /v1/app/accounts with your app token to see your app accounts.

const response = await fetch("https://api.light.dev/v1/app/accounts", {
headers: {
Authorization: `Bearer ${process.env.LIGHT_APP_TOKEN}`,
},
});

App Tokens vs Account Tokens

The first type of auth token is an AppToken, which is a long-lived token that is generated per-application that you build on Light. These tokens can be used for privileged APIs such as creating new Accounts for your App, editing sensitive details about those accounts such as their verified email address, and generating AccountTokens to interact further with the accounts. AppTokens should be kept secret and only used from a secure environment like your backend server.

The second type of token is an AccountToken. These are tokens that you generate using your AppToken for a specific account (i.e. user of your App). AccountTokens are not long-lived and will expire after 60 minutes (or at the expires_at timestamp given in the POST /app/accounts/{uuid}/token API response). These tokens can only access information or affect a single user account of your app and can be used either from a backend server or a frontend client. AccountTokens can be used from a frontend client to speed up and simplify your integration by avoiding proxying all requests through your server. However, you can also use these from your server if you prefer.

Using Account Tokens

In order to use AccountTokens from your frontend client, you will need to provide a way for your client to fetch a new AccountToken periodically as it expires. We would recommend a pattern similar to the following, where you can serve a Light AccountToken for a given user on your platform via API.

1. Backend API to generate Account Tokens

This example includes optional caching of the AccountToken to avoid making unnecessary calls to the API. However if you are caching on your frontend client too, then you can likely skip caching on your backend.

app.post("/energy/token", async (req, res) => {
// Save a Light account UUID to your user model once you have created
// a Light account for the user. (Or if your users can have multiple locations,
// then save the Light account UUID for each location.)
const lightAccountUuid = req.user.lightAccountUuid;
if (!lightAccountUuid) {
throw new Error("Light account not created");
}

// Cache the per-account tokens since they are valid for 1 hour
const cacheKey = `energy-token-${lightAccountUuid}`;
const cachedToken = await cache.get(cacheKey);
if (cachedToken) {
return res.json(cachedToken);
}

// POST /app/accounts/{light_account_uuid}/token
const response = await fetch(
`https://api.light.dev/v1/app/accounts/${lightAccountUuid}/token`,
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LIGHT_APP_TOKEN}`,
"Content-Type": "application/json",
},
},
);

const tokenData = await response.json();

// cache for 50 minutes giving a 10 minute buffer before expiration
await cache.set(cacheKey, tokenData, 50 * 60);

res.json(tokenData);
});
Example Response
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_at": "2024-01-15T11:30:00Z"
}

See the API reference for the complete response format.

2. Frontend client to use Account Tokens

Your front-end would then call the above /energy/token endpoint that you implemented and then use the resulting Account Token for calling Light API endpoints. You may also want to cache Account Tokens on the client to avoid having to call your /energy/token endpoint before each client-side call to the Light API. For example, in JavaScript you could have an API helper like this:

import React, { useState } from "react";

interface EnergyToken {
token: string | null;
expiry: string | null;
}

const useEnergyToken = () => {
const [energyToken, setEnergyToken] = useState<EnergyToken>({
token: null,
expiry: null,
});

const getCachedToken = (): string | null => {
const { token, expiry } = energyToken;
return token && expiry && new Date() < new Date(expiry) ? token : null;
};

const fetchToken = async (): Promise<string> => {
const response = await fetch("/energy/token", {
method: "POST",
credentials: "include",
});
if (!response.ok) throw new Error("Failed to fetch token");
const { token, expires_at } = await response.json();

// cache the token and expiration date
setEnergyToken({ token, expiry: expires_at });
return token;
};

const getToken = async (): Promise<string> => {
return getCachedToken() || (await fetchToken());
};

return { getToken };
};

export default useEnergyToken;

3. Example client usage

You can then use the above helper in your frontend API calls like this:

import useEnergyToken from "./useEnergyToken";

// Example component that uses the account token
const AccountDataComponent = () => {
const { getToken } = useEnergyToken();

const fetchAccountData = async () => {
try {
const token = await getToken();
const response = await fetch("https://api.light.dev/v1/account", {
method: "GET",
headers: { Authorization: `Bearer ${token}` },
});
if (!response.ok) throw new Error("Failed to fetch account data");
const data = await response.json();
console.log("Account data:", data);
} catch (error) {
console.error("Error fetching account data:", error);
}
};

return (
<div>
<button onClick={fetchAccountData}>Fetch Account Data</button>
</div>
);
};

export default AccountDataComponent;