├── 03-Login-app-directory ├── .env.example ├── .eslintrc.json ├── app │ ├── globals.css │ ├── favicon.ico │ ├── page.tsx │ ├── dashboard │ │ └── page.tsx │ ├── layout.tsx │ ├── about │ │ └── page.tsx │ └── blog │ │ ├── page.tsx │ │ └── [slug] │ │ └── page.tsx ├── next.config.js ├── postcss.config.js ├── typings.d.ts ├── components │ ├── Auth.tsx │ ├── footer.tsx │ ├── banner.tsx │ ├── LogoutButton.tsx │ └── DashboardContent.tsx ├── .gitignore ├── tailwind.config.js ├── actions │ └── getCurrentUserInfo.ts ├── public │ ├── vercel.svg │ ├── next.svg │ └── logo.svg ├── tsconfig.json ├── middleware.ts ├── package.json └── README.md ├── 01-Login ├── next.config.js ├── public │ ├── favicon.ico │ └── passageloginscreen.png ├── .eslintrc.json ├── EXAMPLE.env ├── styles │ ├── globals.css │ ├── App.module.css │ └── Banner.module.css ├── pages │ ├── index.js │ ├── _app.js │ └── dashboard.js ├── components │ └── banner.js ├── package.json ├── .gitignore └── README.md ├── 02-Login-With-Profile ├── next.config.js ├── public │ └── favicon.ico ├── .eslintrc.json ├── EXAMPLE.env ├── styles │ ├── globals.css │ ├── App.module.css │ └── Banner.module.css ├── pages │ ├── index.js │ ├── _app.js │ └── dashboard.js ├── components │ └── banner.js ├── package.json ├── .gitignore └── README.md └── README.md /03-Login-app-directory/.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_PASSAGE_APP_ID= 2 | -------------------------------------------------------------------------------- /01-Login/next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | reactStrictMode: true, 3 | } 4 | -------------------------------------------------------------------------------- /03-Login-app-directory/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /02-Login-With-Profile/next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | reactStrictMode: true, 3 | } 4 | -------------------------------------------------------------------------------- /03-Login-app-directory/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | -------------------------------------------------------------------------------- /01-Login/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/passageidentity/example-nextjs/HEAD/01-Login/public/favicon.ico -------------------------------------------------------------------------------- /01-Login/public/passageloginscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/passageidentity/example-nextjs/HEAD/01-Login/public/passageloginscreen.png -------------------------------------------------------------------------------- /03-Login-app-directory/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/passageidentity/example-nextjs/HEAD/03-Login-app-directory/app/favicon.ico -------------------------------------------------------------------------------- /02-Login-With-Profile/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/passageidentity/example-nextjs/HEAD/02-Login-With-Profile/public/favicon.ico -------------------------------------------------------------------------------- /03-Login-app-directory/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /03-Login-app-directory/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /01-Login/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals", 3 | "rules": { 4 | "semi": ["warn", "always"], 5 | "semi-style": ["warn", "last"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /02-Login-With-Profile/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals", 3 | "rules": { 4 | "semi": ["warn", "always"], 5 | "semi-style": ["warn", "last"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /01-Login/EXAMPLE.env: -------------------------------------------------------------------------------- 1 | PASSAGE_APP_ID= 2 | # Note that because this example uses the passage-node package, an API key is required. 3 | # You can retrieve an API key for your application by going to the Dashboard and selecting Settings, then API Keys. 4 | PASSAGE_API_KEY= -------------------------------------------------------------------------------- /02-Login-With-Profile/EXAMPLE.env: -------------------------------------------------------------------------------- 1 | PASSAGE_APP_ID= 2 | # Note that because this example uses the passage-node package, an API key is required. 3 | # You can retrieve an API key for your application by going to the Dashboard and selecting Settings, then API Keys. 4 | PASSAGE_API_KEY= -------------------------------------------------------------------------------- /03-Login-app-directory/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace JSX { 2 | import { PassageElement, PassageProfileElement } from '@passageidentity/passage-elements' 3 | interface IntrinsicElements { 4 | "passage-auth": PassageElement; 5 | "passage-login": PassageElement; 6 | "passage-register": PassageElement; 7 | "passage-profile": PassageProfileElement; 8 | } 9 | } -------------------------------------------------------------------------------- /01-Login/styles/globals.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 7 | background-color: #E5E5E5; 8 | } 9 | 10 | a { 11 | color: inherit; 12 | text-decoration: none; 13 | } 14 | 15 | * { 16 | box-sizing: border-box; 17 | } 18 | -------------------------------------------------------------------------------- /02-Login-With-Profile/styles/globals.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 7 | background-color: #E5E5E5; 8 | } 9 | 10 | a { 11 | color: inherit; 12 | text-decoration: none; 13 | } 14 | 15 | * { 16 | box-sizing: border-box; 17 | } 18 | -------------------------------------------------------------------------------- /03-Login-app-directory/components/Auth.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { FC, useEffect } from "react"; 3 | 4 | interface AuthProps {} 5 | 6 | const Auth: FC = () => { 7 | useEffect(() => { 8 | require("@passageidentity/passage-elements/passage-auth"); 9 | }, []); 10 | return ( 11 | <> 12 | 13 | 14 | ); 15 | }; 16 | 17 | export default Auth; 18 | -------------------------------------------------------------------------------- /01-Login/pages/index.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | 3 | export default function Home({appID}) { 4 | useEffect(()=>{ 5 | require('@passageidentity/passage-elements/passage-auth'); 6 | }, []); 7 | 8 | return ( 9 | <> 10 | 11 | 12 | ) 13 | } 14 | 15 | export async function getStaticProps(){ 16 | return { 17 | props: { 18 | appID: process.env.PASSAGE_APP_ID 19 | } 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /02-Login-With-Profile/pages/index.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | 3 | export default function Home({appID}) { 4 | useEffect(()=>{ 5 | require('@passageidentity/passage-elements/passage-auth'); 6 | }, []); 7 | 8 | return ( 9 | <> 10 | 11 | 12 | ) 13 | } 14 | 15 | export async function getStaticProps(){ 16 | return { 17 | props: { 18 | appID: process.env.PASSAGE_APP_ID 19 | } 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /01-Login/components/banner.js: -------------------------------------------------------------------------------- 1 | import styles from '../styles/Banner.module.css'; 2 | 3 | export default function Banner() { 4 | return ( 5 |
6 |
7 |
Passage + Next.js Example App
8 |
9 | Go to Passage 10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /02-Login-With-Profile/components/banner.js: -------------------------------------------------------------------------------- 1 | import styles from '../styles/Banner.module.css'; 2 | 3 | export default function Banner() { 4 | return ( 5 |
6 |
7 |
Passage + Next.js Example App
8 |
9 | Go to Passage 10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /03-Login-app-directory/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /01-Login/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passage-next-app", 3 | "private": true, 4 | "scripts": { 5 | "dev": "next dev", 6 | "build": "next build", 7 | "start": "next start", 8 | "lint": "next lint" 9 | }, 10 | "dependencies": { 11 | "@passageidentity/passage-elements": "latest", 12 | "@passageidentity/passage-node": "latest", 13 | "next": "12.0.10", 14 | "react": "17.0.2", 15 | "react-dom": "17.0.2" 16 | }, 17 | "devDependencies": { 18 | "eslint": "8.8.0", 19 | "eslint-config-next": "12.0.10" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /02-Login-With-Profile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passage-next-app", 3 | "private": true, 4 | "scripts": { 5 | "dev": "next dev", 6 | "build": "next build", 7 | "start": "next start", 8 | "lint": "next lint" 9 | }, 10 | "dependencies": { 11 | "@passageidentity/passage-elements": "latest", 12 | "@passageidentity/passage-node": "latest", 13 | "next": "12.0.10", 14 | "react": "17.0.2", 15 | "react-dom": "17.0.2" 16 | }, 17 | "devDependencies": { 18 | "eslint": "8.8.0", 19 | "eslint-config-next": "12.0.10" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /03-Login-app-directory/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | './pages/**/*.{js,ts,jsx,tsx,mdx}', 5 | './components/**/*.{js,ts,jsx,tsx,mdx}', 6 | './app/**/*.{js,ts,jsx,tsx,mdx}', 7 | ], 8 | theme: { 9 | extend: { 10 | backgroundImage: { 11 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 12 | 'gradient-conic': 13 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 14 | }, 15 | }, 16 | }, 17 | plugins: [], 18 | } 19 | -------------------------------------------------------------------------------- /03-Login-app-directory/actions/getCurrentUserInfo.ts: -------------------------------------------------------------------------------- 1 | import { Passage } from "@passageidentity/passage-js"; 2 | 3 | export interface PassageUserInfo { 4 | email: string; 5 | created_at: string; 6 | } 7 | 8 | export async function getCurrentUserInfo() { 9 | const passage = new Passage(process.env.NEXT_PUBLIC_PASSAGE_APP_ID!); 10 | try { 11 | const user = passage.getCurrentUser(); 12 | const userInfo = await user.userInfo(); 13 | return { 14 | userInfo, 15 | }; 16 | } catch (error) { 17 | console.log(error); 18 | } 19 | return { userInfo: undefined }; 20 | } 21 | -------------------------------------------------------------------------------- /03-Login-app-directory/app/page.tsx: -------------------------------------------------------------------------------- 1 | import Auth from "@/components/Auth"; 2 | import Link from "next/link"; 3 | 4 | export default function Home() { 5 | return ( 6 | <> 7 | 8 |
9 |
Routes in this application
10 | Home 11 | Dashboard 12 |

Middleware Protected routes

13 | Blog 14 | About 15 |
16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /03-Login-app-directory/components/footer.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from "react"; 2 | 3 | interface FooterProps {} 4 | 5 | const Footer: FC = ({}) => { 6 | return ( 7 | <> 8 |
9 | Learn more with our 10 |  Documentation  11 | and 12 |  Github. 13 |
14 | 15 | ); 16 | }; 17 | 18 | export default Footer; 19 | -------------------------------------------------------------------------------- /03-Login-app-directory/app/dashboard/page.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from "react"; 2 | import DashboardContent from "@/components/DashboardContent"; 3 | import Link from "next/link"; 4 | 5 | interface pageProps {} 6 | 7 | const page: FC = ({}) => { 8 | return ( 9 | <> 10 | 11 |
12 | Back to Home 13 | 14 | Blog(protected route) 15 | 16 |
17 | 18 | ); 19 | }; 20 | 21 | export default page; 22 | -------------------------------------------------------------------------------- /01-Login/styles/App.module.css: -------------------------------------------------------------------------------- 1 | 2 | .mainContainer { 3 | background: white; 4 | box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); 5 | border-radius: 20px; 6 | width: 310px; 7 | min-height: 310px; 8 | margin: 30px auto; 9 | } 10 | .footer { 11 | text-align: center; 12 | font-size: 18px; 13 | } 14 | 15 | .dashboard{ 16 | padding: 30px 30px 20px; 17 | } 18 | .title { 19 | font-size: 24px; 20 | font-weight: 700; 21 | margin-bottom: 30px; 22 | } 23 | .message { 24 | overflow-wrap: anywhere; 25 | } 26 | .link { 27 | color: black; 28 | text-decoration-color: black; 29 | } 30 | -------------------------------------------------------------------------------- /02-Login-With-Profile/styles/App.module.css: -------------------------------------------------------------------------------- 1 | 2 | .mainContainer { 3 | background: white; 4 | box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); 5 | border-radius: 20px; 6 | width: 600px; 7 | min-height: 310px; 8 | margin: 30px auto; 9 | } 10 | .footer { 11 | text-align: center; 12 | font-size: 18px; 13 | } 14 | 15 | .dashboard{ 16 | padding: 30px 30px 20px; 17 | } 18 | .title { 19 | font-size: 24px; 20 | font-weight: 700; 21 | margin-bottom: 30px; 22 | } 23 | .message { 24 | overflow-wrap: anywhere; 25 | } 26 | .link { 27 | color: black; 28 | text-decoration-color: black; 29 | } 30 | -------------------------------------------------------------------------------- /01-Login/pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css'; 2 | 3 | import Banner from '../components/banner'; 4 | import styles from '../styles/App.module.css'; 5 | 6 | function MyApp({ Component, pageProps }) { 7 | return ( 8 | <> 9 | 10 |
11 | 12 |
13 |
14 | Learn more with our Documentation and Github. 15 |
16 | 17 | ); 18 | } 19 | 20 | export default MyApp; -------------------------------------------------------------------------------- /03-Login-app-directory/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /02-Login-With-Profile/pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css'; 2 | 3 | import Banner from '../components/banner'; 4 | import styles from '../styles/App.module.css'; 5 | 6 | function MyApp({ Component, pageProps }) { 7 | return ( 8 | <> 9 | 10 |
11 | 12 |
13 |
14 | Learn more with our Documentation and Github. 15 |
16 | 17 | ); 18 | } 19 | 20 | export default MyApp; -------------------------------------------------------------------------------- /01-Login/styles/Banner.module.css: -------------------------------------------------------------------------------- 1 | .mainHeader{ 2 | padding: 20px 30px; 3 | display: flex; 4 | align-items: center; 5 | background-color: #282727; 6 | color: white; 7 | } 8 | .headerText { 9 | font-size: 24px; 10 | margin: 0px 10px; 11 | } 12 | 13 | .passageLogo { 14 | background-image: url('https://storage.googleapis.com/passage-docs/passage-logo-dark.svg'); 15 | background-repeat: no-repeat; 16 | width: 60px; 17 | height: 60px; 18 | cursor: pointer; 19 | } 20 | .spacer { 21 | flex-grow: 1; 22 | } 23 | 24 | .link { 25 | margin-left: 20px; 26 | color: white; 27 | text-decoration: underline; 28 | text-decoration-color: white; 29 | } -------------------------------------------------------------------------------- /02-Login-With-Profile/styles/Banner.module.css: -------------------------------------------------------------------------------- 1 | .mainHeader{ 2 | padding: 20px 30px; 3 | display: flex; 4 | align-items: center; 5 | background-color: #282727; 6 | color: white; 7 | } 8 | .headerText { 9 | font-size: 24px; 10 | margin: 0px 10px; 11 | } 12 | 13 | .passageLogo { 14 | background-image: url('https://storage.googleapis.com/passage-docs/passage-logo-dark.svg'); 15 | background-repeat: no-repeat; 16 | width: 60px; 17 | height: 60px; 18 | cursor: pointer; 19 | } 20 | .spacer { 21 | flex-grow: 1; 22 | } 23 | 24 | .link { 25 | margin-left: 20px; 26 | color: white; 27 | text-decoration: underline; 28 | text-decoration-color: white; 29 | } -------------------------------------------------------------------------------- /03-Login-app-directory/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | } 21 | ], 22 | "paths": { 23 | "@/*": ["./*"] 24 | } 25 | }, 26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /03-Login-app-directory/components/banner.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | 3 | export default function Banner() { 4 | return ( 5 | <> 6 |
7 |
8 |
9 | 10 | logo 11 | 12 |
13 |
14 | Passage + Next.js Example With /app directory and tailwindcss 15 |
16 |
17 |
18 | Go to Passage 19 |
20 |
21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /03-Login-app-directory/middleware.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | import type { NextRequest } from "next/server"; 3 | import Passage from "@passageidentity/passage-node"; 4 | 5 | export async function middleware(request: NextRequest) { 6 | const authToken = request.cookies.get("psg_auth_token")?.value; 7 | if (!authToken) { 8 | return NextResponse.redirect(new URL("/", request.url)); 9 | } 10 | 11 | const passage = new Passage({ 12 | appID: process.env.NEXT_PUBLIC_PASSAGE_APP_ID!, 13 | }); 14 | 15 | const userID = await passage.validAuthToken(authToken as string); 16 | if (!userID) { 17 | return NextResponse.redirect(new URL("/", request.url)); 18 | } 19 | } 20 | 21 | // See "Matching Paths" below to learn more 22 | export const config = { 23 | matcher: ["/about/:path*", "/blog/:path*"], 24 | }; 25 | -------------------------------------------------------------------------------- /03-Login-app-directory/components/LogoutButton.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useRouter } from "next/navigation"; 3 | import { FC } from "react"; 4 | import { Passage } from "@passageidentity/passage-js"; 5 | 6 | interface LogoutButtonProps {} 7 | 8 | const LogoutButton: FC = ({}) => { 9 | const router = useRouter(); 10 | const passage = new Passage(process.env.NEXT_PUBLIC_PASSAGE_APP_ID!); 11 | const session = passage.getCurrentSession(); 12 | const handleLogout = () => { 13 | session.signOut(); 14 | router.push("/"); 15 | }; 16 | 17 | return ( 18 | <> 19 |
23 | Logout 24 |
25 | 26 | ); 27 | }; 28 | 29 | export default LogoutButton; 30 | -------------------------------------------------------------------------------- /03-Login-app-directory/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import Banner from "@/components/banner"; 2 | import "./globals.css"; 3 | import { Inter } from "next/font/google"; 4 | import Footer from "@/components/footer"; 5 | 6 | const inter = Inter({ subsets: ["latin"] }); 7 | 8 | export const metadata = { 9 | title: "Passage + Next.js Example With /app directory", 10 | description: 11 | "This is an example of how you can integrate passage with the Next.Js /app directory to Register, Login and also get Authentication Status and User Information.", 12 | }; 13 | 14 | export default function RootLayout({ 15 | children, 16 | }: { 17 | children: React.ReactNode; 18 | }) { 19 | return ( 20 | 21 | 22 | 23 | {children} 24 |