Parse Server Nextjs is an authentication library for Next.js that integrates with Parse Server. It provides a complete solution for handling authentication in your Next.js applications, supporting both credentials-based, custom auth adapter and OAuth authentication.
- Multiple Authentication: Support for credentials (email/password), custom auth providers and OAuth authentication
- Session Management: Automatic user session handling
- React Hooks: Custom hooks for easy client-side integration
- Next.js Middleware: Route protection with authentication middleware
- TypeScript: Full TypeScript support with included types
Install the package via npm:
npm install parse-server-nextjsFirst, set up your environment variables:
PARSE_SERVER_URL=your_parse_server_url
PARSE_APPLICATION_ID=your_application_id
SESSION_COOKIE_NAME=your_cookie_name
SESSION_SECRET=your_session_secretCreate an api route named app/api/auth/[...psnjs-auth]/route.ts
import { createAuth } from "parse-server-nextjs";
const auth = createAuth();
export const GET = auth.handlers.GET;
export const POST = auth.handlers.POST;This setup automatically creates the following API endpoints under /api/auth:
GET /api/auth/session: Retrieves the current user's session data if they are authenticated. Returnsnullif not authenticated.POST /api/auth/signup: Handles user registration. Expects{ authType: 'credentials' | 'third-party', data: AuthData }in the request body.AuthDatawill beCredentialsAuthorThirdPartyAuthdepending on theauthType.POST /api/auth/signin: Handles user login. Expects{ authType: 'credentials' | 'third-party', data: AuthData }in the request body.POST /api/auth/logout: Logs out the current user and destroys their session.POST /api/auth/challenge: Used for custom authentication flows that require a challenge step (e.g., fetching a nonce). Expects{ data: unknown }in the request body and returns challenge-specific data.GET /api/auth/oauth/[provider]: Initiates the OAuth flow by redirecting the user to the specified OAuth provider's authorization page (e.g.,/api/auth/oauth/github).GET /api/auth/callback/[provider]: The callback URL that the OAuth provider redirects to after the user grants or denies access. This endpoint handles the exchange of the authorization code for user data and authenticates the user.
The useAuth hook provides all necessary methods for handling authentication:
-
signup(authType: 'credentials' | 'third-party', data: AuthData): Registers a new user
- For credentials auth:
datashould containemailandpassword - For third-party auth:
datashould contain provider-specific auth data - Returns
AuthResponsewith session data on success
- For credentials auth:
-
login(authType: 'credentials' | 'third-party', data: AuthData): Authenticates an existing user
- Same parameters as signup
- Returns
AuthResponsewith session data on success
-
challenge(data: unknown): Initiates a challenge flow (e.g., for SIWE)
data: Challenge-specific data- Returns
ChallengeResponse<T>with challenge data
-
logout(): Ends the current user session
- Returns
AuthResponseindicating success
- Returns
-
getSession(): Retrieves the current session data
- Returns
AuthResponsewith session if authenticated
- Returns
-
getOAuthUrl(provider: string): Generates OAuth redirect URL
provider: Name of OAuth provider (e.g., 'github')- Returns URL string for OAuth flow
-
loading: Boolean state indicating if an auth operation is in progress
import { useAuth } from 'next-parse-auth/client';
function LoginComponent() {
const { login, loading } = useAuth();
const handleLogin = async () => {
const response = await login('credentials', {
email: 'user@example.com',
password: 'password'
});
if (response.success) {
// Handle successful login
}
};
return (
// Your login component
);
}Next Parse Auth provides a SessionProvider and useSession hook for easy session state management across your application.
Wrap your application with SessionProvider to enable session state management:
// app/layout.tsx
import { SessionProvider } from "parse-server-nextjs/client";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<SessionProvider>{children}</SessionProvider>
</body>
</html>
);
}The useSession hook provides easy access to the current session state:
import { useSession } from "parse-server-nextjs/client";
function ProfileComponent() {
const { data, status } = useSession();
if (status === "loading") {
return <div>Loading...</div>;
}
if (status === "unauthenticated") {
return <div>Please sign in</div>;
}
return <div>Welcome {data?.userId}</div>;
}The hook returns:
data: The session data (null if not authenticated)status: One of three states:'loading': Initial session loading'authenticated': User is signed in'unauthenticated': No active session
refresh: Function to manually refresh the session from the serversetSession: Function to manually set session dataclearSession: Function to immediately clear the session locally without making an HTTP request
When performing a logout, you can use clearSession() to immediately update the session state locally instead of waiting for the automatic polling or making an unnecessary HTTP request. This is particularly useful when you've already triggered the logout yourself:
import { useAuth, useSession } from "parse-server-nextjs/client";
function LogoutButton() {
const { logout } = useAuth();
const { clearSession } = useSession();
const handleLogout = async () => {
await logout(); // Logout on server
clearSession(); // Immediately clear local session state
};
return <button onClick={handleLogout}>Logout</button>;
}Protect your application routes using the provided authMiddleware. Configure the matcher in config to specify which routes require authentication.
// File: middleware.ts
import { authMiddleware } from "parse-server-nextjs/middleware";
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
* - public routes (login, signup, etc.)
* - auth API routes (/api/auth/...)
* - home page ($) - adjust if your home page needs auth
*/
"/((?!_next/static|_next/image|favicon.ico|login|signup|terms-of-service|privacy|api/auth|$).*)",
],
};
export { authMiddleware as middleware };Use the auth() function in server components or route handlers to protect server-side resources:
import { auth } from "parse-server-nextjs";
export default function ProtectedPage() {
const session = await auth();
if (!session) {
return unauthorized();
}
return (
<div className="bg-muted flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
sample
</div>
);
}const { login } = useAuth();
await login("credentials", {
email: "user@example.com",
password: "password",
});Exemple of parse-server-otp-auth-adapter
const { challenge, login } = useAuth();
const result = await challenge({ otp: { email: data.email } });
// next login
const result = await login("third-party", {
providerName: "otp",
authData: { email: "your-email", otp: "one time password", id: "your-email" },
});To enable OAuth authentication, you need to configure the OAuth providers in your application. Here's an example using GitHub as a provider:
app/api/auth/[...next-parse-auth]/route.ts
import { createAuth } from "parse-server-nextjs";
import { environments } from "@/config/env";
const oAuthProviders = {
github: {
authorizeUrl: `https://github.com/login/oauth/authorize?client_id=${environments.GITHUB_CLIENT_ID}&redirect_uri=${environments.GITHUB_REDIRECT_URI}&scope=${environments.GITHUB_SCOPE}`,
callBackFunction: async (
providerName: string,
data: { code: string; codeVerifier?: string }
) => {
// Example: Exchange code for token using codeVerifier if PKCE is enabled
// const token = await exchangeCodeForToken(data.code, data.codeVerifier);
return {
providerName,
authData: {
// Include necessary auth data for Parse Server's `signInWith`
code: data.code,
// Potentially include access_token or other relevant data
},
};
},
// Enable PKCE for enhanced security (optional)
pkce: true,
},
};
const auth = createAuth({
oAuthProviders,
});
export const GET = auth.handlers.GET;
export const POST = auth.handlers.POST;The authorizeUrl is the URL that users will be redirected to when initiating the OAuth flow. For github this URL typically includes:
client_id: Your application's ID registered with the OAuth providerredirect_uri: The URL where the provider will redirect back to after authorizationscope: The permissions your app is requesting- Any other provider-specific parameters
The callBackFunction is the function called right after being redirect from your third party. All query params are return as data. This function can be synchronous or asynchronous (return a Promise). If PKCE is enabled (pkce: true), the data object will also contain the codeVerifier which might be needed to exchange the authorization code for an access token. The return object will be used for the signInWith method of parse server.
The pkce: true option enables Proof Key for Code Exchange (PKCE), an extension to the Authorization Code flow which enhances security, especially for public clients like SPAs. When enabled, the library automatically handles the generation of code_verifier and code_challenge.
const { getOAuthUrl } = useAuth();
// Redirects to OAuth provider
window.location.href = getOAuthUrl("github");The OAuth flow supports preserving the user's original destination through the from parameter. This ensures users are redirected back to their intended page after authentication instead of a default landing page.
The redirection follows this priority order:
fromparameter (highest priority) - The original URL the user was trying to accessafterLoginRedirect- Provider-specific redirect URL configured inOAuthConfig- Default redirect - Falls back to
"/"if no other option is specified
Basic OAuth without context:
// User will be redirected to "/" or `afterLoginRedirect` after auth
window.location.href = getOAuthUrl("github");OAuth with contextual redirection:
import { useSearchParams } from "next/navigation";
function LoginComponent() {
const { getOAuthUrl } = useAuth();
const searchParams = useSearchParams();
// Get the 'from' parameter (usually set by middleware)
const fromParam = searchParams.get("from");
// Add 'from' parameter to OAuth URL
const githubUrl = fromParam
? `${getOAuthUrl("github")}&from=${encodeURIComponent(fromParam)}`
: getOAuthUrl("github");
return <a href={githubUrl}>Sign in with GitHub</a>;
}Automatic flow via middleware:
// User tries to access: /dashboard
// Middleware redirects to: /login?from=/dashboard
// OAuth URL becomes: /api/auth/oauth/github?from=/dashboard
// After auth: User lands on /dashboardProvider-specific default redirects:
const oAuthProviders = {
github: {
authorizeUrl: "...",
callBackFunction: async () => { ... },
afterLoginRedirect: "/dashboard", // GitHub users go to dashboard
},
google: {
authorizeUrl: "...",
callBackFunction: async () => { ... },
afterLoginRedirect: "/profile", // Google users go to profile
}
};This approach provides a seamless user experience by maintaining context throughout the authentication flow.
Next Parse Auth uses iron-session for secure session management. Sessions can be configured through environment variables:
SESSION_TTL=31536000 # Session duration in seconds (default: 1 year)The library provides structured error responses:
try {
await login("credentials", credentials);
} catch (error) {
if (error instanceof AuthServiceError) {
console.log(error.code, error.message);
}
}To enable logout functionality, you need to define a cloud function in your Parse Server configuration. Add the following to your cloud code:
import { parseAuthLogout, LOGOUT_FUNCTION_NAME } from "parse-server-nextjs/parse";
Parse.Cloud.define(LOGOUT_FUNCTION_NAME, parseAuthLogout, {
requireUser: true,
});Contributions are welcome! Please feel free to submit pull requests or open issues to discuss new features or improvements.
This project is licensed under the MIT License - see the LICENSE file for details.