- Published on
Integrating Next-Auth in a Streaming AI Chat Assistant Using Material-UI
- Authors
- Name
- Athos Georgiou
Integrating Next-Auth in a Streaming AI Chat Assistant Using Material-UI
Introduction
One of the fundamental aspects of writing applications is authentication. It is the process of verifying the identity of a user and granting them access to protected resources. In this guide in the series, we will be integrating next-auth
in a streaming AI chat assistant built with Next.js.
Additionally, we will be using Material-UI for styling our application. Material-UI is a React UI framework that provides a set of components for building responsive web applications. It also provides a theming solution for customizing the look and feel of your application.
Do keep in mind that the code in this guide is indicative and may have differences from the final code.
If you prefer to skip the guide and get the code yourself, you can find it on GitHub
You can also access and test the app directly on Vercel
Prerequisites
- React and Next.js knowledge
- Material-UI in your project
- GitHub and Google accounts for OAuth
Step-by-Step Guide
Step 1: Acquiring OAuth Credentials
GitHub OAuth Setup
- Visit GitHub Developer Settings.
- Click "New OAuth App".
- Enter the required information.
- Note the
Client ID
andClient Secret
. - Add
http://localhost:3000/api/auth/callback/github
to the "Authorization callback URL".
Google OAuth Setup
- Go to Google Cloud Console.
- Create a new project.
- Navigate to "Credentials" and create an OAuth client ID.
- Note the
Client ID
andClient Secret
. - Add
http://localhost:3000/api/auth/callback/google
to the "Authorized redirect URIs".
Step 2: Configuring Environment Variables
Create .env.local
with:
GITHUB_ID=your_github_client_id
GITHUB_SECRET=your_github_client_secret
GOOGLE_ID=your_google_client_id
GOOGLE_SECRET=your_google_client_secret
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=a_random_secret_string
Step 3: Setting Up Authentication Providers
Create a new auth route: app/api/auth/[...nextauth]/route.ts
:
import NextAuth from 'next-auth/next';
import type { NextAuthOptions } from 'next-auth';
import GitHubProvider from 'next-auth/providers/github';
import GoogleProvider from 'next-auth/providers/google';
const providers = [
GitHubProvider({
clientId: process.env.GITHUB_ID as string,
clientSecret: process.env.GITHUB_SECRET as string,
}),
GoogleProvider({
clientId: process.env.GOOGLE_ID as string,
clientSecret: process.env.GOOGLE_SECRET as string,
}),
];
const options: NextAuthOptions = { providers };
const handler = NextAuth(options);
export { handler as GET, handler as POST };
Step 4: Creating AppBar with Material-UI
Use Material-UI for the AppBar, creating a new responsive component: app/components/AppBar/ResponsiveAppBar.tsx
:
import * as React from 'react';
import { useSession, signIn, signOut } from 'next-auth/react';
import AppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import Menu from '@mui/material/Menu';
import MenuIcon from '@mui/icons-material/Menu';
import Container from '@mui/material/Container';
import Avatar from '@mui/material/Avatar';
import Button from '@mui/material/Button';
import Tooltip from '@mui/material/Tooltip';
import MenuItem from '@mui/material/MenuItem';
import BlurOnRoundedIcon from '@mui/icons-material/BlurOnRounded';
import styles from './ResponsiveAppBar.module.css';
const pages = ['Home', 'About'];
const settings = ['Account', 'Logout'];
function ResponsiveAppBar() {
const { data: session } = useSession();
const [anchorElNav, setAnchorElNav] = React.useState<null | HTMLElement>(
null
);
const [anchorElUser, setAnchorElUser] = React.useState<null | HTMLElement>(
null
);
const handleOpenNavMenu = (event: React.MouseEvent<HTMLElement>) => {
setAnchorElNav(event.currentTarget);
};
const handleOpenUserMenu = (event: React.MouseEvent<HTMLElement>) => {
setAnchorElUser(event.currentTarget);
};
const handleCloseNavMenu = () => {
setAnchorElNav(null);
};
const handleCloseUserMenu = () => {
setAnchorElUser(null);
};
const handleLogout = () => {
signOut();
};
const handleLogin = () => {
signIn();
};
return (
<AppBar position="fixed" className={styles.main}>
<Container maxWidth="xl">
<Toolbar disableGutters>
<BlurOnRoundedIcon
sx={{ display: { xs: 'none', md: 'flex' }, mr: 1 }}
/>
<Typography
variant="h6"
noWrap
component="a"
href="#responsive-app-bar"
sx={{
mr: 2,
display: { xs: 'none', md: 'flex' },
fontFamily: 'monospace',
fontWeight: 700,
letterSpacing: '.3rem',
color: 'inherit',
textDecoration: 'none',
}}
>
Titanium
</Typography>
<Box sx={{ flexGrow: 1, display: { xs: 'flex', md: 'none' } }}>
<IconButton
size="large"
aria-label="account of current user"
aria-controls="menu-appbar"
aria-haspopup="true"
onClick={handleOpenNavMenu}
color="inherit"
>
<MenuIcon />
</IconButton>
<Menu
id="menu-appbar"
anchorEl={anchorElNav}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
keepMounted
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
open={Boolean(anchorElNav)}
onClose={handleCloseNavMenu}
sx={{
display: { xs: 'block', md: 'none' },
}}
>
{pages.map((page) => (
<MenuItem key={page} onClick={handleCloseNavMenu}>
<Typography textAlign="center">{page}</Typography>
</MenuItem>
))}
</Menu>
</Box>
<BlurOnRoundedIcon
sx={{ display: { xs: 'flex', md: 'none' }, mr: 1 }}
/>
<Typography
variant="h5"
noWrap
component="a"
href="#app-bar-with-responsive-menu"
sx={{
mr: 2,
display: { xs: 'flex', md: 'none' },
flexGrow: 1,
fontFamily: 'monospace',
fontWeight: 700,
letterSpacing: '.3rem',
color: 'inherit',
textDecoration: 'none',
}}
>
Titanium
</Typography>
<Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' } }}>
{pages.map((page) => (
<Button
key={page}
onClick={handleCloseNavMenu}
sx={{ my: 2, color: 'white', display: 'block' }}
>
{page}
</Button>
))}
</Box>
<Box sx={{ flexGrow: 0 }}>
<Tooltip title="Open settings">
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
<Avatar alt="User Avatar" src={session?.user?.image || ''} />
</IconButton>
</Tooltip>
<Menu
sx={{ mt: '45px' }}
id="menu-appbar"
anchorEl={anchorElUser}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
keepMounted
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
open={Boolean(anchorElUser)}
onClose={handleCloseUserMenu}
>
{session ? (
settings.map((setting) => (
<MenuItem
key={setting}
onClick={
setting === 'Logout' ? handleLogout : handleCloseUserMenu
}
>
<Typography textAlign="center">{setting}</Typography>
</MenuItem>
))
) : (
<MenuItem onClick={handleLogin}>
<Typography textAlign="center">Log in</Typography>
</MenuItem>
)}
</Menu>
</Box>
</Toolbar>
</Container>
</AppBar>
);
}
export default ResponsiveAppBar;
Step 5: Plugging in...
Import the new AppBar and then wrap your app/page.tsx
in a SessionProvider
:
'use client'
import React from 'react'
import { SessionProvider } from 'next-auth/react'
import ResponsiveAppBar from './components/AppBar/ResponsiveAppBar'
import Chat from './components/Chat/Chat'
import styles from './page.module.css'
export default function Home() {
return (
<SessionProvider>
<ResponsiveAppBar />
<main className={styles.main}>
<Chat />
</main>
</SessionProvider>
)
}
Step 6: Adding Session Protection
Add session protection in app/components/Chat/Chat.tsx
:
if (session) {
return (
<>
{isLoading && <Loader />}
<MessagesField messages={messages} />
<div className={styles.inputArea}>
<CustomizedInputBase setIsLoading={setIsLoading} onSendMessage={sendUserMessage} />
</div>
</>
)
}
return (
<div className={styles.loginPrompt}>
<p>Please sign in to access the chat.</p>
</div>
)
That's It!
I hope you found this guide useful and that it helped you integrate next-auth
in your Next.js project. I know I had a blast coding so far and I'm looking forward to all the cool stuff we'll be doing in the future.
More specifically, now that we have streaming chat and authentication set up in Titanium, we can start working on more advanced concepts, such as: Multi-user persistent memory, RAG, Assistants, Vision, and Speech.
If you have any questions or comments, feel free to reach out to me on GitHub, LinkedIn, or via email.
See ya around and happy coding!