Skip to content

Commit

Permalink
Merge pull request #7 from cerberauth/nextjs-app
Browse files Browse the repository at this point in the history
feat: bootstrap nextjs app example project
  • Loading branch information
emmanuelgautier authored Aug 17, 2024
2 parents 57d9b30 + 3781011 commit fe6fb68
Show file tree
Hide file tree
Showing 29 changed files with 9,893 additions and 3 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ jobs:
- workingDirectory: ./examples/angular-spa
projectName: cerberauth-angular-spa-oidc
directory: ./dist/angular-spa/browser
- workingDirectory: ./examples/nextjs-app
projectName: cerberauth-nextjs-app-oidc
directory: ./.vercel/output/static

permissions:
contents: read
Expand All @@ -49,7 +52,7 @@ jobs:
run: npm ci

- name: Run build
run: npm run build
run: npm run pages:build

- name: Publish
uses: cloudflare/pages-action@1
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# OpenID Connect Examples / Demo

This repository contains a collection of OpenID Connect examples.
This repository contains a collection of OpenID Connect examples. Check out the examples below to see how to integrate OpenID Connect with your application.

You can use the examples as a starting point for your own project or as a reference to understand how OpenID Connect works.

## Examples

- [Angular SPA](./examples/angular-spa/README.md)
- [Next.js SPA](./examples/nextjs-spa/README.md)
- [React SPA](./examples/react-spa/README.md)

## License
Expand Down
1 change: 1 addition & 0 deletions examples/angular-spa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"pages:build": "ng build --configuration production",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
Expand Down
5 changes: 5 additions & 0 deletions examples/nextjs-app/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Use openssl rand -base64 33 to generate a secret
AUTH_SECRET=secret

AUTH_CLIENT_ID=
AUTH_CLIENT_SECRET=
3 changes: 3 additions & 0 deletions examples/nextjs-app/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
36 changes: 36 additions & 0 deletions examples/nextjs-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
43 changes: 43 additions & 0 deletions examples/nextjs-app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
FROM node:20 as builder

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .

ENV NEXT_BUILD_OUTPUT 'standalone'

RUN npm run build

FROM node:20-alpine

WORKDIR /app

ENV NODE_ENV production

RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public

# Set the correct permission for prerender cache
RUN mkdir .next && \
chown nextjs:nodejs .next

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT 3000
ENV HOSTNAME "0.0.0.0"

# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
CMD ["node", "server.js"]
82 changes: 82 additions & 0 deletions examples/nextjs-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# React SPA (Single Page App) using Authorization Code Flow with PKCE

This project demonstrates how to implement the Authorization Code Flow with PKCE for a Next.js App.

Disclaimer: This project is for educational purposes only and should not be used in production without proper security review and testing.

## Deployment

This project is deployed on Cloudflare Pages. You can access the live demo [here](https://cerberauth-nextjs-app-oidc.pages.dev/).

## Prerequisites

Before getting started, make sure you have the following:

- Node.js installed on your machine
- An OpenID Connect provider that supports the Authorization Code Flow with PKCE

## Getting Started

1. Clone the repository:

```bash
git clone https://github.com/cerberauth/openid-connect-examples.git
```

2. Install the dependencies:

```bash
cd openid-connect-examples/nextjs-app
npm ci
```

3. Configure the OpenID Connect provider:

If you don't have an OpenID Connect provider, you can use [TestID OpenID Connect Provider](https://testid.cerberauth.com/).

- Obtain the client ID and client secret from your OpenID Connect provider.
- Register the redirect URI for your React SPA in the provider's developer console.

4. Update the configuration:

- Create a `.env.local` file in the root directory of your project or copy `.env.example` file.
- Add the necessary environment variables to the `.env.local` file. For example:

```plaintext
AUTH_SECRET=secret
AUTH_CLIENT_ID=your-client-id
AUTH_CLIENT_SECRET=your-client-secret
```
Generate a random secret using the following command:
```bash
openssl rand -base64 33
```
Replace `secret` with the generated secret.
Replace `your-client-id` and `your-client-secret` with the actual values provided by your OpenID Connect provider.
- Save the `.env.local` file.
5. Start the development server:
```bash
npm run dev
```

6. Open your browser and navigate to `http://localhost:5173/`.

7. Click on the "Login" button to initiate the authorization code flow.

8. After successful authentication, you will be redirected back to the React SPA and the user information will be displayed.

## Additional Resources

- [Auth.js](https://authjs.dev/)
- [OpenID Connect](https://openid.net/)
- [OAuth 2.0 Authorization Code Flow](https://oauth.net/2/grant-types/authorization-code/)
- [PKCE](https://oauth.net/2/pkce/)
- [Awesome OpenID Connect](https://github.com/cerberauth/awesome-openid-connect)
- [React](https://reactjs.org/)
4 changes: 4 additions & 0 deletions examples/nextjs-app/app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { handlers } from '@/auth'

export const runtime = 'edge'
export const { GET, POST } = handlers
Binary file added examples/nextjs-app/app/favicon.ico
Binary file not shown.
69 changes: 69 additions & 0 deletions examples/nextjs-app/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
}

.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
}

@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
35 changes: 35 additions & 0 deletions examples/nextjs-app/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { Metadata } from 'next'
import { Inter as FontSans } from 'next/font/google'
import { SessionProvider } from 'next-auth/react'
import './globals.css'

import { cn } from '@/lib/utils'

const fontSans = FontSans({
subsets: ['latin'],
variable: '--font-sans',
})

export const metadata: Metadata = {
title: 'Next.js Application Example using OpenID Connect',
description: 'This example demonstrates how to authenticate users in a Next.js Application using OpenID Connect Protocol.',
}

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={cn(
"bg-slate-100 min-h-screen font-sans antialiased",
fontSans.variable,
)}>
<SessionProvider>
{children}
</SessionProvider>
</body>
</html>
)
}
41 changes: 41 additions & 0 deletions examples/nextjs-app/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use client'

import { useSession, signOut, signIn } from 'next-auth/react'
import { Button } from '@/components/ui/button'

export default function Home() {
const session = useSession()
const user = session.data?.user
const isAuthenticated = !!user

return (
<div className="bg-slate-100 min-h-screen">
<main role="main" className="flex flex-col items-center justify-center h-5/6 space-y-8 text-center px-4 py-16 lg:pt-32 md:pt-16 sm:pt-8">
<h1 className="mt-4 text-2xl font-extrabold tracking-tight text-slate-900 sm:text-5xl xl:max-w-[43.5rem]">
Next.js Application Example using OpenID Connect
</h1>
<p className="mt-4 max-w-lg text-lg text-slate-700">
This example demonstrates how to authenticate users in a Next.js Application using OpenID Connect Protocol.
</p>
{isAuthenticated ? (
<Button onClick={() => signOut()}>
Logout
</Button>
) : (
<Button onClick={() => signIn('testid')}>
Login with TestID
</Button>
)}
<p className="mt-4 max-w-lg text-slate-700">
If you want to checkout out how to implement OpenID Connect in your Next.js app, take a look at the <a className="text-indigo-600" href="https://github.com/cerberauth/openid-connect-examples/tree/main/examples/nextjs-app">source code</a>.
</p>
</main>

<footer className="text-center py-4">
<p className="text-sm text-gray-500">
Proudly part of <a className="text-indigo-600" href="https://www.cerberauth.com">CerberAuth</a> community.
</p>
</footer>
</div>
)
}
22 changes: 22 additions & 0 deletions examples/nextjs-app/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import NextAuth from 'next-auth'

export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [{
id: 'testid',
name: 'TestID',
issuer: 'https://testid.cerberauth.com',
type: 'oidc',
clientId: process.env.AUTH_CLIENT_ID,
clientSecret: process.env.AUTH_CLIENT_SECRET,
checks: ['pkce', 'state'],
authorization: {
params: { scope: 'openid profile email' }
},
}],
session: { strategy: 'jwt' },
callbacks: {
session: async ({ session }) => {
return session
},
}
})
Loading

0 comments on commit fe6fb68

Please sign in to comment.