@civic/auth v0.7.0
Civic Auth Client SDK
Civic Auth offers a simple, flexible, and fast way to integrate authentication into your applications.
You can find the full, official documentation here, with a quick-start version in this README.
Note: This package is only published in ESM format. No CJS build is available.
Quick Start
Sign up for Civic Auth in less than a minute at auth.civic.com to get your Client ID.
Install the library in your app using your installer of choice:
npm
npm install @civic/authyarn
yarn add @civic/authpnpm
pnpm install @civic/authbun
bun add @civic/authIntegration
Choose your framework for instructions on how to integrate Civic Auth into your application.
For integrating in other environments using any OIDC or OAuth 2.0-compliant client libraries, see here.
React
Integrate Civic Auth into your React application with ease, just wrap your app with the Civic Auth provider and add your Client ID (provided after you sign up). A working example is available in our github examples repo.
import { CivicAuthProvider, UserButton } from "@civic/auth/react";
function App({ children }) {
return (
<CivicAuthProvider clientId="YOUR CLIENT ID">
<UserButton />
{children}
</CivicAuthProvider>
)
}Usage
The User Button
The Civic Auth SDK comes with a multi-purpose styled component called the UserButton
import { UserButton, CivicAuthProvider } from '@civic/auth/react';
export function TitleBar() {
return (
<div className="flex justify-between items-center">
<h1>My App</h1>
<UserButton />
</div>
);
};
function App({ children }) {
return (
<CivicAuthProvider clientId="YOUR CLIENT ID">
<TitleBar />
</CivicAuthProvider>
)
}This component is context-dependent. If the user is logged in, it will show their profile picture and name. If the user is not logged in, it will show a Log In button.
Getting User Information on the Frontend
Use the Civic Auth SDK to retrieve user information on the frontend.
import { useUser } from '@civic/auth/react';
export function MyComponent() {
const { user } = useUser();
if (!user) return <div>User not logged in</div>
return <div>Hello { user.name }!</div>
}We use name as an example here, but you can call any user object property from the user fields schema, as shown below.
Advanced Configuration
Civic Auth is a "low-code" solution, so all configuration takes place via the dashboard. Changes you make there will be updated automatically in your integration without any code changes. The only required parameter you need to provide is the client ID.
The integration provides additional run-time settings and hooks that you can use to customize the library's integration with your own app. If you need any of these, you can add them to the CivicAuthProvider as follows:
<CivicAuthProvider
clientId="YOUR CLIENT ID"
...other configuration
>See below for the list of all configuration options
Display Mode
The display mode indicates where the Civic login UI will be displayed. The following display modes are supported:
iframe(default): the UI loads in an iframe that shows in an overlay on top of the existing page contentredirect: the UI redirects the current URL to a Civic login screen, then redirects back to your site when login is completenew_tab: the UI opens in a new tab or popup window (depending on browser preferences), and after login is complete, the tab or popup closes to return the user to your site
API
User Context
The full user context object (provided by useUser) looks like this:
{
user: User | null;
// these are the OAuth tokens created during authentication
idToken?: string;
accessToken?: string;
refreshToken?: string;
forwardedTokens?: ForwardedTokens;
// functions and flags for UI and signIn/signOut
isLoading: boolean;
authStatus: AuthStatus;
error: Error | null;
signIn: (displayMode?: DisplayMode) => Promise<void>;
signOut: () => Promise<void>;
}User
The User object looks like this:
type BaseUser = {
id: string;
email?: string;
name?: string;
picture?: string;
given_name?: string;
family_name?: string;
updated_at?: Date;
};
type UnknownObject = Record<string, unknown>;
type EmptyObject = Record<string, never>;
type User<T extends UnknownObject | EmptyObject = EmptyObject> =
T extends EmptyObject ? BaseUser : BaseUser & T;Where you can pass extra user attributes to the object that you know will be present in user claims, e.g.
const UserWithNickName = User<{ nickname: string }>;Field descriptions:
Base User Fields
Token Fields
Typically developers will not need to interact with the token fields, which are used only for advanced use cases.
Forwarded Tokens
Use forwardedTokens if you need to make requests to the source provider, such as find out provider-specific information.
\ An example would be, if a user logged in via Google, using the Google forwarded token to query the Google Groups that the user is a member of.
For example:
const googleAccessToken = user.forwardedTokens?.google?.accessToken;Embedded Login Iframe
If you want to have the Login screen open directly on a page without the user having to click on button, you can import the CivicAuthIframeContainer component and use it along with the AuthProvider option iframeMode={"embedded"}
You just need to ensure that the CivicAuthIframeContainer is a child under a CivicAuthProvider
import { CivicAuthIframeContainer } from "@civic/auth/react";
const Login = () => {
return (
<div class="login-container">
<CivicAuthIframeContainer />
</div>
);
};
const App = () => {
return (
<CivicAuthProvider
clientId={"YOUR CLIENT ID"}
iframeMode={"embedded"}
>
<Login />
</CivicAuthProvider>
);
}Next.JS
Quick Start
Integrate Civic Auth into your Next.js application using the following steps (a working example is available in our github examples repo):
1. Add the Civic Auth Plugin
This is where you give your app the Client ID provided when you sign up at auth.civic.com.
The defaults should work out of the box for most customers, but if you want to configure your app, see below for details.
import { createCivicAuthPlugin } from "@civic/auth/nextjs"
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
const withCivicAuth = createCivicAuthPlugin({
clientId: 'YOUR CLIENT ID'
});
export default withCivicAuth(nextConfig)2. Create an API Route
This is where your app will handle login and logout requests.
Create this file at the following path:
src/app/api/auth/[...civicauth]/route.ts
import { handler } from '@civic/auth/nextjs'
export const GET = handler()These steps apply to the App Router. If you are using the Pages Router, please contact us for integration steps.
3. Middleware
Middleware is used to protect your backend routes, server components and server actions from unauthenticated requests.
Using the Civic Auth middleware ensures that only logged-in users have access to secure parts of your service.
import { authMiddleware } from '@civic/auth/nextjs/middleware'
export default authMiddleware();
export const config = {
// include the paths you wish to secure here
matcher: [
/*
* Match all request paths except:
* - _next directory (Next.js static files)
* - favicon.ico, sitemap.xml, robots.txt
* - image files
*/
'/((?!_next|favicon.ico|sitemap.xml|robots.txt|.*\\.jpg|.*\\.png|.*\\.svg|.*\\.gif).*)',
],
}Token Handling Behavior:
- Valid tokens: User passes through to protected routes
- Refresh token available: Automatically redirects to refresh endpoint to rehydrate session with
targetUrlparameter - Expired tokens without refresh: Redirects to logout callback to clear bad tokens, then to login
- No tokens: Redirects to login URL
- Login URL access: Always allowed regardless of authentication state to prevent redirect loops
Middleware Chaining
If you are already using middleware in your Next.js app, then you can chain them with Civic Auth as follows:
import { auth } from '@civic/auth/nextjs'
import { NextRequest, NextResponse } from "next/server";
const withCivicAuth = auth()
const otherMiddleware = (request: NextRequest) => {
console.log('my middleware')
return NextResponse.next()
}
export default withCivicAuth(otherMiddleware)4. Frontend Integration
Add the Civic Auth context to your app to give your frontend access to the logged-in user.
import { CivicAuthProvider } from "@civic/auth/nextjs";
function Layout({ children }) {
return (
// ... the rest of your app layout
<CivicAuthProvider>
{children}
</CivicAuthProvider>
)
}Unlike the pure React integration, you do not have to add your client ID again here!
Usage
Getting User Information on the Frontend
Next.JS client components can use the Civic Auth React tools to obtain user information as well as display convenient login, and logout buttons. See the React Usage page for details.
Getting User Information on the Backend
Retrieve user information on backend code, such as in React Server Components, React Server Actions, or api routes using getUser:
import { getUser } from '@civic/auth/nextjs';
const user = await getUser();For example, in a NextJS Server Component:
import { getUser } from '@civic/auth/nextjs';
export async function MyServerComponent() {
const user = await getUser();
if (!user) return <div>User not logged in</div>
return <div>Hello { user.name }!</div>
}The name property is used as an example here, check out the React Usage page to see the entire basic user object structure.
Advanced Configuration
Civic Auth is a "low-code" solution, so most of the configuration takes place via the dashboard. Changes you make there will be updated automatically in your integration without any code changes. The only required parameter you need to provide is the client ID.
The integration also offers the ability customize the library according to the needs of your Next.js app. For example, to restrict authentication checks to specific pages and routes in your app. You can do so inside next.config.js as follows:
const withCivicAuth = createCivicAuthPlugin({
clientId: 'YOUR CLIENT ID'
...other config
});
export default withCivicAuth(nextConfig) // your next config hereHere are the available configuration options:
Server Integration
Using the CivicAuth Interface
For server-side integration, you can use the new unified CivicAuth interface which provides a full set of authentication methods with built-in token validation.
Quick Start
import { CivicAuth, CookieStorage } from '@civic/auth/server';
// Define your authentication configuration
const config = {
clientId: 'YOUR_CLIENT_ID',
redirectUrl: 'http://yoursite.com/auth/callback',
oauthServer: 'https://auth.civic.com/oauth',
postLogoutRedirectUrl: 'http://yoursite.com/auth/logoutcallback',
};
// Create a storage adapter that implements AuthStorage interface
class YourStorageAdapter extends CookieStorage {
// Implement the storage methods (get, set, delete)
// to work with your server framework's cookie system
}
// Create the Civic Auth instance
const storage = new YourStorageAdapter();
const civicAuth = new CivicAuth(storage, config);Framework Examples
The Civic Auth SDK includes examples for popular server frameworks including Express, Fastify, and Hono.
Express Example
import express from 'express';
import cookieParser from 'cookie-parser';
import { CookieStorage, CivicAuth } from '@civic/auth/server';
// Extend Express Request to include civicAuth instance
declare global {
namespace Express {
interface Request {
storage: ExpressCookieStorage;
civicAuth: CivicAuth;
}
}
}
const config: AuthConfig = {
clientId: process.env.CLIENT_ID!,
redirectUrl: `https://<your server>/auth/callback`,
postLogoutRedirectUrl: `https://<your server>/auth/logoutcallback`,
};
const app = express();
app.use(cookieParser());
// Create middleware to attach CivicAuth to each request
app.use((req, res, next) => {
const storage = new ExpressCookieStorage(req, res);
req.storage = storage;
req.civicAuth = new CivicAuth(storage, config);
next();
});
// Protected route example
app.get('/admin/profile', async (req, res) => {
if (!(await req.civicAuth.isLoggedIn())) {
return res.status(401).send('Unauthorized');
}
const user = await req.civicAuth.getUser();
res.send(`Hello, ${user?.name || user?.email || 'User'}`);
});
// Authentication route
app.get('/auth/login', async (req, res) => {
const url = await req.civicAuth.buildLoginUrl();
res.redirect(url.toString());
});
// Handle post-logout callback and clear session
app.get('/auth/logoutcallback', async (req: Request, res: Response) => {
const { state } = req.query as { state: string };
console.log(`Logout-callback: state=${state}`);
await req.civicAuth.clearTokens();
res.redirect('/');
});Available Methods
The CivicAuth interface provides the following methods:
| Method | Description |
|---|---|
getUser() | Gets the authenticated user with token validation |
getTokens() | Gets the authentication tokens with token validation |
resolveOAuthAccessCode(code, state) | Resolves an OAuth access code to a set of OIDC tokens |
isLoggedIn() | Checks if the user is currently logged in |
buildLoginUrl(options?) | Builds a login URL to redirect the user to |
buildLogoutRedirectUrl(options?) | Builds a logout URL to redirect the user to |
refreshTokens() | Refreshes the current set of OIDC tokens |
clearTokens() | Clears all authentication tokens from storage |
Each method is designed to be used in a server-side context and includes proper validation of tokens to ensure security.
Server-Side Token Validation
The CivicAuth interface automatically validates tokens for you when using getUser() or getTokens() methods, providing an extra layer of security for your server-side authentication logic.
Authentication Storage and State
Civic Auth SDK maintains several storage entries to track authentication state. Understanding these entries is helpful for debugging and understanding the authentication flow. These tokens can be stored either in cookies (server-side token exchange) or in local storage (client-side token exchange) depending on your configuration.
Storage Entries
| Entry | Purpose | Required for Authentication | Notes |
|---|---|---|---|
id_token | Contains the ID token that verifies the user's identity | Yes | Primary token used for authentication |
access_token | Contains the OAuth2 access token | No | Optional as of version 0.6.0 - used for API access |
refresh_token | Used to refresh tokens when they expire | No | Stored as a session-only entry with no expiry |
access_token_expires_at | Tracks when the access token expires | No | Used for client-side token refresh scheduling |
Authentication Requirements
For a user to be considered authenticated, the SDK requires:
- A valid
id_tokento be present in storage - The ID token must not be expired
Token Validation
The SDK validates tokens using the following checks:
- Signature verification against the OAuth provider's public keys
- Expiration time validation
- Issuer validation
- Audience validation
- Additional optional claim validations based on your application's requirements
If token validation fails, the user will be automatically redirected to re-authenticate.
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago