Follow these simple steps to set up Civic Auth with a backend.
1. Install dependencies
npm install @civic/auth @fastify/cookie
yarn add @civic/auth @fastify/cookie
pnpm install @civic/auth @fastify/cookie
bun add @civic/auth @fastify/cookie
2. Configure your App
Your app will need the following configuration:
const config = {
clientId: // Client ID from auth.civic.com
redirectUrl: 'http://localhost:3000/auth/callback', // change to your domain when deploying
postLogoutRedirectUrl: 'http://localhost:3000/' // The postLogoutRedirectUrl is the URL where the user will be redirected after successfully logging out from Civic's auth server.
};
Note: redirectUrl and postLogoutRedirectUrl must be absolute URLs.
3. Set up Cookies
Civic Auth uses cookies for storing the login state by default
import Fastify, { FastifyReply, FastifyRequest } from 'fastify';
import fastifyCookie from '@fastify/cookie';
import { CookieStorage, CivicAuth } from '@civic/auth/server';
const fastify = Fastify().register(fastifyCookie, {
secret: "my-secret", // should be changed in production
parseOptions: {}
})
class FastifyCookieStorage extends CookieStorage {
constructor(private request: FastifyRequest, private reply: FastifyReply) {
super();
}
async get(key: string): Promise<string | null> {
return Promise.resolve(this.request.cookies[key] ?? null);
}
async set(key: string, value: string): Promise<void> {
await this.reply.setCookie(key, value, this.settings);
}
}
// attach an instance of the cookie storage and civicApi to each request
fastify.decorateRequest('storage', null);
fastify.decorateRequest('civicAuth', null);
fastify.addHook('preHandler', async (request, reply) => {
request.storage = new FastifyCookieStorage(request, reply);
request.civicAuth = new CivicAuth(request.storage, config);
});
4. Create a Login Endpoint
This endpoint will handle login requests, build the Civic login URL and redirect the user to it.
import { isLoggedIn } from '@civic/auth/server';
fastify.addHook('preHandler', async (request, reply) => {
// apply to whichever routes need it
if (!request.url.includes('/admin')) return;
const loggedIn = await request.civicAuth.isLoggedIn();
if (!loggedIn) return reply.status(401).send({ error: 'Unauthorized' });
});
8. Use the Session
If needed, get the logged-in user information.
import { user } from '@civic/auth/server';
fastify.get('/admin/hello', async (request, reply) => {
const user = await request.civicAuth.getUser();
return `Hello, ${user?.name}!`;
});
PKCE and Client Secrets
When using the Civic Auth SDK, PKCE is handled entirely by the library.
Civic Auth uses , to protect users and clients from unauthorized access to user information. This, alongside domain registration for apps in production environments, mean that you don't need to provide a client secret in your backend.