mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
2 Commits
v3.3.0-can
...
v3.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bd9d87633 | ||
|
|
6af40e3fe2 |
@@ -1,8 +1,13 @@
|
||||
# Rename file to .env.local and populate values
|
||||
# Rename file to .env.local (or .env) and populate values
|
||||
# to be able to run the dev app
|
||||
|
||||
NEXTAUTH_URL=http://localhost:3000
|
||||
SECRET= Linux: `openssl rand -hex 32` or https://generate-secret.now.sh/32
|
||||
|
||||
# You can use `openssl rand -hex 32` or
|
||||
# https://generate-secret.now.sh/32 to generate a secret.
|
||||
# Note: Changing a secret may invalidate existing sessions
|
||||
# and/or verificaion tokens.
|
||||
SECRET=
|
||||
|
||||
AUTH0_ID=
|
||||
AUTH0_DOMAIN=
|
||||
@@ -12,4 +17,17 @@ GITHUB_ID=
|
||||
GITHUB_SECRET=
|
||||
|
||||
TWITTER_ID=
|
||||
TWITTER_SECRET=
|
||||
TWITTER_SECRET=
|
||||
|
||||
# Example configuration for a Gmail account (will need SMTP enabled)
|
||||
EMAIL_SERVER=smtps://user@gmail.com:password@smtp.gmail.com:465
|
||||
EMAIL_FROM=user@gmail.com
|
||||
|
||||
# You can use any of these as the "DATABASE_URL" for
|
||||
# databases started with Docker using `npm run db:start`.
|
||||
# Note: If using with Prisma adapter, you need to use a `.env`
|
||||
# file rather than a `.env.local` file to configure env vars.
|
||||
# Postgres: DATABASE_URL=postgres://nextauth:password@127.0.0.1:5432/nextauth?synchronize=true
|
||||
# MySQL: DATABASE_URL=mysql://nextauth:password@127.0.0.1:3306/nextauth?synchronize=true
|
||||
# MongoDB: DATABASE_URL=mongodb://nextauth:password@127.0.0.1:27017/nextauth?synchronize=true
|
||||
DATABASE_URL=
|
||||
1
.github/workflows/labeler.yml
vendored
1
.github/workflows/labeler.yml
vendored
@@ -9,4 +9,3 @@ jobs:
|
||||
- uses: actions/labeler@main
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
sync-labels: true
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -33,4 +33,7 @@ node_modules
|
||||
|
||||
# GitHub Actions runner
|
||||
/actions-runner
|
||||
/_work
|
||||
/_work
|
||||
|
||||
# Prisma migrations
|
||||
/prisma/migrations
|
||||
@@ -154,3 +154,4 @@ We're open to all community contributions! If you'd like to contribute in any wa
|
||||
## License
|
||||
|
||||
ISC
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ export default function Header () {
|
||||
className={styles.button}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
signOut()
|
||||
signOut({ redirect: false })
|
||||
}}
|
||||
>
|
||||
Sign out
|
||||
|
||||
53
package-lock.json
generated
53
package-lock.json
generated
@@ -2695,6 +2695,27 @@
|
||||
"resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz",
|
||||
"integrity": "sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw=="
|
||||
},
|
||||
"@prisma/client": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.16.1.tgz",
|
||||
"integrity": "sha512-g4zXwC9PRtlrad/CBu+lXHRhvkEz4QW9tDn7bJGwCVNeLi+gLzSbEHjo3xLZgI3+Jp+40flOzrJrYP0bkNCpdQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@prisma/engines-version": "2.16.1-1.8b74ad57aaf2cc6c155f382a18a8e3ba95aceb03"
|
||||
}
|
||||
},
|
||||
"@prisma/engines": {
|
||||
"version": "2.16.1-1.8b74ad57aaf2cc6c155f382a18a8e3ba95aceb03",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-2.16.1-1.8b74ad57aaf2cc6c155f382a18a8e3ba95aceb03.tgz",
|
||||
"integrity": "sha512-GZ1huP5KC6TPf9u8pYGFklUkGVTKFel6k4wL4iMr8AQ6MeSV4GDJX3lEtEJLb0ayj6je/hDEyQG9iMp/BysFYg==",
|
||||
"dev": true
|
||||
},
|
||||
"@prisma/engines-version": {
|
||||
"version": "2.16.1-1.8b74ad57aaf2cc6c155f382a18a8e3ba95aceb03",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-2.16.1-1.8b74ad57aaf2cc6c155f382a18a8e3ba95aceb03.tgz",
|
||||
"integrity": "sha512-BkqxSWOc9aNYXjtmRtaLy2fKIeJ3+NKimRL1gKWXMjtxhKS5E3wvyxwZamtfIpEaZELGAO3x5+gqwoR9kS2oZA==",
|
||||
"dev": true
|
||||
},
|
||||
"@semantic-release/commit-analyzer": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-8.0.1.tgz",
|
||||
@@ -15716,6 +15737,15 @@
|
||||
"integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=",
|
||||
"dev": true
|
||||
},
|
||||
"prisma": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-2.16.1.tgz",
|
||||
"integrity": "sha512-TniTihl4xwWY7Hy+1UUpZ6jxHyriRDUW4i7TChZNBZM88IG8kvR5cSX+/JY/lzWGMUR4ZDBzoIuNcdPx/7eWag==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@prisma/engines": "2.16.1-1.8b74ad57aaf2cc6c155f382a18a8e3ba95aceb03"
|
||||
}
|
||||
},
|
||||
"process": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
@@ -16016,6 +16046,29 @@
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-dom": {
|
||||
|
||||
@@ -64,12 +64,13 @@
|
||||
"mysql": "^2.18.1",
|
||||
"mssql": "^6.2.1",
|
||||
"pg": "^8.2.1",
|
||||
"@prisma/client": "^2.12.0"
|
||||
"@prisma/client": "^2.16.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.8.4",
|
||||
"@babel/core": "^7.9.6",
|
||||
"@babel/preset-env": "^7.9.6",
|
||||
"@prisma/client": "^2.16.1",
|
||||
"@semantic-release/commit-analyzer": "^8.0.1",
|
||||
"@semantic-release/github": "^7.2.0",
|
||||
"@semantic-release/npm": "7.0.8",
|
||||
@@ -89,6 +90,7 @@
|
||||
"pg": "^8.2.1",
|
||||
"postcss-cli": "^7.1.1",
|
||||
"postcss-nested": "^4.2.1",
|
||||
"prisma": "^2.16.1",
|
||||
"puppeteer": "^5.2.1",
|
||||
"puppeteer-extra": "^3.1.15",
|
||||
"puppeteer-extra-plugin-stealth": "^2.6.1",
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import NextAuth from 'next-auth'
|
||||
import Providers from 'next-auth/providers'
|
||||
|
||||
// import Adapters from 'next-auth/adapters'
|
||||
// import { PrismaClient } from '@prisma/client'
|
||||
// const prisma = new PrismaClient()
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
Providers.Email({
|
||||
server: process.env.EMAIL_SERVER,
|
||||
from: process.env.EMAIL_FROM
|
||||
}),
|
||||
Providers.GitHub({
|
||||
clientId: process.env.GITHUB_ID,
|
||||
clientSecret: process.env.GITHUB_SECRET
|
||||
@@ -39,5 +47,15 @@ export default NextAuth({
|
||||
encryption: true,
|
||||
secret: process.env.SECRET
|
||||
},
|
||||
debug: false
|
||||
debug: false,
|
||||
theme: 'auto'
|
||||
|
||||
// Default Database Adapter (TypeORM)
|
||||
// database: process.env.DATABASE_URL
|
||||
|
||||
// Prisma Database Adapter
|
||||
// To configure this app to use the schema in `prisma/schema.prisma` run:
|
||||
// npx prisma generate
|
||||
// npx prisma migrate dev --preview-feature
|
||||
// adapter: Adapters.Prisma.Adapter({ prisma })
|
||||
})
|
||||
|
||||
@@ -4,6 +4,6 @@ import jwt from 'next-auth/jwt'
|
||||
const secret = process.env.SECRET
|
||||
|
||||
export default async (req, res) => {
|
||||
const token = await jwt.getToken({ req, secret, encryption: true })
|
||||
const token = await jwt.getToken({ req, secret })
|
||||
res.send(JSON.stringify(token, null, 2))
|
||||
}
|
||||
|
||||
@@ -10,10 +10,6 @@ export default function Page () {
|
||||
}
|
||||
const response = await signIn('credentials', options)
|
||||
setResponse(response)
|
||||
if (response.ok) {
|
||||
window.alert('Manually refreshing to update session, if login was successful')
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
|
||||
const handleLogout = (options) => async () => {
|
||||
@@ -22,10 +18,6 @@ export default function Page () {
|
||||
}
|
||||
const response = await signOut(options)
|
||||
setResponse(response)
|
||||
if (response.ok) {
|
||||
window.alert('Manually refreshing to update session, if logout was successful')
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
|
||||
const [session] = useSession()
|
||||
|
||||
63
prisma/schema.prisma
Normal file
63
prisma/schema.prisma
Normal file
@@ -0,0 +1,63 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model Account {
|
||||
id Int @default(autoincrement()) @id
|
||||
compoundId String @unique @map(name: "compound_id")
|
||||
userId Int @map(name: "user_id")
|
||||
providerType String @map(name: "provider_type")
|
||||
providerId String @map(name: "provider_id")
|
||||
providerAccountId String @map(name: "provider_account_id")
|
||||
refreshToken String? @map(name: "refresh_token")
|
||||
accessToken String? @map(name: "access_token")
|
||||
accessTokenExpires DateTime? @map(name: "access_token_expires")
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @default(now()) @map(name: "updated_at")
|
||||
|
||||
@@index([providerAccountId], name: "providerAccountId")
|
||||
@@index([providerId], name: "providerId")
|
||||
@@index([userId], name: "userId")
|
||||
|
||||
@@map(name: "accounts")
|
||||
}
|
||||
|
||||
model Session {
|
||||
id Int @default(autoincrement()) @id
|
||||
userId Int @map(name: "user_id")
|
||||
expires DateTime
|
||||
sessionToken String @unique @map(name: "session_token")
|
||||
accessToken String @unique @map(name: "access_token")
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @default(now()) @map(name: "updated_at")
|
||||
|
||||
@@map(name: "sessions")
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @default(autoincrement()) @id
|
||||
name String?
|
||||
email String? @unique
|
||||
emailVerified DateTime? @map(name: "email_verified")
|
||||
image String?
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @default(now()) @map(name: "updated_at")
|
||||
|
||||
@@map(name: "users")
|
||||
}
|
||||
|
||||
model VerificationRequest {
|
||||
id Int @default(autoincrement()) @id
|
||||
identifier String
|
||||
token String @unique
|
||||
expires DateTime
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @default(now()) @map(name: "updated_at")
|
||||
|
||||
@@map(name: "verification_requests")
|
||||
}
|
||||
@@ -280,11 +280,15 @@ const Adapter = (config) => {
|
||||
// Hash token provided with secret before trying to match it with database
|
||||
// @TODO Use bcrypt instead of salted SHA-256 hash for token
|
||||
const hashedToken = createHash('sha256').update(`${token}${secret}`).digest('hex')
|
||||
const verificationRequest = await prisma[VerificationRequest].findUnique({ where: { token: hashedToken } })
|
||||
|
||||
const verificationRequest = await prisma[VerificationRequest].findFirst({
|
||||
where: {
|
||||
identifier,
|
||||
token: hashedToken
|
||||
}
|
||||
})
|
||||
if (verificationRequest && verificationRequest.expires && new Date() > verificationRequest.expires) {
|
||||
// Delete verification entry so it cannot be used again
|
||||
await prisma[VerificationRequest].delete({ where: { token: hashedToken } })
|
||||
await prisma[VerificationRequest].deleteMany({ where: { identifier, token: hashedToken } })
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -300,7 +304,7 @@ const Adapter = (config) => {
|
||||
try {
|
||||
// Delete verification entry so it cannot be used again
|
||||
const hashedToken = createHash('sha256').update(`${token}${secret}`).digest('hex')
|
||||
await prisma[VerificationRequest].delete({ where: { token: hashedToken } })
|
||||
await prisma[VerificationRequest].deleteMany({ where: { identifier, token: hashedToken } })
|
||||
} catch (error) {
|
||||
logger.error('DELETE_VERIFICATION_REQUEST_ERROR', error)
|
||||
return Promise.reject(new Error('DELETE_VERIFICATION_REQUEST_ERROR', error))
|
||||
|
||||
@@ -331,7 +331,7 @@ const Adapter = (typeOrmConfig, options = {}) => {
|
||||
|
||||
if (verificationRequest && verificationRequest.expires && new Date() > new Date(verificationRequest.expires)) {
|
||||
// Delete verification entry so it cannot be used again
|
||||
await manager.delete(VerificationRequest, { token: hashedToken })
|
||||
await manager.delete(VerificationRequest, { identifier, token: hashedToken })
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -347,7 +347,7 @@ const Adapter = (typeOrmConfig, options = {}) => {
|
||||
try {
|
||||
// Delete verification entry so it cannot be used again
|
||||
const hashedToken = createHash('sha256').update(`${token}${secret}`).digest('hex')
|
||||
await manager.delete(VerificationRequest, { token: hashedToken })
|
||||
await manager.delete(VerificationRequest, { identifier, token: hashedToken })
|
||||
} catch (error) {
|
||||
logger.error('DELETE_VERIFICATION_REQUEST_ERROR', error)
|
||||
return Promise.reject(new Error('DELETE_VERIFICATION_REQUEST_ERROR', error))
|
||||
|
||||
@@ -114,12 +114,10 @@ const setOptions = ({
|
||||
}
|
||||
|
||||
// Universal method (client + server)
|
||||
export const getSession = async ({ req, ctx, triggerEvent = true } = {}) => {
|
||||
// If passed 'appContext' via getInitialProps() in _app.js then get the req
|
||||
// object from ctx and use that for the req value to allow getSession() to
|
||||
// work seemlessly in getInitialProps() on server side pages *and* in _app.js.
|
||||
if (!req && ctx && ctx.req) { req = ctx.req }
|
||||
|
||||
// If passed 'appContext' via getInitialProps() in _app.js then get the req
|
||||
// object from ctx and use that for the req value to allow getSession() to
|
||||
// work seemlessly in getInitialProps() on server side pages *and* in _app.js.
|
||||
export async function getSession ({ ctx, req = ctx?.req, triggerEvent = true } = {}) {
|
||||
const baseUrl = _apiBaseUrl()
|
||||
const fetchOptions = req ? { headers: { cookie: req.headers.cookie } } : {}
|
||||
const session = await _fetchData(`${baseUrl}/session`, fetchOptions)
|
||||
@@ -130,12 +128,10 @@ export const getSession = async ({ req, ctx, triggerEvent = true } = {}) => {
|
||||
}
|
||||
|
||||
// Universal method (client + server)
|
||||
const getCsrfToken = async ({ req, ctx } = {}) => {
|
||||
// If passed 'appContext' via getInitialProps() in _app.js then get the req
|
||||
// object from ctx and use that for the req value to allow getCsrfToken() to
|
||||
// work seemlessly in getInitialProps() on server side pages *and* in _app.js.
|
||||
if (!req && ctx && ctx.req) { req = ctx.req }
|
||||
|
||||
// If passed 'appContext' via getInitialProps() in _app.js then get the req
|
||||
// object from ctx and use that for the req value to allow getCsrfToken() to
|
||||
// work seemlessly in getInitialProps() on server side pages *and* in _app.js.
|
||||
async function getCsrfToken ({ ctx, req = ctx?.req } = {}) {
|
||||
const baseUrl = _apiBaseUrl()
|
||||
const fetchOptions = req ? { headers: { cookie: req.headers.cookie } } : {}
|
||||
const data = await _fetchData(`${baseUrl}/csrf`, fetchOptions)
|
||||
@@ -287,10 +283,16 @@ export async function signIn (provider, options = {}, authorizationParams = {})
|
||||
}
|
||||
|
||||
const error = new URL(data.url).searchParams.get('error')
|
||||
|
||||
if (res.ok) {
|
||||
await __NEXTAUTH._getSession({ event: 'storage' })
|
||||
}
|
||||
|
||||
return {
|
||||
error,
|
||||
status: res.status,
|
||||
ok: res.ok
|
||||
ok: res.ok,
|
||||
url: error ? null : data.url
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,6 +328,8 @@ export async function signOut (options = {}) {
|
||||
return
|
||||
}
|
||||
|
||||
await __NEXTAUTH._getSession({ event: 'storage' })
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
--border-radius: .3rem;
|
||||
--color-error: #c94b4b;
|
||||
--color-info: #157efb;
|
||||
--color-info-text: #fff;
|
||||
}
|
||||
|
||||
.__next-auth-theme-auto,
|
||||
@@ -23,7 +24,6 @@
|
||||
--color-control-border: #555;
|
||||
--color-button-active-background: #060606;
|
||||
--color-button-active-border: #666;
|
||||
|
||||
--color-seperator: #444;
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ input[type] {
|
||||
font-size: 1rem;
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: inset 0 .1rem .2rem rgba(0, 0, 0, .2);
|
||||
color: var(--color-text);
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
@@ -202,13 +203,13 @@ a.site {
|
||||
font-weight: 500;
|
||||
border-radius: 0.3rem;
|
||||
background: var(--color-info);
|
||||
color: var(--color-text);
|
||||
|
||||
p {
|
||||
text-align: left;
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.2rem;
|
||||
color: var(--color-info-text);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
21
src/providers/eveonline.js
Normal file
21
src/providers/eveonline.js
Normal file
@@ -0,0 +1,21 @@
|
||||
export default (options) => {
|
||||
return {
|
||||
id: 'eveonline',
|
||||
name: 'EVE Online',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://login.eveonline.com/oauth/token',
|
||||
authorizationUrl: 'https://login.eveonline.com/oauth/authorize?response_type=code',
|
||||
profileUrl: 'https://login.eveonline.com/oauth/verify',
|
||||
profile: (profile) => {
|
||||
return {
|
||||
id: profile.CharacterID,
|
||||
name: profile.CharacterName,
|
||||
image: `https://image.eveonline.com/Character/${profile.CharacterID}_128.jpg`,
|
||||
email: null
|
||||
}
|
||||
},
|
||||
...options
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import Cognito from './cognito'
|
||||
import Credentials from './credentials'
|
||||
import Discord from './discord'
|
||||
import Email from './email'
|
||||
import EVEOnline from './eveonline'
|
||||
import Facebook from './facebook'
|
||||
import Foursquare from './foursquare'
|
||||
import FusionAuth from './fusionauth'
|
||||
@@ -46,6 +47,7 @@ export default {
|
||||
Credentials,
|
||||
Discord,
|
||||
Email,
|
||||
EVEOnline,
|
||||
Facebook,
|
||||
Foursquare,
|
||||
FusionAuth,
|
||||
|
||||
@@ -132,7 +132,7 @@ async function getOAuth2AccessToken (code, provider, codeVerifier) {
|
||||
headers.Authorization = 'Basic ' + Buffer.from((provider.clientId + ':' + provider.clientSecret)).toString('base64')
|
||||
}
|
||||
|
||||
if ((provider.id === 'okta' || provider.id === 'identity-server4') && !headers.Authorization) {
|
||||
if (provider.id === 'identity-server4' && !headers.Authorization) {
|
||||
headers.Authorization = `Bearer ${code}`
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ export default function error ({ baseUrl, basePath, error, res }) {
|
||||
message: (
|
||||
<div>
|
||||
<p>The sign in link is no longer valid.</p>
|
||||
<p>It may have be used already or it may have expired.</p>
|
||||
<p>It may have been used already or it may have expired.</p>
|
||||
</div>
|
||||
),
|
||||
signin: <p><a className='button' href={signinPageUrl}>Sign in</a></p>
|
||||
|
||||
@@ -44,7 +44,7 @@ export default async function signin (req, res) {
|
||||
|
||||
// Check if user is allowed to sign in
|
||||
try {
|
||||
const signInCallbackResponse = await callbacks.signIn(profile, account, { email })
|
||||
const signInCallbackResponse = await callbacks.signIn(profile, account, { email, verificationRequest: true })
|
||||
if (signInCallbackResponse === false) {
|
||||
return res.redirect(`${baseUrl}${basePath}/error?error=AccessDenied`)
|
||||
} else if (typeof signInCallbackResponse === 'string') {
|
||||
|
||||
@@ -118,3 +118,7 @@ You can also use the `signIn()` function which will handle obtaining the CSRF to
|
||||
```js
|
||||
signIn('credentials', { username: 'jsmith', password: '1234' })
|
||||
```
|
||||
|
||||
:::tip
|
||||
Remember to put any custom pages in a folder outside **/pages/api** which is reserved for API code. As per the examples above, a location convention suggestion is `pages/auth/...`.
|
||||
:::
|
||||
@@ -133,7 +133,7 @@ The `getProviders()` method returns the list of providers currently configured f
|
||||
|
||||
It calls `/api/auth/providers` and returns a list of the currently configured authentication providers.
|
||||
|
||||
It can be use useful if you are creating a dynamic custom sign in page.
|
||||
It can be useful if you are creating a dynamic custom sign in page.
|
||||
|
||||
---
|
||||
|
||||
@@ -210,6 +210,35 @@ e.g.
|
||||
|
||||
The URL must be considered valid by the [redirect callback handler](/configuration/callbacks#redirect). By default it requires the URL to be an absolute URL at the same hostname, or else it will redirect to the homepage. You can define your own redirect callback to allow other URLs, including supporting relative URLs.
|
||||
|
||||
#### Using the redirect: false option
|
||||
|
||||
When you use the `credentials` provider, you might not want the user to redirect to an error page if an error occurs, so you can handle any errors (like wrong credentials given by the user) on the same page. For that, you can pass `redirect: false` in the second parameter object. `signIn` then will return a Promise, that resolves to the following:
|
||||
|
||||
```ts
|
||||
{
|
||||
/**
|
||||
* Will be different error codes,
|
||||
* depending on the type of error.
|
||||
*/
|
||||
error: string | undefined
|
||||
/**
|
||||
* HTTP status code,
|
||||
* hints the kind of error that happened.
|
||||
*/
|
||||
status: number
|
||||
/**
|
||||
* `true` if the signin was successful
|
||||
*/
|
||||
ok: boolean
|
||||
/**
|
||||
* `null` if there was an error,
|
||||
* otherwise the url the user
|
||||
* should have been redirected to.
|
||||
*/
|
||||
url: string | null
|
||||
}
|
||||
```
|
||||
|
||||
#### Additional params
|
||||
|
||||
It is also possible to pass additional parameters to the `/authorize` endpoint through the third argument of `signIn()`.
|
||||
@@ -256,6 +285,16 @@ e.g. `signOut({ callbackUrl: 'http://localhost:3000/foo' })`
|
||||
|
||||
The URL must be considered valid by the [redirect callback handler](/configuration/callbacks#redirect). By default this means it must be an absolute URL at the same hostname (or else it will default to the homepage); you can define your own custom redirect callback to allow other URLs, including supporting relative URLs.
|
||||
|
||||
#### Using the redirect: false option
|
||||
|
||||
If you pass `redirect: false` to `signOut`, the page will not reload. The session will be deleted, and the `useSession` hook is notified, so any indication about the user will be shown as logged out automatically. It can give a very nice experience for the user.
|
||||
|
||||
:::tip
|
||||
If you need to redirect to another page but you want to avoid a page reload, you can try:
|
||||
`const data = await signOut({redirect: false, callbackUrl: "/foo"})`
|
||||
where `data.url` is the validated url you can redirect the user to without any flicker by using Next.js's `useRouter().push(data.url)`
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Provider
|
||||
|
||||
@@ -9,8 +9,6 @@ https://developer.atlassian.com/cloud/jira/platform/oauth-2-authorization-code-g
|
||||
|
||||
## Example
|
||||
|
||||
For Jira Platform API access:
|
||||
|
||||
```js
|
||||
import Providers from `next-auth/providers`
|
||||
...
|
||||
@@ -18,8 +16,7 @@ providers: [
|
||||
Providers.Atlassian({
|
||||
clientId: process.env.ATLASSIAN_CLIENT_ID,
|
||||
clientSecret: process.env.ATLASSIAN_CLIENT_SECRET,
|
||||
scope:
|
||||
'write:jira-work read:jira-work read:jira-user offline_access read:me',
|
||||
scope: 'write:jira-work read:jira-work read:jira-user offline_access read:me'
|
||||
})
|
||||
]
|
||||
...
|
||||
@@ -33,7 +30,7 @@ providers: [
|
||||
An app can be created at https://developer.atlassian.com/apps/
|
||||
:::
|
||||
|
||||
Under "Apis and features" side menu, configure the following for the "OAuth 2.0 (3LO)"
|
||||
Under "Apis and features" in the side menu, configure the following for "OAuth 2.0 (3LO)":
|
||||
|
||||
- Redirect URL
|
||||
- http://localhost:3000/api/auth/callback/atlassian
|
||||
|
||||
@@ -12,7 +12,24 @@ https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-c
|
||||
https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-tenant
|
||||
|
||||
## Example
|
||||
- In https://portal.azure.com/ -> Azure Active Directory create a new App Registration.
|
||||
- Make sure to remember / copy
|
||||
- Application (client) ID
|
||||
- Directory (tenant) ID
|
||||
- When asked for a redirection URL, use http://localhost:3000/api/auth/callback/azure-ad-b2c
|
||||
- Create a new secret and remember / copy its value immediately, it will disappear.
|
||||
|
||||
In `.env.local` create the follwing entries:
|
||||
|
||||
```
|
||||
AZURE_CLIENT_ID=<copy Application (client) ID here>
|
||||
AZURE_CLIENT_SECRET=<copy generated secret value here>
|
||||
AZURE_TENANT_NAME=<copy the name of the tenant here>
|
||||
AZURE_TENANT_ID=<copy the tenant id here>
|
||||
```
|
||||
|
||||
In `pages/api/auth/[...nextauth].js` find or add the AZURE entries:
|
||||
|
||||
```js
|
||||
import Providers from 'next-auth/providers';
|
||||
...
|
||||
@@ -25,4 +42,5 @@ providers: [
|
||||
}),
|
||||
]
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
57
www/docs/providers/eveonline.md
Normal file
57
www/docs/providers/eveonline.md
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
id: eveonline
|
||||
title: EVE Online
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
https://developers.eveonline.com/blog/article/sso-to-authenticated-calls
|
||||
|
||||
## Configuration
|
||||
|
||||
https://developers.eveonline.com/
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
import Providers from `next-auth/providers`
|
||||
...
|
||||
providers: [
|
||||
Providers.EVEOnline({
|
||||
clientId: process.env.EVE_CLIENT_ID,
|
||||
clientSecret: process.env.EVE_CLIENT_SECRET
|
||||
})
|
||||
]
|
||||
...
|
||||
```
|
||||
|
||||
:::tip When creating your application, make sure to select `Authentication Only` as the connection type.
|
||||
|
||||
:::tip If using JWT for the session, you can add the `CharacterID` to the JWT token and session. Example:
|
||||
|
||||
```js
|
||||
...
|
||||
options: {
|
||||
jwt: {
|
||||
secret: process.env.JWT_SECRET,
|
||||
},
|
||||
callbacks: {
|
||||
jwt: async (token, user, account, profile, isNewUser) => {
|
||||
if (profile) {
|
||||
token = {
|
||||
...token,
|
||||
id: profile.CharacterID,
|
||||
}
|
||||
}
|
||||
return token;
|
||||
},
|
||||
session: async (session, token) => {
|
||||
if (token) {
|
||||
session.user.id = token.id;
|
||||
}
|
||||
return session;
|
||||
}
|
||||
}
|
||||
}
|
||||
...
|
||||
```
|
||||
@@ -55,13 +55,13 @@ However, it should not be enabled against production databases as it may cause d
|
||||
|
||||
## Prisma Adapter
|
||||
|
||||
You can also use NextAuth.js with [Prisma](https://www.prisma.io/docs/).
|
||||
You can also use NextAuth.js with the experimental adapter for [Prisma 2](https://www.prisma.io/docs/).
|
||||
|
||||
To use this adapter, you need to install Prisma Client and Prisma CLI:
|
||||
|
||||
```
|
||||
npm i @prisma/client
|
||||
npm add -D @prisma/cli
|
||||
npm install @prisma/client
|
||||
npm install prisma --save-dev
|
||||
```
|
||||
|
||||
Configure your NextAuth.js to use the Prisma adapter:
|
||||
@@ -91,7 +91,7 @@ While Prisma includes an experimental feature in the migration command that is a
|
||||
|
||||
### Prisma Schema
|
||||
|
||||
Create a `schema.prisma` file similar to this one:
|
||||
Create a schema file in `prisma/schema.prisma` similar to this one:
|
||||
|
||||
```json title="schema.prisma"
|
||||
generator client {
|
||||
@@ -179,12 +179,22 @@ datasource db {
|
||||
|
||||
### Generate Client
|
||||
|
||||
Once you have saved your schema, you can run the Prisma CLI to generate the Prisma Client:
|
||||
Once you have saved your schema, use the Prisma CLI to generate the Prisma Client:
|
||||
|
||||
```
|
||||
npx @prisma/cli generate
|
||||
npx prisma generate
|
||||
```
|
||||
|
||||
To configure you database to use the new schema (i.e. create tables and columns) use the `primsa migrate` command:
|
||||
|
||||
```
|
||||
npx prisma migrate dev --preview-feature
|
||||
```
|
||||
|
||||
To generate a schema in this way with the above example code, you will need to specify your datbase connection string in the environment variable `DATABASE_URL`. You can do this by setting it in a `.env` file at the root of your project.
|
||||
|
||||
As this feature is experimental in Prisma, it is behind a feature flag. You should check your database schema manually after using this option. See the [Prisma documentation](https://www.prisma.io/docs/) for information on how to use `prisma migrate`.
|
||||
|
||||
### Custom Models
|
||||
|
||||
You can add properties to the schema and map them to any database column names you wish, but you should not change the base properties or types defined in the example schema.
|
||||
|
||||
@@ -68,7 +68,7 @@ module.exports = {
|
||||
to: '/contributors'
|
||||
},
|
||||
{
|
||||
label: 'Canary docs',
|
||||
label: 'Canary documentation',
|
||||
to: 'https://next-auth-git-canary.nextauthjs.vercel.app/'
|
||||
}
|
||||
]
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"credentials": "Credentials",
|
||||
"discord": "Discord",
|
||||
"email": "Email",
|
||||
"eveonline": "EVE Online",
|
||||
"facebook": "Facebook",
|
||||
"foursquare": "Foursquare",
|
||||
"fusionauth": "FusionAuth",
|
||||
|
||||
Reference in New Issue
Block a user