├── .gitignore
├── LICENSE
├── README.md
├── django
├── .gitignore
├── apps
│ └── example
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ ├── apps.py
│ │ ├── migrations
│ │ └── __init__.py
│ │ ├── models.py
│ │ ├── serializers.py
│ │ ├── tests.py
│ │ └── views.py
├── manage.py
├── media
│ └── .gitkeep
├── project
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── requirements.txt
├── static
│ └── .gitkeep
└── templates
│ └── .gitkeep
└── nextjs
├── .env.local.example
├── .github
└── FUNDING.yml
├── .gitignore
├── LICENSE
├── README.md
├── components
├── access-denied.js
├── footer.js
├── footer.module.css
├── header.js
├── header.module.css
└── layout.js
├── middleware.js
├── package-lock.json
├── package.json
└── pages
├── _app.js
├── admin.js
├── api-example.js
├── api
├── auth
│ └── [...nextauth].js
└── examples
│ ├── jwt.js
│ ├── protected.js
│ └── session.js
├── client.js
├── index.js
├── me.js
├── policy.js
├── protected.js
├── server.js
└── styles.css
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Mark Hill
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NextAuth.js Django Provider Example
2 |
3 | Example of using NextAuth.js with a Django (DRF & JWT) backend.
4 |
5 | This repository contains:
6 |
7 | 1. [Django](https://www.djangoproject.com/) backend with a Django Rest Framework API supporting authentication using JWT.
8 | 2. [NextAuth.js](https://next-auth.js.org/) application with a Django JWT provider.
9 |
10 | **Note:** The main inspiration comes from this [thread](https://github.com/nextauthjs/next-auth/discussions/1350) and in particular [this contribution](https://github.com/nextauthjs/next-auth/discussions/1350#discussioncomment-2145362) by [mojtabajahannia](https://github.com/mojtabajahannia).
11 |
12 | Not sure if this is the best approach, so all suggestions welcome.
13 |
14 | ## Django
15 |
16 | ### Environment
17 |
18 | ```
19 | cd django
20 | python3 -m venv ./env
21 | source ./env/bin/activate
22 | ```
23 |
24 | ### Installation
25 |
26 | ```
27 | pip install --upgrade pip
28 | pip install -r ./requirements.txt
29 | ```
30 |
31 | ### Setup
32 |
33 | ```
34 | python manage.py makemigrations
35 | python manage.py migrate
36 | ```
37 |
38 | ### Admin
39 |
40 | Add admin user to demonstrate unrestricted access to all client-side routes.
41 |
42 | ```
43 | python manage.py createsuperuser
44 | ```
45 |
46 | ### Visitor
47 |
48 | Add an ordinary user to demonstrate restricted access to client-side /admin route.
49 |
50 | ```
51 | python manage.py shell
52 |
53 | >>> from django.contrib.auth.models import User
54 | >>> user=User.objects.create_user('visitor', password='visitor')
55 | >>> user.save()
56 | >>> exit()
57 | ```
58 |
59 | ### Start
60 |
61 | ```
62 | python manage.py runserver
63 | ```
64 |
65 | ## Next.js
66 |
67 | Install modules and run development server.
68 |
69 | ### Installation
70 |
71 | ```
72 | cd nextjs
73 | npm install
74 | ```
75 |
76 | ### Local environment
77 |
78 | ```
79 | cp .env.local.example .env.local
80 | ```
81 |
82 | ### Start
83 |
84 | ```
85 | npm run dev
86 | ```
87 |
88 | **Note:** Based on the original [next-auth-sample](https://github.com/nextauthjs/next-auth-example) code.
89 |
--------------------------------------------------------------------------------
/django/.gitignore:
--------------------------------------------------------------------------------
1 | *.egg-info
2 | *.pot
3 | *.py[co]
4 | .tox/
5 | __pycache__
6 | MANIFEST
7 | dist/
8 | docs/_build/
9 | docs/locale/
10 | node_modules/
11 | tests/coverage_html/
12 | tests/.coverage
13 | build/
14 | tests/report/
15 |
16 | .DS_Store
17 |
18 | env/
19 |
20 | db.sqlite3
--------------------------------------------------------------------------------
/django/apps/example/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hillmark/next-auth-django/a6368729161aaf71c369da0df62c98e3630f71e7/django/apps/example/__init__.py
--------------------------------------------------------------------------------
/django/apps/example/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/django/apps/example/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class ExampleConfig(AppConfig):
5 | default_auto_field = 'django.db.models.BigAutoField'
6 | name = 'apps.example'
7 |
--------------------------------------------------------------------------------
/django/apps/example/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hillmark/next-auth-django/a6368729161aaf71c369da0df62c98e3630f71e7/django/apps/example/migrations/__init__.py
--------------------------------------------------------------------------------
/django/apps/example/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 |
--------------------------------------------------------------------------------
/django/apps/example/serializers.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.models import User, Group
2 | from rest_framework import serializers
3 | from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
4 |
5 |
6 | class UserSerializer(serializers.HyperlinkedModelSerializer):
7 | class Meta:
8 | model = User
9 | fields = ['url', 'username', 'email', 'groups']
10 |
11 |
12 | class GroupSerializer(serializers.HyperlinkedModelSerializer):
13 | class Meta:
14 | model = Group
15 | fields = ['url', 'name']
16 |
17 | class ExampleTokenObtainPairSerializer(TokenObtainPairSerializer):
18 | """Customizes JWT default Serializer to add more information about user"""
19 | @classmethod
20 | def get_token(cls, user):
21 | token = super().get_token(user)
22 | token['username'] = user.username
23 | token['email'] = user.email
24 | token['is_superuser'] = user.is_superuser
25 | token['is_staff'] = user.is_staff
26 | return token
--------------------------------------------------------------------------------
/django/apps/example/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/django/apps/example/views.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.models import User, Group
2 | from rest_framework import viewsets
3 | from rest_framework import permissions
4 | from rest_framework_simplejwt.views import TokenObtainPairView
5 |
6 | from .serializers import UserSerializer, GroupSerializer,ExampleTokenObtainPairSerializer
7 |
8 |
9 | class UserViewSet(viewsets.ModelViewSet):
10 | """
11 | API endpoint that allows users to be viewed or edited.
12 | """
13 | queryset = User.objects.all().order_by('-date_joined')
14 | serializer_class = UserSerializer
15 | permission_classes = [permissions.IsAuthenticated]
16 |
17 |
18 | class GroupViewSet(viewsets.ModelViewSet):
19 | """
20 | API endpoint that allows groups to be viewed or edited.
21 | """
22 | queryset = Group.objects.all()
23 | serializer_class = GroupSerializer
24 | permission_classes = [permissions.IsAuthenticated]
25 |
26 |
27 | class ExampleTokenObtainPairView(TokenObtainPairView):
28 | # Replace the serializer with your custom
29 | serializer_class = ExampleTokenObtainPairSerializer
--------------------------------------------------------------------------------
/django/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 |
7 | def main():
8 | """Run administrative tasks."""
9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
10 | try:
11 | from django.core.management import execute_from_command_line
12 | except ImportError as exc:
13 | raise ImportError(
14 | "Couldn't import Django. Are you sure it's installed and "
15 | "available on your PYTHONPATH environment variable? Did you "
16 | "forget to activate a virtual environment?"
17 | ) from exc
18 | execute_from_command_line(sys.argv)
19 |
20 |
21 | if __name__ == '__main__':
22 | main()
23 |
--------------------------------------------------------------------------------
/django/media/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hillmark/next-auth-django/a6368729161aaf71c369da0df62c98e3630f71e7/django/media/.gitkeep
--------------------------------------------------------------------------------
/django/project/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hillmark/next-auth-django/a6368729161aaf71c369da0df62c98e3630f71e7/django/project/__init__.py
--------------------------------------------------------------------------------
/django/project/asgi.py:
--------------------------------------------------------------------------------
1 | """
2 | ASGI config for project project.
3 |
4 | It exposes the ASGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.asgi import get_asgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
15 |
16 | application = get_asgi_application()
17 |
--------------------------------------------------------------------------------
/django/project/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for project project.
3 |
4 | Generated by 'django-admin startproject' using Django 3.2.15.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.2/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/3.2/ref/settings/
11 | """
12 | import os
13 |
14 | from pathlib import Path
15 |
16 | # Build paths inside the project like this: BASE_DIR / 'subdir'.
17 | BASE_DIR = Path(__file__).resolve().parent.parent
18 |
19 |
20 | # Quick-start development settings - unsuitable for production
21 | # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
22 |
23 | # SECURITY WARNING: keep the secret key used in production secret!
24 | SECRET_KEY = 'django-insecure-t%cgmm5zkgtfx!!*+ry&cqz^ei7ylc7$l&n5iu1ukzk5slri'
25 |
26 | # SECURITY WARNING: don't run with debug turned on in production!
27 | DEBUG = True
28 |
29 | ALLOWED_HOSTS = []
30 |
31 |
32 | # Application definition
33 |
34 | INSTALLED_APPS = [
35 | 'django.contrib.admin',
36 | 'django.contrib.auth',
37 | 'django.contrib.contenttypes',
38 | 'django.contrib.sessions',
39 | 'django.contrib.messages',
40 | 'django.contrib.staticfiles',
41 | # vendor
42 | 'corsheaders',
43 | 'rest_framework',
44 | # custom
45 | 'apps.example'
46 | ]
47 |
48 | MIDDLEWARE = [
49 | 'django.middleware.security.SecurityMiddleware',
50 | 'django.contrib.sessions.middleware.SessionMiddleware',
51 | 'corsheaders.middleware.CorsMiddleware', # cors
52 | 'django.middleware.common.CommonMiddleware',
53 | 'django.middleware.csrf.CsrfViewMiddleware',
54 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
55 | 'django.contrib.messages.middleware.MessageMiddleware',
56 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
57 | ]
58 |
59 | ROOT_URLCONF = 'project.urls'
60 |
61 | TEMPLATES = [
62 | {
63 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
64 | 'DIRS': [BASE_DIR / 'templates/',],
65 | 'APP_DIRS': True,
66 | 'OPTIONS': {
67 | 'context_processors': [
68 | 'django.template.context_processors.debug',
69 | 'django.template.context_processors.request',
70 | 'django.contrib.auth.context_processors.auth',
71 | 'django.contrib.messages.context_processors.messages',
72 | ],
73 | },
74 | },
75 | ]
76 |
77 | WSGI_APPLICATION = 'project.wsgi.application'
78 |
79 |
80 | # Database
81 | # https://docs.djangoproject.com/en/3.2/ref/settings/#databases
82 |
83 | DATABASES = {
84 | 'default': {
85 | 'ENGINE': 'django.db.backends.sqlite3',
86 | 'NAME': BASE_DIR / 'db.sqlite3',
87 | }
88 | }
89 |
90 |
91 | # Password validation
92 | # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
93 |
94 | AUTH_PASSWORD_VALIDATORS = [
95 | {
96 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
97 | },
98 | {
99 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
100 | },
101 | {
102 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
103 | },
104 | {
105 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
106 | },
107 | ]
108 |
109 |
110 | # Internationalization
111 | # https://docs.djangoproject.com/en/3.2/topics/i18n/
112 |
113 | LANGUAGE_CODE = 'en-us'
114 |
115 | TIME_ZONE = 'UTC'
116 |
117 | USE_I18N = True
118 |
119 | USE_L10N = True
120 |
121 | USE_TZ = True
122 |
123 |
124 | # Static files (CSS, JavaScript, Images)
125 | # https://docs.djangoproject.com/en/3.2/howto/static-files/
126 |
127 | STATIC_URL = '/static/'
128 |
129 | # Default primary key field type
130 | # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
131 |
132 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
133 |
134 | REST_FRAMEWORK = {
135 | 'DEFAULT_AUTHENTICATION_CLASSES': (
136 | 'rest_framework_simplejwt.authentication.JWTAuthentication',
137 | ),
138 | 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
139 | 'PAGE_SIZE': 10
140 | }
141 |
142 | CORS_ALLOWED_ORIGINS = [
143 | 'http://localhost:3000',
144 | ]
145 |
146 | # Static files (CSS, JavaScript, Images)
147 | STATIC_URL = '/static/'
148 | STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
149 |
150 | # Media Files
151 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
152 | MEDIA_URL = '/media/'
--------------------------------------------------------------------------------
/django/project/urls.py:
--------------------------------------------------------------------------------
1 | """project URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/3.2/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.urls import include, path
14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15 | """
16 | from django.contrib import admin
17 | from django.urls import include, path
18 | from rest_framework import routers
19 | from rest_framework_simplejwt.views import (
20 | TokenObtainPairView,
21 | TokenRefreshView,
22 | TokenVerifyView
23 | )
24 | from apps.example import views
25 | from apps.example.views import (
26 | ExampleTokenObtainPairView
27 | )
28 |
29 | router = routers.DefaultRouter()
30 | router.register(r'users', views.UserViewSet)
31 | router.register(r'groups', views.GroupViewSet)
32 |
33 | urlpatterns = [
34 | path('admin/', admin.site.urls),
35 | path('api/', include(router.urls)),
36 | path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
37 | path('api/token/', ExampleTokenObtainPairView.as_view(), name='token_obtain_pair'),
38 | path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
39 | path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
40 | ]
--------------------------------------------------------------------------------
/django/project/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for project project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/django/requirements.txt:
--------------------------------------------------------------------------------
1 | Django==3.2.15
2 | django-cors-headers==3.13.0
3 | djangorestframework==3.14.0
4 | djangorestframework-simplejwt==5.2.0
5 |
6 |
--------------------------------------------------------------------------------
/django/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hillmark/next-auth-django/a6368729161aaf71c369da0df62c98e3630f71e7/django/static/.gitkeep
--------------------------------------------------------------------------------
/django/templates/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hillmark/next-auth-django/a6368729161aaf71c369da0df62c98e3630f71e7/django/templates/.gitkeep
--------------------------------------------------------------------------------
/nextjs/.env.local.example:
--------------------------------------------------------------------------------
1 | NEXTAUTH_URL=http://localhost:3000
2 | NEXTAUTH_SECRET= # Linux: `openssl rand -hex 32` or go to https://generate-secret.now.sh/32
3 |
4 | APPLE_ID=
5 | APPLE_TEAM_ID=
6 | APPLE_PRIVATE_KEY=
7 | APPLE_KEY_ID=
8 |
9 | AUTH0_ID=
10 | AUTH0_SECRET=
11 | AUTH0_ISSUER=
12 |
13 | FACEBOOK_ID=
14 | FACEBOOK_SECRET=
15 |
16 | GITHUB_ID=
17 | GITHUB_SECRET=
18 |
19 | GOOGLE_ID=
20 | GOOGLE_SECRET=
21 |
22 | TWITTER_ID=
23 | TWITTER_SECRET=
24 |
25 | EMAIL_SERVER=smtp://username:password@smtp.example.com:587
26 | EMAIL_FROM=NextAuth
27 |
28 | DATABASE_URL=sqlite://localhost/:memory:?synchronize=true
29 |
--------------------------------------------------------------------------------
/nextjs/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # https://docs.github.com/en/github/administering-a-repository/displaying-a-sponsor-button-in-your-repository
2 |
3 | open_collective: nextauth
4 | github: [balazsorban44]
5 |
--------------------------------------------------------------------------------
/nextjs/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | node_modules/
4 | logs
5 | *.log
6 | npm-debug.log*
7 | yarn-debug.log*
8 | yarn-error.log*
9 | lerna-debug.log*
10 | .yarn-integrity
11 | .npm
12 |
13 | .eslintcache
14 |
15 | *.tsbuildinfo
16 | next-env.d.ts
17 |
18 | .next
19 | .vercel
20 | .env*.local
--------------------------------------------------------------------------------
/nextjs/LICENSE:
--------------------------------------------------------------------------------
1 | ISC License
2 |
3 | Copyright (c) 2018-2021, Iain Collins
4 |
5 | Permission to use, copy, modify, and/or distribute this software for any
6 | purpose with or without fee is hereby granted, provided that the above
7 | copyright notice and this permission notice appear in all copies.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--------------------------------------------------------------------------------
/nextjs/README.md:
--------------------------------------------------------------------------------
1 | > The example repository is maintained from a [monorepo](https://github.com/nextauthjs/next-auth/tree/main/apps/example-nextjs). Pull Requests should be opened against [`nextauthjs/next-auth`](https://github.com/nextauthjs/next-auth).
2 |
3 |
4 |
5 |
6 |
NextAuth.js Example App
7 |
8 | Open Source. Full Stack. Own Your Data.
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | ## Overview
27 |
28 | NextAuth.js is a complete open source authentication solution.
29 |
30 | This is an example application that shows how `next-auth` is applied to a basic Next.js app.
31 |
32 | The deployed version can be found at [`next-auth-example.vercel.app`](https://next-auth-example.vercel.app)
33 |
34 | ### About NextAuth.js
35 |
36 | NextAuth.js is an easy to implement, full-stack (client/server) open source authentication library originally designed for [Next.js](https://nextjs.org) and [Serverless](https://vercel.com). Our goal is to [support even more frameworks](https://github.com/nextauthjs/next-auth/issues/2294) in the future.
37 |
38 | Go to [next-auth.js.org](https://next-auth.js.org) for more information and documentation.
39 |
40 | > *NextAuth.js is not officially associated with Vercel or Next.js.*
41 |
42 | ## Getting Started
43 |
44 | ### 1. Clone the repository and install dependencies
45 |
46 | ```
47 | git clone https://github.com/nextauthjs/next-auth-example.git
48 | cd next-auth-example
49 | npm install
50 | ```
51 |
52 | ### 2. Configure your local environment
53 |
54 | Copy the .env.local.example file in this directory to .env.local (which will be ignored by Git):
55 |
56 | ```
57 | cp .env.local.example .env.local
58 | ```
59 |
60 | Add details for one or more providers (e.g. Google, Twitter, GitHub, Email, etc).
61 |
62 | #### Database
63 |
64 | A database is needed to persist user accounts and to support email sign in. However, you can still use NextAuth.js for authentication without a database by using OAuth for authentication. If you do not specify a database, [JSON Web Tokens](https://jwt.io/introduction) will be enabled by default.
65 |
66 | You **can** skip configuring a database and come back to it later if you want.
67 |
68 | For more information about setting up a database, please check out the following links:
69 |
70 | * Docs: [next-auth.js.org/adapters/overview](https://next-auth.js.org/adapters/overview)
71 |
72 | ### 3. Configure Authentication Providers
73 |
74 | 1. Review and update options in `pages/api/auth/[...nextauth].js` as needed.
75 |
76 | 2. When setting up OAuth, in the developer admin page for each of your OAuth services, you should configure the callback URL to use a callback path of `{server}/api/auth/callback/{provider}`.
77 |
78 | e.g. For Google OAuth you would use: `http://localhost:3000/api/auth/callback/google`
79 |
80 | A list of configured providers and their callback URLs is available from the endpoint `/api/auth/providers`. You can find more information at https://next-auth.js.org/configuration/providers/oauth
81 |
82 | 3. You can also choose to specify an SMTP server for passwordless sign in via email.
83 |
84 | ### 4. Start the application
85 |
86 | To run your site locally, use:
87 |
88 | ```
89 | npm run dev
90 | ```
91 |
92 | To run it in production mode, use:
93 |
94 | ```
95 | npm run build
96 | npm run start
97 | ```
98 |
99 | ### 5. Preparing for Production
100 |
101 | Follow the [Deployment documentation](https://next-auth.js.org/deployment)
102 |
103 | ## Acknowledgements
104 |
105 |
106 |
107 |
108 | Thanks to Vercel sponsoring this project by allowing it to be deployed for free for the entire NextAuth.js Team
109 |
110 | ## License
111 |
112 | ISC
113 |
114 |
--------------------------------------------------------------------------------
/nextjs/components/access-denied.js:
--------------------------------------------------------------------------------
1 | import { signIn } from "next-auth/react"
2 |
3 | export default function AccessDenied() {
4 | return (
5 | <>
6 | Access Denied
7 |
8 | {
11 | e.preventDefault()
12 | signIn()
13 | }}
14 | >
15 | You must be signed in to view this page
16 |
17 |
18 | >
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/nextjs/components/footer.js:
--------------------------------------------------------------------------------
1 | import Link from "next/link"
2 | import styles from "./footer.module.css"
3 | import packageJSON from "../package.json"
4 |
5 | export default function Footer() {
6 | return (
7 |
8 |
9 |
10 |
11 | Documentation
12 |
13 |
14 | NPM
15 |
16 |
17 | GitHub
18 |
19 |
20 | Policy
21 |
22 |
23 | next-auth@{packageJSON.dependencies["next-auth"]}
24 |
25 |
26 |
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/nextjs/components/footer.module.css:
--------------------------------------------------------------------------------
1 | .footer {
2 | margin-top: 2rem;
3 | }
4 |
5 | .navItems {
6 | margin-bottom: 1rem;
7 | padding: 0;
8 | list-style: none;
9 | }
10 |
11 | .navItem {
12 | display: inline-block;
13 | margin-right: 1rem;
14 | }
15 |
--------------------------------------------------------------------------------
/nextjs/components/header.js:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | import { signIn, signOut, useSession } from "next-auth/react";
3 | import styles from "./header.module.css";
4 |
5 | // The approach used in this component shows how to build a sign in and sign out
6 | // component that works on pages which support both client and server side
7 | // rendering, and avoids any flash incorrect content on initial page load.
8 | export default function Header() {
9 | const { data: session, status } = useSession();
10 | const loading = status === "loading";
11 |
12 | return (
13 |
93 | );
94 | }
95 |
--------------------------------------------------------------------------------
/nextjs/components/header.module.css:
--------------------------------------------------------------------------------
1 | /* Set min-height to avoid page reflow while session loading */
2 | .signedInStatus {
3 | display: block;
4 | min-height: 4rem;
5 | width: 100%;
6 | }
7 |
8 | .loading,
9 | .loaded {
10 | position: relative;
11 | top: 0;
12 | opacity: 1;
13 | overflow: hidden;
14 | border-radius: 0 0 0.6rem 0.6rem;
15 | padding: 0.6rem 1rem;
16 | margin: 0;
17 | background-color: rgba(0, 0, 0, 0.05);
18 | transition: all 0.2s ease-in;
19 | }
20 |
21 | .loading {
22 | top: -2rem;
23 | opacity: 0;
24 | }
25 |
26 | .signedInText,
27 | .notSignedInText {
28 | position: absolute;
29 | padding-top: 0.8rem;
30 | left: 1rem;
31 | right: 6.5rem;
32 | white-space: nowrap;
33 | text-overflow: ellipsis;
34 | overflow: hidden;
35 | display: inherit;
36 | z-index: 1;
37 | line-height: 1.3rem;
38 | }
39 |
40 | .signedInText {
41 | padding-top: 0rem;
42 | left: 4.6rem;
43 | }
44 |
45 | .avatar {
46 | border-radius: 2rem;
47 | float: left;
48 | height: 2.8rem;
49 | width: 2.8rem;
50 | background-color: white;
51 | background-size: cover;
52 | background-repeat: no-repeat;
53 | }
54 |
55 | .button,
56 | .buttonPrimary {
57 | float: right;
58 | margin-right: -0.4rem;
59 | font-weight: 500;
60 | border-radius: 0.3rem;
61 | cursor: pointer;
62 | font-size: 1rem;
63 | line-height: 1.4rem;
64 | padding: 0.7rem 0.8rem;
65 | position: relative;
66 | z-index: 10;
67 | background-color: transparent;
68 | color: #555;
69 | }
70 |
71 | .buttonPrimary {
72 | background-color: #346df1;
73 | border-color: #346df1;
74 | color: #fff;
75 | text-decoration: none;
76 | padding: 0.7rem 1.4rem;
77 | }
78 |
79 | .buttonPrimary:hover {
80 | box-shadow: inset 0 0 5rem rgba(0, 0, 0, 0.2);
81 | }
82 |
83 | .navItems {
84 | margin-bottom: 2rem;
85 | padding: 0;
86 | list-style: none;
87 | }
88 |
89 | .navItem {
90 | display: inline-block;
91 | margin-right: 1rem;
92 | }
93 |
--------------------------------------------------------------------------------
/nextjs/components/layout.js:
--------------------------------------------------------------------------------
1 | import Header from "./header";
2 | import Footer from "./footer";
3 |
4 | export default function Layout({ children }) {
5 | return (
6 | <>
7 |
8 | {children}
9 |
10 | >
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/nextjs/middleware.js:
--------------------------------------------------------------------------------
1 | import { withAuth } from "next-auth/middleware";
2 |
3 | // More on how NextAuth.js middleware works: https://next-auth.js.org/configuration/nextjs#middleware
4 | export default withAuth(
5 | function middleware(req) {
6 | console.log("authorized");
7 | },
8 | {
9 | callbacks: {
10 | authorized({ req, token }) {
11 | // `/admin` requires is_superuser
12 | if (req.nextUrl.pathname === "/admin") {
13 | return token?.is_superuser;
14 | }
15 | // `/me` only requires the user to be logged in
16 | return !!token;
17 | },
18 | },
19 | }
20 | );
21 |
22 | export const config = { matcher: ["/admin", "/me"] };
23 |
--------------------------------------------------------------------------------
/nextjs/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nextjs",
3 | "lockfileVersion": 2,
4 | "requires": true,
5 | "packages": {
6 | "": {
7 | "dependencies": {
8 | "jwt-decode": "^3.1.2",
9 | "next": "latest",
10 | "next-auth": "latest",
11 | "nodemailer": "^6",
12 | "react": "^18.2.0",
13 | "react-dom": "^18.2.0"
14 | },
15 | "devDependencies": {
16 | "@types/node": "^17",
17 | "@types/react": "^18.0.15",
18 | "typescript": "^4"
19 | }
20 | },
21 | "node_modules/@babel/runtime": {
22 | "version": "7.20.1",
23 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz",
24 | "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==",
25 | "dependencies": {
26 | "regenerator-runtime": "^0.13.10"
27 | },
28 | "engines": {
29 | "node": ">=6.9.0"
30 | }
31 | },
32 | "node_modules/@next/env": {
33 | "version": "13.0.2",
34 | "resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.2.tgz",
35 | "integrity": "sha512-Qb6WPuRriGIQ19qd6NBxpcrFOfj8ziN7l9eZUfwff5gl4zLXluqtuZPddYZM/oWjN53ZYcuRXzL+oowKyJeYtA=="
36 | },
37 | "node_modules/@next/swc-android-arm-eabi": {
38 | "version": "13.0.2",
39 | "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.2.tgz",
40 | "integrity": "sha512-X54UQCTFyOGnJP//Z71dPPlp4BCYcQL2ncikKXQcPzVpqPs4C3m+tKC8ivBNH6edAXkppwsLRz1/yQwgSZ9Swg==",
41 | "cpu": [
42 | "arm"
43 | ],
44 | "optional": true,
45 | "os": [
46 | "android"
47 | ],
48 | "engines": {
49 | "node": ">= 10"
50 | }
51 | },
52 | "node_modules/@next/swc-android-arm64": {
53 | "version": "13.0.2",
54 | "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.2.tgz",
55 | "integrity": "sha512-1P00Kv8uKaLubqo7JzPrTqgFAzSOmfb8iwqJrOb9in5IvTRtNGlkR4hU0sXzqbQNM/+SaYxze6Z5ry1IDyb/cQ==",
56 | "cpu": [
57 | "arm64"
58 | ],
59 | "optional": true,
60 | "os": [
61 | "android"
62 | ],
63 | "engines": {
64 | "node": ">= 10"
65 | }
66 | },
67 | "node_modules/@next/swc-darwin-arm64": {
68 | "version": "13.0.2",
69 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.2.tgz",
70 | "integrity": "sha512-1zGIOkInkOLRv0QQGZ+3wffYsyKI4vIy62LYTvDWUn7TAYqnmXwougp9NSLqDeagLwgsv2URrykyAFixA/YqxA==",
71 | "cpu": [
72 | "arm64"
73 | ],
74 | "optional": true,
75 | "os": [
76 | "darwin"
77 | ],
78 | "engines": {
79 | "node": ">= 10"
80 | }
81 | },
82 | "node_modules/@next/swc-darwin-x64": {
83 | "version": "13.0.2",
84 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.2.tgz",
85 | "integrity": "sha512-ECDAjoMP1Y90cARaelS6X+k6BQx+MikAYJ8f/eaJrLur44NIOYc9HA/dgcTp5jenguY4yT8V+HCquLjAVle6fA==",
86 | "cpu": [
87 | "x64"
88 | ],
89 | "optional": true,
90 | "os": [
91 | "darwin"
92 | ],
93 | "engines": {
94 | "node": ">= 10"
95 | }
96 | },
97 | "node_modules/@next/swc-freebsd-x64": {
98 | "version": "13.0.2",
99 | "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.2.tgz",
100 | "integrity": "sha512-2DcL/ofQdBnQX3IoI9sjlIAyLCD1oZoUBuhrhWbejvBQjutWrI0JTEv9uG69WcxWhVMm3BCsjv8GK2/68OKp7A==",
101 | "cpu": [
102 | "x64"
103 | ],
104 | "optional": true,
105 | "os": [
106 | "freebsd"
107 | ],
108 | "engines": {
109 | "node": ">= 10"
110 | }
111 | },
112 | "node_modules/@next/swc-linux-arm-gnueabihf": {
113 | "version": "13.0.2",
114 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.2.tgz",
115 | "integrity": "sha512-Y3OQF1CSBSWW2vGkmvOIuOUNqOq8qX7f1ZpcKUVWP3/Uq++DZmVi9d18lgnSe1I3QFqc+nXWyun9ljsN83j0sw==",
116 | "cpu": [
117 | "arm"
118 | ],
119 | "optional": true,
120 | "os": [
121 | "linux"
122 | ],
123 | "engines": {
124 | "node": ">= 10"
125 | }
126 | },
127 | "node_modules/@next/swc-linux-arm64-gnu": {
128 | "version": "13.0.2",
129 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.2.tgz",
130 | "integrity": "sha512-mNyzwsFF6kwZYEjnGicx9ksDZYEZvyzEc1BtCu8vdZi/v8UeixQwCiAT6FyYX9uxMPEkzk8qiU0t0u9gvltsKw==",
131 | "cpu": [
132 | "arm64"
133 | ],
134 | "optional": true,
135 | "os": [
136 | "linux"
137 | ],
138 | "engines": {
139 | "node": ">= 10"
140 | }
141 | },
142 | "node_modules/@next/swc-linux-arm64-musl": {
143 | "version": "13.0.2",
144 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.2.tgz",
145 | "integrity": "sha512-M6SdYjWgRrY3tJBxz0663zCRPTu5BRONmxlftKWWHv9LjAJ59neTLaGj4rp0A08DkJglZIoCkLOzLrzST6TGag==",
146 | "cpu": [
147 | "arm64"
148 | ],
149 | "optional": true,
150 | "os": [
151 | "linux"
152 | ],
153 | "engines": {
154 | "node": ">= 10"
155 | }
156 | },
157 | "node_modules/@next/swc-linux-x64-gnu": {
158 | "version": "13.0.2",
159 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.2.tgz",
160 | "integrity": "sha512-pi63RoxvG4ES1KS06Zpm0MATVIXTs/TIbLbdckeLoM40u1d3mQl/+hSSrLRSxzc2OtyL8fh92sM4gkJrQXAMAw==",
161 | "cpu": [
162 | "x64"
163 | ],
164 | "optional": true,
165 | "os": [
166 | "linux"
167 | ],
168 | "engines": {
169 | "node": ">= 10"
170 | }
171 | },
172 | "node_modules/@next/swc-linux-x64-musl": {
173 | "version": "13.0.2",
174 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.2.tgz",
175 | "integrity": "sha512-9Pv91gfYnDONgjtRm78n64b/c54+azeHtlnqBLTnIFWSMBDRl1/WDkhKWIj3fBGPLimtK7Tko3ULR3og9RRUPw==",
176 | "cpu": [
177 | "x64"
178 | ],
179 | "optional": true,
180 | "os": [
181 | "linux"
182 | ],
183 | "engines": {
184 | "node": ">= 10"
185 | }
186 | },
187 | "node_modules/@next/swc-win32-arm64-msvc": {
188 | "version": "13.0.2",
189 | "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.2.tgz",
190 | "integrity": "sha512-Nvewe6YZaizAkGHHprbMkYqQulBjZCHKBGKeFPwoPtOA+a2Qi4pZzc/qXFyC5/2A6Z0mr2U1zg9rd04WBYMwBw==",
191 | "cpu": [
192 | "arm64"
193 | ],
194 | "optional": true,
195 | "os": [
196 | "win32"
197 | ],
198 | "engines": {
199 | "node": ">= 10"
200 | }
201 | },
202 | "node_modules/@next/swc-win32-ia32-msvc": {
203 | "version": "13.0.2",
204 | "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.2.tgz",
205 | "integrity": "sha512-ZUBYGZw5G3QrqDpRq1EWi3aHmvPZM8ijK5TFL6UbH16cYQ0JpANmuG2P66KB93Qe/lWWzbeAZk/tj1XqwoCuPA==",
206 | "cpu": [
207 | "ia32"
208 | ],
209 | "optional": true,
210 | "os": [
211 | "win32"
212 | ],
213 | "engines": {
214 | "node": ">= 10"
215 | }
216 | },
217 | "node_modules/@next/swc-win32-x64-msvc": {
218 | "version": "13.0.2",
219 | "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.2.tgz",
220 | "integrity": "sha512-fA9uW1dm7C0mEYGcKlbmLcVm2sKcye+1kPxh2cM4jVR+kQQMtHWsjIzeSpe2grQLSDan06z4n6hbr8b1c3hA8w==",
221 | "cpu": [
222 | "x64"
223 | ],
224 | "optional": true,
225 | "os": [
226 | "win32"
227 | ],
228 | "engines": {
229 | "node": ">= 10"
230 | }
231 | },
232 | "node_modules/@panva/hkdf": {
233 | "version": "1.0.2",
234 | "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.0.2.tgz",
235 | "integrity": "sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA==",
236 | "funding": {
237 | "url": "https://github.com/sponsors/panva"
238 | }
239 | },
240 | "node_modules/@swc/helpers": {
241 | "version": "0.4.11",
242 | "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.11.tgz",
243 | "integrity": "sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw==",
244 | "dependencies": {
245 | "tslib": "^2.4.0"
246 | }
247 | },
248 | "node_modules/@types/node": {
249 | "version": "17.0.45",
250 | "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
251 | "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==",
252 | "dev": true
253 | },
254 | "node_modules/@types/prop-types": {
255 | "version": "15.7.5",
256 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
257 | "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
258 | "dev": true
259 | },
260 | "node_modules/@types/react": {
261 | "version": "18.0.25",
262 | "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.25.tgz",
263 | "integrity": "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==",
264 | "dev": true,
265 | "dependencies": {
266 | "@types/prop-types": "*",
267 | "@types/scheduler": "*",
268 | "csstype": "^3.0.2"
269 | }
270 | },
271 | "node_modules/@types/scheduler": {
272 | "version": "0.16.2",
273 | "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
274 | "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==",
275 | "dev": true
276 | },
277 | "node_modules/caniuse-lite": {
278 | "version": "1.0.30001431",
279 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz",
280 | "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==",
281 | "funding": [
282 | {
283 | "type": "opencollective",
284 | "url": "https://opencollective.com/browserslist"
285 | },
286 | {
287 | "type": "tidelift",
288 | "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
289 | }
290 | ]
291 | },
292 | "node_modules/client-only": {
293 | "version": "0.0.1",
294 | "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
295 | "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
296 | },
297 | "node_modules/cookie": {
298 | "version": "0.5.0",
299 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
300 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
301 | "engines": {
302 | "node": ">= 0.6"
303 | }
304 | },
305 | "node_modules/csstype": {
306 | "version": "3.1.1",
307 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
308 | "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==",
309 | "dev": true
310 | },
311 | "node_modules/jose": {
312 | "version": "4.11.0",
313 | "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.0.tgz",
314 | "integrity": "sha512-wLe+lJHeG8Xt6uEubS4x0LVjS/3kXXu9dGoj9BNnlhYq7Kts0Pbb2pvv5KiI0yaKH/eaiR0LUOBhOVo9ktd05A==",
315 | "funding": {
316 | "url": "https://github.com/sponsors/panva"
317 | }
318 | },
319 | "node_modules/js-tokens": {
320 | "version": "4.0.0",
321 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
322 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
323 | },
324 | "node_modules/jwt-decode": {
325 | "version": "3.1.2",
326 | "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
327 | "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
328 | },
329 | "node_modules/loose-envify": {
330 | "version": "1.4.0",
331 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
332 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
333 | "dependencies": {
334 | "js-tokens": "^3.0.0 || ^4.0.0"
335 | },
336 | "bin": {
337 | "loose-envify": "cli.js"
338 | }
339 | },
340 | "node_modules/lru-cache": {
341 | "version": "6.0.0",
342 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
343 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
344 | "dependencies": {
345 | "yallist": "^4.0.0"
346 | },
347 | "engines": {
348 | "node": ">=10"
349 | }
350 | },
351 | "node_modules/nanoid": {
352 | "version": "3.3.4",
353 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
354 | "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
355 | "bin": {
356 | "nanoid": "bin/nanoid.cjs"
357 | },
358 | "engines": {
359 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
360 | }
361 | },
362 | "node_modules/next": {
363 | "version": "13.0.2",
364 | "resolved": "https://registry.npmjs.org/next/-/next-13.0.2.tgz",
365 | "integrity": "sha512-uQ5z5e4D9mOe8+upy6bQdYYjo/kk1v3jMW87kTy2TgAyAsEO+CkwRnMgyZ4JoHEnhPZLHwh7dk0XymRNLe1gFw==",
366 | "dependencies": {
367 | "@next/env": "13.0.2",
368 | "@swc/helpers": "0.4.11",
369 | "caniuse-lite": "^1.0.30001406",
370 | "postcss": "8.4.14",
371 | "styled-jsx": "5.1.0",
372 | "use-sync-external-store": "1.2.0"
373 | },
374 | "bin": {
375 | "next": "dist/bin/next"
376 | },
377 | "engines": {
378 | "node": ">=14.6.0"
379 | },
380 | "optionalDependencies": {
381 | "@next/swc-android-arm-eabi": "13.0.2",
382 | "@next/swc-android-arm64": "13.0.2",
383 | "@next/swc-darwin-arm64": "13.0.2",
384 | "@next/swc-darwin-x64": "13.0.2",
385 | "@next/swc-freebsd-x64": "13.0.2",
386 | "@next/swc-linux-arm-gnueabihf": "13.0.2",
387 | "@next/swc-linux-arm64-gnu": "13.0.2",
388 | "@next/swc-linux-arm64-musl": "13.0.2",
389 | "@next/swc-linux-x64-gnu": "13.0.2",
390 | "@next/swc-linux-x64-musl": "13.0.2",
391 | "@next/swc-win32-arm64-msvc": "13.0.2",
392 | "@next/swc-win32-ia32-msvc": "13.0.2",
393 | "@next/swc-win32-x64-msvc": "13.0.2"
394 | },
395 | "peerDependencies": {
396 | "fibers": ">= 3.1.0",
397 | "node-sass": "^6.0.0 || ^7.0.0",
398 | "react": "^18.2.0",
399 | "react-dom": "^18.2.0",
400 | "sass": "^1.3.0"
401 | },
402 | "peerDependenciesMeta": {
403 | "fibers": {
404 | "optional": true
405 | },
406 | "node-sass": {
407 | "optional": true
408 | },
409 | "sass": {
410 | "optional": true
411 | }
412 | }
413 | },
414 | "node_modules/next-auth": {
415 | "version": "4.16.4",
416 | "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.16.4.tgz",
417 | "integrity": "sha512-KXW578+ER1u5RcWLwCHMdb/RIBIO6JM8r6xlf9RIPSKzkvDcX9FHiZfJS2vOq/SurHXPJZc4J3OS4IDJpF74Dw==",
418 | "dependencies": {
419 | "@babel/runtime": "^7.16.3",
420 | "@panva/hkdf": "^1.0.1",
421 | "cookie": "^0.5.0",
422 | "jose": "^4.9.3",
423 | "oauth": "^0.9.15",
424 | "openid-client": "^5.1.0",
425 | "preact": "^10.6.3",
426 | "preact-render-to-string": "^5.1.19",
427 | "uuid": "^8.3.2"
428 | },
429 | "engines": {
430 | "node": "^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0"
431 | },
432 | "peerDependencies": {
433 | "next": "^12.2.5 || ^13",
434 | "nodemailer": "^6.6.5",
435 | "react": "^17.0.2 || ^18",
436 | "react-dom": "^17.0.2 || ^18"
437 | },
438 | "peerDependenciesMeta": {
439 | "nodemailer": {
440 | "optional": true
441 | }
442 | }
443 | },
444 | "node_modules/nodemailer": {
445 | "version": "6.8.0",
446 | "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.8.0.tgz",
447 | "integrity": "sha512-EjYvSmHzekz6VNkNd12aUqAco+bOkRe3Of5jVhltqKhEsjw/y0PYPJfp83+s9Wzh1dspYAkUW/YNQ350NATbSQ==",
448 | "engines": {
449 | "node": ">=6.0.0"
450 | }
451 | },
452 | "node_modules/oauth": {
453 | "version": "0.9.15",
454 | "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
455 | "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
456 | },
457 | "node_modules/object-hash": {
458 | "version": "2.2.0",
459 | "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
460 | "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
461 | "engines": {
462 | "node": ">= 6"
463 | }
464 | },
465 | "node_modules/oidc-token-hash": {
466 | "version": "5.0.1",
467 | "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz",
468 | "integrity": "sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==",
469 | "engines": {
470 | "node": "^10.13.0 || >=12.0.0"
471 | }
472 | },
473 | "node_modules/openid-client": {
474 | "version": "5.3.0",
475 | "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.3.0.tgz",
476 | "integrity": "sha512-SykPCeZBZ/SxiBH5AWynvFUIDX3//2pgwc/3265alUmGHeCN03+X8uP+pHOVnCXCKfX/XOhO90qttAQ76XcGxA==",
477 | "dependencies": {
478 | "jose": "^4.10.0",
479 | "lru-cache": "^6.0.0",
480 | "object-hash": "^2.0.1",
481 | "oidc-token-hash": "^5.0.1"
482 | },
483 | "funding": {
484 | "url": "https://github.com/sponsors/panva"
485 | }
486 | },
487 | "node_modules/picocolors": {
488 | "version": "1.0.0",
489 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
490 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
491 | },
492 | "node_modules/postcss": {
493 | "version": "8.4.14",
494 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
495 | "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
496 | "funding": [
497 | {
498 | "type": "opencollective",
499 | "url": "https://opencollective.com/postcss/"
500 | },
501 | {
502 | "type": "tidelift",
503 | "url": "https://tidelift.com/funding/github/npm/postcss"
504 | }
505 | ],
506 | "dependencies": {
507 | "nanoid": "^3.3.4",
508 | "picocolors": "^1.0.0",
509 | "source-map-js": "^1.0.2"
510 | },
511 | "engines": {
512 | "node": "^10 || ^12 || >=14"
513 | }
514 | },
515 | "node_modules/preact": {
516 | "version": "10.11.2",
517 | "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.2.tgz",
518 | "integrity": "sha512-skAwGDFmgxhq1DCBHke/9e12ewkhc7WYwjuhHB8HHS8zkdtITXLRmUMTeol2ldxvLwYtwbFeifZ9uDDWuyL4Iw==",
519 | "funding": {
520 | "type": "opencollective",
521 | "url": "https://opencollective.com/preact"
522 | }
523 | },
524 | "node_modules/preact-render-to-string": {
525 | "version": "5.2.6",
526 | "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz",
527 | "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==",
528 | "dependencies": {
529 | "pretty-format": "^3.8.0"
530 | },
531 | "peerDependencies": {
532 | "preact": ">=10"
533 | }
534 | },
535 | "node_modules/pretty-format": {
536 | "version": "3.8.0",
537 | "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
538 | "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="
539 | },
540 | "node_modules/react": {
541 | "version": "18.2.0",
542 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
543 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
544 | "dependencies": {
545 | "loose-envify": "^1.1.0"
546 | },
547 | "engines": {
548 | "node": ">=0.10.0"
549 | }
550 | },
551 | "node_modules/react-dom": {
552 | "version": "18.2.0",
553 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
554 | "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
555 | "dependencies": {
556 | "loose-envify": "^1.1.0",
557 | "scheduler": "^0.23.0"
558 | },
559 | "peerDependencies": {
560 | "react": "^18.2.0"
561 | }
562 | },
563 | "node_modules/regenerator-runtime": {
564 | "version": "0.13.10",
565 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz",
566 | "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw=="
567 | },
568 | "node_modules/scheduler": {
569 | "version": "0.23.0",
570 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
571 | "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
572 | "dependencies": {
573 | "loose-envify": "^1.1.0"
574 | }
575 | },
576 | "node_modules/source-map-js": {
577 | "version": "1.0.2",
578 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
579 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
580 | "engines": {
581 | "node": ">=0.10.0"
582 | }
583 | },
584 | "node_modules/styled-jsx": {
585 | "version": "5.1.0",
586 | "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.0.tgz",
587 | "integrity": "sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ==",
588 | "dependencies": {
589 | "client-only": "0.0.1"
590 | },
591 | "engines": {
592 | "node": ">= 12.0.0"
593 | },
594 | "peerDependencies": {
595 | "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0"
596 | },
597 | "peerDependenciesMeta": {
598 | "@babel/core": {
599 | "optional": true
600 | },
601 | "babel-plugin-macros": {
602 | "optional": true
603 | }
604 | }
605 | },
606 | "node_modules/tslib": {
607 | "version": "2.4.1",
608 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
609 | "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
610 | },
611 | "node_modules/typescript": {
612 | "version": "4.8.4",
613 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
614 | "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
615 | "dev": true,
616 | "bin": {
617 | "tsc": "bin/tsc",
618 | "tsserver": "bin/tsserver"
619 | },
620 | "engines": {
621 | "node": ">=4.2.0"
622 | }
623 | },
624 | "node_modules/use-sync-external-store": {
625 | "version": "1.2.0",
626 | "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
627 | "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
628 | "peerDependencies": {
629 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
630 | }
631 | },
632 | "node_modules/uuid": {
633 | "version": "8.3.2",
634 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
635 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
636 | "bin": {
637 | "uuid": "dist/bin/uuid"
638 | }
639 | },
640 | "node_modules/yallist": {
641 | "version": "4.0.0",
642 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
643 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
644 | }
645 | },
646 | "dependencies": {
647 | "@babel/runtime": {
648 | "version": "7.20.1",
649 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz",
650 | "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==",
651 | "requires": {
652 | "regenerator-runtime": "^0.13.10"
653 | }
654 | },
655 | "@next/env": {
656 | "version": "13.0.2",
657 | "resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.2.tgz",
658 | "integrity": "sha512-Qb6WPuRriGIQ19qd6NBxpcrFOfj8ziN7l9eZUfwff5gl4zLXluqtuZPddYZM/oWjN53ZYcuRXzL+oowKyJeYtA=="
659 | },
660 | "@next/swc-android-arm-eabi": {
661 | "version": "13.0.2",
662 | "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.2.tgz",
663 | "integrity": "sha512-X54UQCTFyOGnJP//Z71dPPlp4BCYcQL2ncikKXQcPzVpqPs4C3m+tKC8ivBNH6edAXkppwsLRz1/yQwgSZ9Swg==",
664 | "optional": true
665 | },
666 | "@next/swc-android-arm64": {
667 | "version": "13.0.2",
668 | "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.2.tgz",
669 | "integrity": "sha512-1P00Kv8uKaLubqo7JzPrTqgFAzSOmfb8iwqJrOb9in5IvTRtNGlkR4hU0sXzqbQNM/+SaYxze6Z5ry1IDyb/cQ==",
670 | "optional": true
671 | },
672 | "@next/swc-darwin-arm64": {
673 | "version": "13.0.2",
674 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.2.tgz",
675 | "integrity": "sha512-1zGIOkInkOLRv0QQGZ+3wffYsyKI4vIy62LYTvDWUn7TAYqnmXwougp9NSLqDeagLwgsv2URrykyAFixA/YqxA==",
676 | "optional": true
677 | },
678 | "@next/swc-darwin-x64": {
679 | "version": "13.0.2",
680 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.2.tgz",
681 | "integrity": "sha512-ECDAjoMP1Y90cARaelS6X+k6BQx+MikAYJ8f/eaJrLur44NIOYc9HA/dgcTp5jenguY4yT8V+HCquLjAVle6fA==",
682 | "optional": true
683 | },
684 | "@next/swc-freebsd-x64": {
685 | "version": "13.0.2",
686 | "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.2.tgz",
687 | "integrity": "sha512-2DcL/ofQdBnQX3IoI9sjlIAyLCD1oZoUBuhrhWbejvBQjutWrI0JTEv9uG69WcxWhVMm3BCsjv8GK2/68OKp7A==",
688 | "optional": true
689 | },
690 | "@next/swc-linux-arm-gnueabihf": {
691 | "version": "13.0.2",
692 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.2.tgz",
693 | "integrity": "sha512-Y3OQF1CSBSWW2vGkmvOIuOUNqOq8qX7f1ZpcKUVWP3/Uq++DZmVi9d18lgnSe1I3QFqc+nXWyun9ljsN83j0sw==",
694 | "optional": true
695 | },
696 | "@next/swc-linux-arm64-gnu": {
697 | "version": "13.0.2",
698 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.2.tgz",
699 | "integrity": "sha512-mNyzwsFF6kwZYEjnGicx9ksDZYEZvyzEc1BtCu8vdZi/v8UeixQwCiAT6FyYX9uxMPEkzk8qiU0t0u9gvltsKw==",
700 | "optional": true
701 | },
702 | "@next/swc-linux-arm64-musl": {
703 | "version": "13.0.2",
704 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.2.tgz",
705 | "integrity": "sha512-M6SdYjWgRrY3tJBxz0663zCRPTu5BRONmxlftKWWHv9LjAJ59neTLaGj4rp0A08DkJglZIoCkLOzLrzST6TGag==",
706 | "optional": true
707 | },
708 | "@next/swc-linux-x64-gnu": {
709 | "version": "13.0.2",
710 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.2.tgz",
711 | "integrity": "sha512-pi63RoxvG4ES1KS06Zpm0MATVIXTs/TIbLbdckeLoM40u1d3mQl/+hSSrLRSxzc2OtyL8fh92sM4gkJrQXAMAw==",
712 | "optional": true
713 | },
714 | "@next/swc-linux-x64-musl": {
715 | "version": "13.0.2",
716 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.2.tgz",
717 | "integrity": "sha512-9Pv91gfYnDONgjtRm78n64b/c54+azeHtlnqBLTnIFWSMBDRl1/WDkhKWIj3fBGPLimtK7Tko3ULR3og9RRUPw==",
718 | "optional": true
719 | },
720 | "@next/swc-win32-arm64-msvc": {
721 | "version": "13.0.2",
722 | "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.2.tgz",
723 | "integrity": "sha512-Nvewe6YZaizAkGHHprbMkYqQulBjZCHKBGKeFPwoPtOA+a2Qi4pZzc/qXFyC5/2A6Z0mr2U1zg9rd04WBYMwBw==",
724 | "optional": true
725 | },
726 | "@next/swc-win32-ia32-msvc": {
727 | "version": "13.0.2",
728 | "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.2.tgz",
729 | "integrity": "sha512-ZUBYGZw5G3QrqDpRq1EWi3aHmvPZM8ijK5TFL6UbH16cYQ0JpANmuG2P66KB93Qe/lWWzbeAZk/tj1XqwoCuPA==",
730 | "optional": true
731 | },
732 | "@next/swc-win32-x64-msvc": {
733 | "version": "13.0.2",
734 | "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.2.tgz",
735 | "integrity": "sha512-fA9uW1dm7C0mEYGcKlbmLcVm2sKcye+1kPxh2cM4jVR+kQQMtHWsjIzeSpe2grQLSDan06z4n6hbr8b1c3hA8w==",
736 | "optional": true
737 | },
738 | "@panva/hkdf": {
739 | "version": "1.0.2",
740 | "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.0.2.tgz",
741 | "integrity": "sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA=="
742 | },
743 | "@swc/helpers": {
744 | "version": "0.4.11",
745 | "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.11.tgz",
746 | "integrity": "sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw==",
747 | "requires": {
748 | "tslib": "^2.4.0"
749 | }
750 | },
751 | "@types/node": {
752 | "version": "17.0.45",
753 | "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
754 | "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==",
755 | "dev": true
756 | },
757 | "@types/prop-types": {
758 | "version": "15.7.5",
759 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
760 | "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
761 | "dev": true
762 | },
763 | "@types/react": {
764 | "version": "18.0.25",
765 | "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.25.tgz",
766 | "integrity": "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==",
767 | "dev": true,
768 | "requires": {
769 | "@types/prop-types": "*",
770 | "@types/scheduler": "*",
771 | "csstype": "^3.0.2"
772 | }
773 | },
774 | "@types/scheduler": {
775 | "version": "0.16.2",
776 | "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
777 | "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==",
778 | "dev": true
779 | },
780 | "caniuse-lite": {
781 | "version": "1.0.30001431",
782 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz",
783 | "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ=="
784 | },
785 | "client-only": {
786 | "version": "0.0.1",
787 | "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
788 | "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
789 | },
790 | "cookie": {
791 | "version": "0.5.0",
792 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
793 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
794 | },
795 | "csstype": {
796 | "version": "3.1.1",
797 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
798 | "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==",
799 | "dev": true
800 | },
801 | "jose": {
802 | "version": "4.11.0",
803 | "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.0.tgz",
804 | "integrity": "sha512-wLe+lJHeG8Xt6uEubS4x0LVjS/3kXXu9dGoj9BNnlhYq7Kts0Pbb2pvv5KiI0yaKH/eaiR0LUOBhOVo9ktd05A=="
805 | },
806 | "js-tokens": {
807 | "version": "4.0.0",
808 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
809 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
810 | },
811 | "jwt-decode": {
812 | "version": "3.1.2",
813 | "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
814 | "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
815 | },
816 | "loose-envify": {
817 | "version": "1.4.0",
818 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
819 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
820 | "requires": {
821 | "js-tokens": "^3.0.0 || ^4.0.0"
822 | }
823 | },
824 | "lru-cache": {
825 | "version": "6.0.0",
826 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
827 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
828 | "requires": {
829 | "yallist": "^4.0.0"
830 | }
831 | },
832 | "nanoid": {
833 | "version": "3.3.4",
834 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
835 | "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
836 | },
837 | "next": {
838 | "version": "13.0.2",
839 | "resolved": "https://registry.npmjs.org/next/-/next-13.0.2.tgz",
840 | "integrity": "sha512-uQ5z5e4D9mOe8+upy6bQdYYjo/kk1v3jMW87kTy2TgAyAsEO+CkwRnMgyZ4JoHEnhPZLHwh7dk0XymRNLe1gFw==",
841 | "requires": {
842 | "@next/env": "13.0.2",
843 | "@next/swc-android-arm-eabi": "13.0.2",
844 | "@next/swc-android-arm64": "13.0.2",
845 | "@next/swc-darwin-arm64": "13.0.2",
846 | "@next/swc-darwin-x64": "13.0.2",
847 | "@next/swc-freebsd-x64": "13.0.2",
848 | "@next/swc-linux-arm-gnueabihf": "13.0.2",
849 | "@next/swc-linux-arm64-gnu": "13.0.2",
850 | "@next/swc-linux-arm64-musl": "13.0.2",
851 | "@next/swc-linux-x64-gnu": "13.0.2",
852 | "@next/swc-linux-x64-musl": "13.0.2",
853 | "@next/swc-win32-arm64-msvc": "13.0.2",
854 | "@next/swc-win32-ia32-msvc": "13.0.2",
855 | "@next/swc-win32-x64-msvc": "13.0.2",
856 | "@swc/helpers": "0.4.11",
857 | "caniuse-lite": "^1.0.30001406",
858 | "postcss": "8.4.14",
859 | "styled-jsx": "5.1.0",
860 | "use-sync-external-store": "1.2.0"
861 | }
862 | },
863 | "next-auth": {
864 | "version": "4.16.4",
865 | "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.16.4.tgz",
866 | "integrity": "sha512-KXW578+ER1u5RcWLwCHMdb/RIBIO6JM8r6xlf9RIPSKzkvDcX9FHiZfJS2vOq/SurHXPJZc4J3OS4IDJpF74Dw==",
867 | "requires": {
868 | "@babel/runtime": "^7.16.3",
869 | "@panva/hkdf": "^1.0.1",
870 | "cookie": "^0.5.0",
871 | "jose": "^4.9.3",
872 | "oauth": "^0.9.15",
873 | "openid-client": "^5.1.0",
874 | "preact": "^10.6.3",
875 | "preact-render-to-string": "^5.1.19",
876 | "uuid": "^8.3.2"
877 | }
878 | },
879 | "nodemailer": {
880 | "version": "6.8.0",
881 | "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.8.0.tgz",
882 | "integrity": "sha512-EjYvSmHzekz6VNkNd12aUqAco+bOkRe3Of5jVhltqKhEsjw/y0PYPJfp83+s9Wzh1dspYAkUW/YNQ350NATbSQ=="
883 | },
884 | "oauth": {
885 | "version": "0.9.15",
886 | "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
887 | "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
888 | },
889 | "object-hash": {
890 | "version": "2.2.0",
891 | "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
892 | "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw=="
893 | },
894 | "oidc-token-hash": {
895 | "version": "5.0.1",
896 | "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz",
897 | "integrity": "sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ=="
898 | },
899 | "openid-client": {
900 | "version": "5.3.0",
901 | "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.3.0.tgz",
902 | "integrity": "sha512-SykPCeZBZ/SxiBH5AWynvFUIDX3//2pgwc/3265alUmGHeCN03+X8uP+pHOVnCXCKfX/XOhO90qttAQ76XcGxA==",
903 | "requires": {
904 | "jose": "^4.10.0",
905 | "lru-cache": "^6.0.0",
906 | "object-hash": "^2.0.1",
907 | "oidc-token-hash": "^5.0.1"
908 | }
909 | },
910 | "picocolors": {
911 | "version": "1.0.0",
912 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
913 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
914 | },
915 | "postcss": {
916 | "version": "8.4.14",
917 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
918 | "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
919 | "requires": {
920 | "nanoid": "^3.3.4",
921 | "picocolors": "^1.0.0",
922 | "source-map-js": "^1.0.2"
923 | }
924 | },
925 | "preact": {
926 | "version": "10.11.2",
927 | "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.2.tgz",
928 | "integrity": "sha512-skAwGDFmgxhq1DCBHke/9e12ewkhc7WYwjuhHB8HHS8zkdtITXLRmUMTeol2ldxvLwYtwbFeifZ9uDDWuyL4Iw=="
929 | },
930 | "preact-render-to-string": {
931 | "version": "5.2.6",
932 | "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz",
933 | "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==",
934 | "requires": {
935 | "pretty-format": "^3.8.0"
936 | }
937 | },
938 | "pretty-format": {
939 | "version": "3.8.0",
940 | "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
941 | "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="
942 | },
943 | "react": {
944 | "version": "18.2.0",
945 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
946 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
947 | "requires": {
948 | "loose-envify": "^1.1.0"
949 | }
950 | },
951 | "react-dom": {
952 | "version": "18.2.0",
953 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
954 | "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
955 | "requires": {
956 | "loose-envify": "^1.1.0",
957 | "scheduler": "^0.23.0"
958 | }
959 | },
960 | "regenerator-runtime": {
961 | "version": "0.13.10",
962 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz",
963 | "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw=="
964 | },
965 | "scheduler": {
966 | "version": "0.23.0",
967 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
968 | "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
969 | "requires": {
970 | "loose-envify": "^1.1.0"
971 | }
972 | },
973 | "source-map-js": {
974 | "version": "1.0.2",
975 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
976 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
977 | },
978 | "styled-jsx": {
979 | "version": "5.1.0",
980 | "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.0.tgz",
981 | "integrity": "sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ==",
982 | "requires": {
983 | "client-only": "0.0.1"
984 | }
985 | },
986 | "tslib": {
987 | "version": "2.4.1",
988 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
989 | "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
990 | },
991 | "typescript": {
992 | "version": "4.8.4",
993 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
994 | "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
995 | "dev": true
996 | },
997 | "use-sync-external-store": {
998 | "version": "1.2.0",
999 | "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
1000 | "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
1001 | "requires": {}
1002 | },
1003 | "uuid": {
1004 | "version": "8.3.2",
1005 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
1006 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
1007 | },
1008 | "yallist": {
1009 | "version": "4.0.0",
1010 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
1011 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
1012 | }
1013 | }
1014 | }
1015 |
--------------------------------------------------------------------------------
/nextjs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "description": "An example project for NextAuth.js with Next.js",
4 | "repository": "https://github.com/nextauthjs/next-auth-example.git",
5 | "bugs": {
6 | "url": "https://github.com/nextauthjs/next-auth/issues"
7 | },
8 | "homepage": "https://next-auth-example.vercel.app",
9 | "scripts": {
10 | "dev": "next",
11 | "build": "next build",
12 | "start": "next start"
13 | },
14 | "author": "Iain Collins ",
15 | "contributors": [
16 | "Balázs Orbán ",
17 | "Nico Domino ",
18 | "Lluis Agusti "
19 | ],
20 | "dependencies": {
21 | "jwt-decode": "^3.1.2",
22 | "next": "latest",
23 | "next-auth": "latest",
24 | "nodemailer": "^6",
25 | "react": "^18.2.0",
26 | "react-dom": "^18.2.0"
27 | },
28 | "devDependencies": {
29 | "@types/node": "^17",
30 | "@types/react": "^18.0.15",
31 | "typescript": "^4"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/nextjs/pages/_app.js:
--------------------------------------------------------------------------------
1 | import { SessionProvider } from "next-auth/react";
2 | import "./styles.css";
3 |
4 | // Use of the is mandatory to allow components that call
5 | // `useSession()` anywhere in your application to access the `session` object.
6 | export default function App({
7 | Component,
8 | pageProps: { session, ...pageProps },
9 | }) {
10 | return (
11 |
12 |
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/nextjs/pages/admin.js:
--------------------------------------------------------------------------------
1 | import Layout from "../components/layout"
2 |
3 | export default function Page() {
4 | return (
5 |
6 | This page is protected by Middleware
7 | Only admin users can see this page.
8 |
9 | To learn more about the NextAuth middleware see
10 |
11 | the docs
12 |
13 | .
14 |
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/nextjs/pages/api-example.js:
--------------------------------------------------------------------------------
1 | import Layout from "../components/layout"
2 |
3 | export default function ApiExamplePage() {
4 | return (
5 |
6 | API Example
7 | The examples below show responses from the example API endpoints.
8 |
9 | You must be signed in to see responses.
10 |
11 | Session
12 | /api/examples/session
13 |
14 | JSON Web Token
15 | /api/examples/jwt
16 |
17 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/nextjs/pages/api/auth/[...nextauth].js:
--------------------------------------------------------------------------------
1 | import NextAuth from "next-auth";
2 | import jwtDecode from "jwt-decode";
3 | import CredentialsProvider from "next-auth/providers/credentials";
4 |
5 | async function refreshAccessToken(token) {
6 | try {
7 | const response = await fetch("http://127.0.0.1:8000/api/token/refresh/", {
8 | method: "POST",
9 | body: JSON.stringify({ refresh: token.refresh }),
10 | headers: { "Content-Type": "application/json" },
11 | });
12 | const refreshedToken = await res.json();
13 | if (response.status !== 200) throw refreshedToken;
14 | const { exp } = jwtDecode(refreshedToken.access);
15 | return {
16 | ...token,
17 | ...refreshedToken,
18 | exp,
19 | };
20 | } catch (error) {
21 | return {
22 | ...token,
23 | error: "RefreshAccessTokenError",
24 | };
25 | }
26 | }
27 |
28 | // For more information on each option (and a full list of options) go to
29 | // https://next-auth.js.org/configuration/options
30 | export const authOptions = {
31 | // https://next-auth.js.org/configuration/providers/oauth
32 | providers: [
33 | CredentialsProvider({
34 | // The name to display on the sign in form (e.g. 'Sign in with...')
35 | name: "Django Rest Framework",
36 | // The credentials is used to generate a suitable form on the sign in page.
37 | // You can specify whatever fields you are expecting to be submitted.
38 | // e.g. domain, username, password, 2FA token, etc.
39 | credentials: {
40 | username: {
41 | label: "Username",
42 | type: "username",
43 | placeholder: "username",
44 | },
45 | password: { label: "Password", type: "password" },
46 | },
47 |
48 | async authorize(credentials) {
49 | try {
50 | const response = await fetch("http://127.0.0.1:8000/api/token/", {
51 | method: "POST",
52 | body: JSON.stringify(credentials),
53 | headers: { "Content-Type": "application/json" },
54 | });
55 | const token = await response.json();
56 | if (response.status !== 200) throw token;
57 | console.log(jwtDecode(token.access));
58 | const { username, email, user_id, exp, is_superuser, is_staff } =
59 | jwtDecode(token.access);
60 | return {
61 | ...token,
62 | exp,
63 | user: {
64 | username,
65 | email,
66 | user_id,
67 | is_staff,
68 | is_superuser,
69 | },
70 | };
71 | } catch (error) {
72 | return null;
73 | }
74 | },
75 | }),
76 | ],
77 | theme: {
78 | colorScheme: "light",
79 | },
80 | callbacks: {
81 | async redirect({ url, baseUrl }) {
82 | return url.startsWith(baseUrl)
83 | ? Promise.resolve(url)
84 | : Promise.resolve(baseUrl);
85 | },
86 | async jwt({ token, user, account, profile, isNewUser }) {
87 | // initial signin
88 | if (account && user) {
89 | return user;
90 | }
91 |
92 | // Return previous token if the access token has not expired
93 | if (Date.now() < token.exp * 100) {
94 | return token;
95 | }
96 |
97 | // refresh token
98 | return refreshAccessToken(token);
99 | },
100 | async session({ session, user, token }) {
101 | session.user = token.user;
102 | session.access = token.access;
103 | session.refresh = token.refresh;
104 | session.exp = token.exp;
105 | return session;
106 | },
107 | },
108 | session: { strategy: "jwt" },
109 | };
110 |
111 | export default NextAuth(authOptions);
112 |
--------------------------------------------------------------------------------
/nextjs/pages/api/examples/jwt.js:
--------------------------------------------------------------------------------
1 | // This is an example of how to read a JSON Web Token from an API route
2 | import { getToken } from "next-auth/jwt";
3 |
4 | export default async function handler(req, res) {
5 | // If you don't have the NEXTAUTH_SECRET environment variable set,
6 | // you will have to pass your secret as `secret` to `getToken`
7 | const token = await getToken({ req });
8 | res.send(JSON.stringify(token, null, 2));
9 | }
10 |
--------------------------------------------------------------------------------
/nextjs/pages/api/examples/protected.js:
--------------------------------------------------------------------------------
1 | // This is an example of to protect an API route
2 | import { unstable_getServerSession } from "next-auth/next";
3 | import { authOptions } from "../auth/[...nextauth]";
4 |
5 | export default async function handler(req, res) {
6 | const session = await unstable_getServerSession(req, res, authOptions);
7 |
8 | if (session) {
9 | return res.send({
10 | content:
11 | "This is protected content. You can access this content because you are signed in.",
12 | });
13 | }
14 |
15 | res.send({
16 | error: "You must be signed in to view the protected content on this page.",
17 | });
18 | }
19 |
--------------------------------------------------------------------------------
/nextjs/pages/api/examples/session.js:
--------------------------------------------------------------------------------
1 | // This is an example of how to access a session from an API route
2 | import { unstable_getServerSession } from "next-auth";
3 | import { authOptions } from "../auth/[...nextauth]";
4 |
5 | export default async function handler(req, res) {
6 | const session = await unstable_getServerSession(req, res, authOptions);
7 | res.send(JSON.stringify(session, null, 2));
8 | }
9 |
--------------------------------------------------------------------------------
/nextjs/pages/client.js:
--------------------------------------------------------------------------------
1 | import Layout from "../components/layout"
2 |
3 | export default function ClientPage() {
4 | return (
5 |
6 | Client Side Rendering
7 |
8 | This page uses the useSession() React Hook in the{" "}
9 | <Header/> component.
10 |
11 |
12 | The useSession() React Hook is easy to use and allows
13 | pages to render very quickly.
14 |
15 |
16 | The advantage of this approach is that session state is shared between
17 | pages by using the Provider in _app.js {" "}
18 | so that navigation between pages using useSession() is
19 | very fast.
20 |
21 |
22 | The disadvantage of useSession() is that it requires
23 | client side JavaScript.
24 |
25 |
26 | )
27 | }
28 |
--------------------------------------------------------------------------------
/nextjs/pages/index.js:
--------------------------------------------------------------------------------
1 | import Layout from "../components/layout"
2 |
3 | export default function IndexPage() {
4 | return (
5 |
6 | NextAuth.js Example
7 |
8 | This is an example site to demonstrate how to use{" "}
9 | NextAuth.js for authentication.
10 |
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/nextjs/pages/me.js:
--------------------------------------------------------------------------------
1 | import { useSession } from "next-auth/react"
2 | import Layout from "../components/layout"
3 |
4 | export default function MePage() {
5 | const { data } = useSession()
6 |
7 | return (
8 |
9 | {JSON.stringify(data, null, 2)}
10 |
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/nextjs/pages/policy.js:
--------------------------------------------------------------------------------
1 | import Layout from "../components/layout"
2 |
3 | export default function PolicyPage() {
4 | return (
5 |
6 |
7 | This is an example site to demonstrate how to use{" "}
8 | NextAuth.js for authentication.
9 |
10 | Terms of Service
11 |
12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
13 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
15 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
16 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
17 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
18 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 |
20 | Privacy Policy
21 |
22 | This site uses JSON Web Tokens and an in-memory database which resets
23 | every ~2 hours.
24 |
25 |
26 | Data provided to this site is exclusively used to support signing in and
27 | is not passed to any third party services, other than via SMTP or OAuth
28 | for the purposes of authentication.
29 |
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/nextjs/pages/protected.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react"
2 | import { useSession } from "next-auth/react"
3 | import Layout from "../components/layout"
4 | import AccessDenied from "../components/access-denied"
5 |
6 | export default function ProtectedPage() {
7 | const { data: session } = useSession()
8 | const [content, setContent] = useState()
9 |
10 | // Fetch content from protected route
11 | useEffect(() => {
12 | const fetchData = async () => {
13 | const res = await fetch("/api/examples/protected")
14 | const json = await res.json()
15 | if (json.content) {
16 | setContent(json.content)
17 | }
18 | }
19 | fetchData()
20 | }, [session])
21 |
22 |
23 | // If no session exists, display access denied message
24 | if (!session) {
25 | return (
26 |
27 |
28 |
29 | )
30 | }
31 |
32 | // If session exists, display content
33 | return (
34 |
35 | Protected Page
36 |
37 | {content ?? "\u00a0"}
38 |
39 |
40 | )
41 | }
42 |
--------------------------------------------------------------------------------
/nextjs/pages/server.js:
--------------------------------------------------------------------------------
1 | import { unstable_getServerSession } from "next-auth/next";
2 | import { authOptions } from "./api/auth/[...nextauth]";
3 | import Layout from "../components/layout";
4 |
5 | export default function ServerSidePage({ session }) {
6 | // As this page uses Server Side Rendering, the `session` will be already
7 | // populated on render without needing to go through a loading stage.
8 | return (
9 |
10 | Server Side Rendering
11 |
12 | This page uses the unstable_getServerSession() method
13 | in getServerSideProps() .
14 |
15 |
16 | Using unstable_getServerSession() in{" "}
17 | getServerSideProps() is the recommended approach if you
18 | need to support Server Side Rendering with authentication.
19 |
20 |
21 | The advantage of Server Side Rendering is this page does not require
22 | client side JavaScript.
23 |
24 |
25 | The disadvantage of Server Side Rendering is that this page is slower to
26 | render.
27 |
28 | {JSON.stringify(session, null, 2)}
29 |
30 | );
31 | }
32 |
33 | // Export the `session` prop to use sessions with Server Side Rendering
34 | export async function getServerSideProps(context) {
35 | return {
36 | props: {
37 | session: await unstable_getServerSession(
38 | context.req,
39 | context.res,
40 | authOptions
41 | ),
42 | },
43 | };
44 | }
45 |
--------------------------------------------------------------------------------
/nextjs/pages/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
3 | "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif,
4 | "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
5 | padding: 0 1rem 1rem 1rem;
6 | max-width: 680px;
7 | margin: 0 auto;
8 | background: #fff;
9 | color: #333;
10 | }
11 |
12 | li,
13 | p {
14 | line-height: 1.5rem;
15 | }
16 |
17 | a {
18 | font-weight: 500;
19 | }
20 |
21 | hr {
22 | border: 1px solid #ddd;
23 | }
24 |
25 | iframe {
26 | background: #ccc;
27 | border: 1px solid #ccc;
28 | height: 10rem;
29 | width: 100%;
30 | border-radius: 0.5rem;
31 | filter: invert(1);
32 | }
33 |
--------------------------------------------------------------------------------