Ответ
Я действительно рекомендую посмотреть примеры, чтобы увидеть, как Next JS предлагает справиться с этим. Ресурсы действительно хороши!
https://github.com/vercel/next.js/tree/master/examples
Например, вы можете использовать next-auth
, который является опцией авторизации с открытым исходным кодом.
Пример здесь. https://github.com/vercel/next.js/tree/master/examples/with-next-auth
// _app.js
import { Provider } from 'next-auth/client'
import '../styles.css'
const App = ({ Component, pageProps }) => {
const { session } = pageProps
return (
<Provider options={{ site: process.env.SITE }} session={session}>
<Component {...pageProps} />
</Provider>
)
}
export default App
// /pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
const options = {
site: process.env.VERCEL_URL,
providers: [
Providers.Email({
// SMTP connection string or nodemailer configuration object https://nodemailer.com/
server: process.env.EMAIL_SERVER,
// Email services often only allow sending email from a valid/verified address
from: process.env.EMAIL_FROM,
}),
// When configuring oAuth providers make sure you enabling requesting
// permission to get the users email address (required to sign in)
Providers.Google({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
}),
Providers.Facebook({
clientId: process.env.FACEBOOK_ID,
clientSecret: process.env.FACEBOOK_SECRET,
}),
Providers.Twitter({
clientId: process.env.TWITTER_ID,
clientSecret: process.env.TWITTER_SECRET,
}),
Providers.GitHub({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
}),
],
// The 'database' option should be a connection string or TypeORM
// configuration object https://typeorm.io/#/connection-options
//
// Notes:
// * You need to install an appropriate node_module for your database!
// * The email sign in provider requires a database but OAuth providers do not
database: process.env.DATABASE_URL,
session: {
// Use JSON Web Tokens for session instead of database sessions.
// This option can be used with or without a database for users/accounts.
// Note: `jwt` is automatically set to `true` if no database is specified.
// jwt: false,
// Seconds - How long until an idle session expires and is no longer valid.
// maxAge: 30 * 24 * 60 * 60, // 30 days
// Seconds - Throttle how frequently to write to database to extend a session.
// Use it to limit write operations. Set to 0 to always update the database.
// Note: This option is ignored if using JSON Web Tokens
// updateAge: 24 * 60 * 60, // 24 hours
// Easily add custom properties to response from `/api/auth/session`.
// Note: This should not return any sensitive information.
/*
get: async (session) => {
session.customSessionProperty = "ABC123"
return session
}
*/
},
// JSON Web Token options
jwt: {
// secret: 'my-secret-123', // Recommended (but auto-generated if not specified)
// Custom encode/decode functions for signing + encryption can be specified.
// if you want to override what is in the JWT or how it is signed.
// encode: async ({ secret, key, token, maxAge }) => {},
// decode: async ({ secret, key, token, maxAge }) => {},
// Easily add custom to the JWT. It is updated every time it is accessed.
// This is encrypted and signed by default and may contain sensitive information
// as long as a reasonable secret is defined.
/*
set: async (token) => {
token.customJwtProperty = "ABC123"
return token
}
*/
},
// Control which users / accounts can sign in
// You can use this option in conjunction with OAuth and JWT to control which
// accounts can sign in without having to use a database.
allowSignin: async (user, account) => {
// Return true if user / account is allowed to sign in.
// Return false to display an access denied message.
return true
},
// You can define custom pages to override the built-in pages
// The routes shown here are the default URLs that will be used.
pages: {
// signin: '/api/auth/signin', // Displays signin buttons
// signout: '/api/auth/signout', // Displays form with sign out button
// error: '/api/auth/error', // Error code passed in query string as ?error=
// verifyRequest: '/api/auth/verify-request', // Used for check email page
// newUser: null // If set, new users will be directed here on first sign in
},
// Additional options
// secret: 'abcdef123456789' // Recommended (but auto-generated if not specified)
// debug: true, // Use this option to enable debug messages in the console
}
const Auth = (req, res) => NextAuth(req, res, options)
export default Auth
Таким образом, приведенный выше вариант - это defo приложение, отображаемое на стороне сервера, поскольку мы используем пути / api для auth. Если вам нужно бессерверное решение, вам, возможно, придется вытащить все из пути / api в лямбда (AWS Lambda) + api шлюза (AWS Api Gateway). Все, что вам нужно, это настраиваемый хук, который подключается к этому API. Конечно, вы можете сделать это по-разному.
Вот еще один пример аутентификации с использованием firebase.
https://github.com/vercel/next.js/tree/master/examples/with-firebase-authentication
И еще один пример с использованием Паспорт. js
https://github.com/vercel/next.js/tree/master/examples/with-passport
Также вы спрашивали о поведении загрузки, и этот пример может вам помочь
https://github.com/vercel/next.js/tree/master/examples/with-loading
?
Мнение
Пользовательский компонент _app обычно является оболочкой верхнего уровня (не совсем верхний _document под это описание).
Реально я бы создал компонент входа на один шаг ниже _app. Обычно я добиваюсь этого шаблона в компоненте Layout или, как в приведенных выше примерах, используя путь api или служебную функцию.