Civic Docs
Civic Auth
Civic Auth
  • 🔏Civic Auth
  • Integration
    • React
    • Next.JS
    • Node.JS
      • Express
      • Hono
      • Fastify
    • Other OIDC / OAuth 2.0-Compliant Environments
    • FAQs
    • Error Codes
    • Bring Your App to Production
  • Web3
    • Embedded Wallets
    • Ethereum / EVM
    • Solana
Powered by GitBook
On this page
  • 1. Install dependencies
  • 2. Configure your App
  • 3. Set up Cookies
  • 4. Create a Login Endpoint
  • 5. Create the Callback Endpoint
  • 6. Create a Logout Endpoint
  • 7. Add an Authentication Hook
  • 8. Use the Session
  • PKCE and Client Secrets

Was this helpful?

  1. Integration
  2. Node.JS

Fastify

PreviousHonoNextOther OIDC / OAuth 2.0-Compliant Environments

Last updated 2 months ago

Was this helpful?

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 { buildLoginUrl } from '@civic/auth/server';

fastify.get('/', async (request, reply) => {
  const url = await request.civicAuth.buildLoginUrl();

  return reply.redirect(url.toString());
});

5. Create the Callback Endpoint

This endpoint handles successful logins and creates the session

import { resolveOAuthAccessCode } from '@civic/auth/server';

fastify.get<{
  Querystring: { code: string, state: string }
}>('/auth/callback', async (request, reply) => {
  await request.civicAuth.resolveOAuthAccessCode(request.query.code, request.query.state);
  reply.redirect('/admin/hello');
});

6. Create a Logout Endpoint

This endpoint will handle logout requests, build the Civic logout URL and redirect the user to it.

import { buildLogoutRedirectUrl } from '@civic/auth/server';

app.get('/auth/logout', async (req: Request, res: Response) => {
  const url = await request.civicAuth.buildLogoutRedirectUrl();
  res.redirect(url.toString());
});

7. Add an Authentication Hook

This hook protects routes that require login.

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.

Fastify
PKCE (Proof Key for Code Exchange)