Back to Documentation
Tutorial45 minutes

Implementing Authentication

Secure your application with Munashe Tech Security and implement comprehensive user authentication

Prerequisites
  • Existing Next.js 14+ application with App Router
  • Munashe Tech Security account and API credentials
  • Understanding of React hooks and server/client components
  • Database configured (PostgreSQL, MySQL, or MongoDB)

Step-by-Step Guide

1
Install Munashe Tech Security SDK
3 min
  • Add the Security SDK to your project
  • Configure environment variables
  • Initialize the authentication module
Code
# Install the SDK
npm install @munashetech/security

# Add to .env.local
NEXT_PUBLIC_MT_APP_ID=your_app_id
MT_SECRET_KEY=your_secret_key
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=generate_random_secret_here
2
Configure Authentication Provider
8 min
  • Set up NextAuth with Munashe Tech provider
  • Configure session strategy
  • Define authentication callbacks
Code
// app/api/auth/[...nextauth]/route.ts
import NextAuth from 'next-auth';
import { MunasheTechProvider } from '@munashetech/security/nextauth';

const handler = NextAuth({
  providers: [
    MunasheTechProvider({
      clientId: process.env.NEXT_PUBLIC_MT_APP_ID!,
      clientSecret: process.env.MT_SECRET_KEY!,
      // Enable multi-factor authentication
      mfaEnabled: true,
      // Choose authentication methods
      authMethods: ['email', 'oauth', 'magic-link']
    })
  ],
  session: {
    strategy: 'jwt',
    maxAge: 30 * 24 * 60 * 60, // 30 days
  },
  callbacks: {
    async jwt({ token, user, account }) {
      if (user) {
        token.id = user.id;
        token.role = user.role;
      }
      return token;
    },
    async session({ session, token }) {
      if (session.user) {
        session.user.id = token.id;
        session.user.role = token.role;
      }
      return session;
    }
  },
  pages: {
    signIn: '/auth/signin',
    signOut: '/auth/signout',
    error: '/auth/error',
  }
});

export { handler as GET, handler as POST };
3
Create Sign-In Page
10 min
  • Build a custom sign-in interface
  • Add social login buttons
  • Implement error handling
Code
// app/auth/signin/page.tsx
'use client';

import { signIn } from 'next-auth/react';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';

export default function SignIn() {
  const [email, setEmail] = useState('');
  const [loading, setLoading] = useState(false);

  const handleEmailSignIn = async (e: React.FormEvent) => {
    e.preventDefault();
    setLoading(true);
    
    try {
      const result = await signIn('email', {
        email,
        callbackUrl: '/dashboard',
        redirect: false
      });
      
      if (result?.error) {
        console.error('Sign in error:', result.error);
      }
    } catch (error) {
      console.error('Sign in failed:', error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="min-h-screen flex items-center justify-center bg-muted/30">
      <Card className="w-full max-w-md">
        <CardHeader>
          <CardTitle className="text-2xl text-center">Welcome Back</CardTitle>
        </CardHeader>
        <CardContent className="space-y-4">
          <form onSubmit={handleEmailSignIn} className="space-y-4">
            <div>
              <Input
                type="email"
                placeholder="Enter your email"
                value={email}
                onChange={(e) => setEmail(e.target.value)}
                required
              />
            </div>
            <Button type="submit" className="w-full" disabled={loading}>
              {loading ? 'Signing in...' : 'Sign in with Email'}
            </Button>
          </form>

          <div className="relative">
            <div className="absolute inset-0 flex items-center">
              <span className="w-full border-t" />
            </div>
            <div className="relative flex justify-center text-xs uppercase">
              <span className="bg-background px-2 text-muted-foreground">
                Or continue with
              </span>
            </div>
          </div>

          <div className="space-y-2">
            <Button
              variant="outline"
              className="w-full"
              onClick={() => signIn('google', { callbackUrl: '/dashboard' })}
            >
              <svg className="w-5 h-5 mr-2" viewBox="0 0 24 24">
                {/* Google icon SVG */}
              </svg>
              Google
            </Button>
            
            <Button
              variant="outline"
              className="w-full"
              onClick={() => signIn('github', { callbackUrl: '/dashboard' })}
            >
              <svg className="w-5 h-5 mr-2" viewBox="0 0 24 24">
                {/* GitHub icon SVG */}
              </svg>
              GitHub
            </Button>
          </div>
        </CardContent>
      </Card>
    </div>
  );
}
4
Protect Routes with Middleware
7 min
  • Create authentication middleware
  • Define protected route patterns
  • Handle unauthorized access
Code
// middleware.ts
import { withAuth } from 'next-auth/middleware';
import { NextResponse } from 'next/server';

export default withAuth(
  function middleware(req) {
    const token = req.nextauth.token;
    const isAuth = !!token;
    const isAuthPage = req.nextUrl.pathname.startsWith('/auth');
    
    if (isAuthPage) {
      if (isAuth) {
        return NextResponse.redirect(new URL('/dashboard', req.url));
      }
      return null;
    }
    
    if (!isAuth) {
      let from = req.nextUrl.pathname;
      if (req.nextUrl.search) {
        from += req.nextUrl.search;
      }
      
      return NextResponse.redirect(
        new URL(`/auth/signin?from=${encodeURIComponent(from)}`, req.url)
      );
    }
  },
  {
    callbacks: {
      authorized: ({ token }) => !!token,
    },
  }
);

export const config = {
  matcher: [
    '/dashboard/:path*',
    '/profile/:path*',
    '/settings/:path*',
    '/api/protected/:path*'
  ],
};
5
Add Session Management
8 min
  • Create session provider wrapper
  • Access user session in components
  • Implement sign-out functionality
Code
// app/providers.tsx
'use client';

import { SessionProvider } from 'next-auth/react';

export function Providers({ children }: { children: React.ReactNode }) {
  return <SessionProvider>{children}</SessionProvider>;
}

// app/layout.tsx
import { Providers } from './providers';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Providers>
          {children}
        </Providers>
      </body>
    </html>
  );
}

// components/user-nav.tsx
'use client';

import { useSession, signOut } from 'next-auth/react';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Button } from '@/components/ui/button';
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';

export function UserNav() {
  const { data: session } = useSession();

  if (!session?.user) return null;

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="ghost" className="relative h-8 w-8 rounded-full">
          <Avatar className="h-8 w-8">
            <AvatarImage src={session.user.image} alt={session.user.name} />
            <AvatarFallback>{session.user.name?.[0]}</AvatarFallback>
          </Avatar>
        </Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent className="w-56" align="end" forceMount>
        <DropdownMenuLabel className="font-normal">
          <div className="flex flex-col space-y-1">
            <p className="text-sm font-medium leading-none">
              {session.user.name}
            </p>
            <p className="text-xs leading-none text-muted-foreground">
              {session.user.email}
            </p>
          </div>
        </DropdownMenuLabel>
        <DropdownMenuSeparator />
        <DropdownMenuItem onClick={() => signOut()}>
          Log out
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}
6
Implement Role-Based Access Control
9 min
  • Define user roles and permissions
  • Create authorization helpers
  • Protect components based on roles
Code
// lib/auth/permissions.ts
export enum Role {
  USER = 'user',
  ADMIN = 'admin',
  MODERATOR = 'moderator'
}

export const permissions = {
  [Role.USER]: ['read:own', 'write:own'],
  [Role.MODERATOR]: ['read:own', 'write:own', 'read:all', 'moderate:content'],
  [Role.ADMIN]: ['read:all', 'write:all', 'delete:all', 'manage:users']
};

export function hasPermission(role: Role, permission: string): boolean {
  return permissions[role]?.includes(permission) ?? false;
}

// hooks/usePermissions.ts
import { useSession } from 'next-auth/react';
import { hasPermission, Role } from '@/lib/auth/permissions';

export function usePermissions() {
  const { data: session } = useSession();
  const userRole = (session?.user?.role as Role) ?? Role.USER;

  return {
    can: (permission: string) => hasPermission(userRole, permission),
    isAdmin: userRole === Role.ADMIN,
    isModerator: userRole === Role.MODERATOR,
    role: userRole
  };
}

// components/admin-only.tsx
'use client';

import { usePermissions } from '@/hooks/usePermissions';

export function AdminOnly({ children }: { children: React.ReactNode }) {
  const { isAdmin } = usePermissions();
  
  if (!isAdmin) return null;
  
  return <>{children}</>;
}

// Usage in components
import { AdminOnly } from '@/components/admin-only';

export function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      
      <AdminOnly>
        <Button variant="destructive">Delete All Data</Button>
      </AdminOnly>
    </div>
  );
}
Security Best Practices
  • Never expose secrets: Keep API keys and secrets in environment variables, never commit them to version control
  • Use HTTPS only: Always use HTTPS in production to encrypt data in transit
  • Implement rate limiting: Protect your authentication endpoints from brute force attacks
  • Enable MFA: Offer multi-factor authentication for enhanced security
  • Regular security audits: Periodically review and update your authentication implementation
Testing Authentication

Verify your authentication implementation with these tests:

  1. 1.Test sign-in with valid credentials
  2. 2.Verify protected routes redirect to sign-in when not authenticated
  3. 3.Test sign-out functionality clears session
  4. 4.Verify role-based access control works correctly
  5. 5.Test social login providers (Google, GitHub)
  6. 6.Verify session persistence across page reloads

Common Issues

  • Redirect loops: Check middleware configuration and callback URLs
  • Session not persisting: Verify NEXTAUTH_SECRET is set correctly
  • OAuth errors: Confirm callback URLs match in provider settings
  • Database errors: Ensure database adapter is configured properly

Next Steps

Add Two-Factor Auth
Enhance security with TOTP-based 2FA
Implement API Authentication
Secure your API routes with JWT tokens

Need Help with Security?

Our security experts are here to help protect your application