Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: /tokens endpoint #951

Closed
wants to merge 132 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
132 commits
Select commit Hold shift + click to select a range
e031591
feat: simplify NextAuth instantiation (#911)
balazsorban44 Dec 5, 2020
7b6fd81
feat: allow react 17 as a peer dependency (#819)
lukel97 Dec 5, 2020
d155720
docs: update for Now to Vercel (#847)
perkinsjr Dec 5, 2020
c8de34d
docs: fix discord example code (#850)
joshkmartinez Dec 5, 2020
2ee460d
docs: fix typo in callbacks.md (#815)
Pauldic Dec 5, 2020
0327b90
fix: update nodemailer version in response to CVE. (#860)
josh-padnick Dec 5, 2020
0b73437
fix: ensure Images are produced for discord (#734)
daggy1234 Dec 5, 2020
766874d
fix: update Okta routes (#763)
ohheyalanray Dec 5, 2020
d242d72
fix(provider): handle no profile image for Spotify (#914)
balazsorban44 Dec 5, 2020
6088a05
Merge main into canary (#917)
balazsorban44 Dec 6, 2020
93f4dc0
docs: Update default ports for support Databases (#839)
manishrc Dec 6, 2020
06a160a
Fix for Reddit Authentication (#866)
afoyer Dec 6, 2020
47b8788
WIP: Update Docusaurus + Site dependencies (#802)
ndom91 Dec 6, 2020
1aea187
Include callbackUrl in newUser page (#790)
ramiel Dec 6, 2020
4635113
add(db): Add support for Fauna DB (#708)
Joev- Dec 6, 2020
12d7856
feat(provider): add netlify (#555)
styxlab Dec 6, 2020
8c56e13
Bump next from 9.5.3 to 9.5.4 in /test/docker/app (#759)
dependabot[bot] Dec 6, 2020
3abb0c8
feat(provider): Add Bungie (#589)
RobertCraigie Dec 6, 2020
831c59d
feat: add foursquare (#584)
joe-bell Dec 6, 2020
dde908b
feat(provider): Add Azure Active Directory B2C (#921)
vladimir-e Dec 6, 2020
9dbd372
update(provider): Update Slack provider to use V2 OAuth endpoints (#895)
cathykc Dec 6, 2020
ddaa830
refactor(db): update Prisma calls to support 2.12+ (#881)
kripod Dec 6, 2020
7a4534a
chore(dep): Bump highlight.js from 9.18.1 to 9.18.5 (#880)
dependabot[bot] Dec 7, 2020
5fcf80c
chore: disallow issues without template
balazsorban44 Dec 7, 2020
3c3a4d2
chore: add note about conveting questions to discussions
balazsorban44 Dec 7, 2020
7ce37c7
chore: create PULL_REQUEST_TEMPLATE.md
balazsorban44 Dec 7, 2020
bd86e7c
chore: reword PR template
balazsorban44 Dec 7, 2020
19f2664
feat: Store user ID in sub claim of default JWT (#784)
lukel97 Dec 8, 2020
be159b1
docs: fix incorrect references in cypress docs (#932)
haldunanil Dec 9, 2020
f87e8b3
feat(ts): add type declaration files
balazsorban44 Dec 11, 2020
5811248
feat: save access and id tokens in individual cookies
balazsorban44 Dec 11, 2020
b98fb61
feat: add token endpoint (WIP)
balazsorban44 Dec 11, 2020
16849d1
refactor: renamce refreshedToken -> newTokens
balazsorban44 Dec 11, 2020
4f89d74
feat: Display error if no [...nextauth].js found (#678)
jaknas Dec 13, 2020
8827950
chore(deps): Bump ini from 1.3.5 to 1.3.8 in /www (#953)
dependabot[bot] Dec 13, 2020
d03504c
docs: fix typo Adapater -> Adapter (#960)
imgregduh Dec 16, 2020
eb828d4
docs: We have twice the word "side" (#964)
paulkabore Dec 17, 2020
28e2afb
docs: Correcting a typo. "available" Line 70 (#965)
paulkabore Dec 17, 2020
28ce71d
chore: hide comments from pull request template
balazsorban44 Dec 17, 2020
652ac7d
Update README.md
ndom91 Dec 21, 2020
1a315fe
feat: add strava provider (#986)
paulkennethkent Dec 23, 2020
a490686
Update README.md
ndom91 Dec 25, 2020
66ec439
Update README.md
ndom91 Dec 26, 2020
aa4439e
feat: add semantic-release (#920)
balazsorban44 Dec 29, 2020
15196ee
chore(release): change semantic-release/git to semantic-release/github
Dec 29, 2020
2eb17cb
docs(database): add mssql indexes in docs, fix typos (#925)
vidz1979 Dec 29, 2020
1728f50
chore(release): delete old workflow
balazsorban44 Dec 29, 2020
c31cbbc
chore(release): trigger release on docs type
balazsorban44 Dec 29, 2020
ee398d1
fix: treat user.id as optional param (#1010)
balazsorban44 Dec 30, 2020
abcf845
fix(adapter): use findOne for typeorm (#1014)
matsune Dec 30, 2020
9b579b5
Change image to text from varchar (#777)
Xodarap Dec 31, 2020
b9862b8
feat(db): make Fauna DB collections & indexes configurable (#968)
n44ps Dec 31, 2020
bf7efbc
docs: Remove unnecessary promises (#915)
balazsorban44 Dec 31, 2020
82d16e6
feat: allow to return string in signIn callback (#1019)
balazsorban44 Dec 31, 2020
c684336
docs: small update to sign in/out examples (#1016)
melanieseltzer Jan 1, 2021
7fa4275
docs: update contributing information [skip release] (#1011)
balazsorban44 Jan 1, 2021
ca06976
docs: fix typos in CONTRIBUTING.md [skip release]
balazsorban44 Jan 1, 2021
f2ad693
refactor: code base improvements (#959)
balazsorban44 Jan 1, 2021
a8362ec
feat(provider): Add Mail.ru OAuth Service Provider and Callback snipp…
nyedidikeke Jan 1, 2021
c8e76b4
feat: forward id_token to jwt and signIn callbacks (#1024)
balazsorban44 Jan 1, 2021
91e26ca
chore: add auto labeling to PRs [skip release] (#1025)
balazsorban44 Jan 1, 2021
e17acb6
chore: rename labeler.yaml to labeler.yml [skip release]
balazsorban44 Jan 2, 2021
4d89b27
fix: miscellaneous bugfixes (#1030)
balazsorban44 Jan 2, 2021
0380edf
fix: don't chain on res.end on non-chainable res methods (#1031)
balazsorban44 Jan 2, 2021
f1f1449
docs: add powered by vercel logo [skip release]
balazsorban44 Jan 3, 2021
b5c4e91
chore: run tests on canary [skip release]
balazsorban44 Jan 3, 2021
b5384e7
docs: misc improvements [skip release] (#1043)
balazsorban44 Jan 4, 2021
20f40d0
refactor: code base improvements 2 (#1045)
balazsorban44 Jan 4, 2021
f7ff4c9
fix: trigger release
balazsorban44 Jan 4, 2021
7a1d712
fix: use authorizationUrl correctly
balazsorban44 Jan 4, 2021
3fcdd22
feat(provider): reduce user facing API (#1023)
balazsorban44 Jan 5, 2021
65504d6
fix: remove async from NextAuth default handler
balazsorban44 Jan 6, 2021
fb8ec8a
feat(provider): add vk.com provider (#1060)
boreyko1 Jan 9, 2021
44ffd55
refactor: code base improvements 3 (#1072)
balazsorban44 Jan 10, 2021
173df76
feat: improve package development experience (#1064)
balazsorban44 Jan 10, 2021
e504044
fix: pass csrfToken to signin renderer
balazsorban44 Jan 10, 2021
416d92c
feat: replace blur/focus event to visibility API for getSession (#1081)
lnikell Jan 10, 2021
e7e8e0f
docs: clarify .env usage in CONTRIBUTING.md [skip release] (#1085)
balazsorban44 Jan 11, 2021
f05644d
docs: improve FAQ docs [skip release]
balazsorban44 Jan 11, 2021
2e4832c
chore: update caiuse-lite db
balazsorban44 Jan 11, 2021
354d6c3
docs: update some urls in the docs [skip release]
balazsorban44 Jan 11, 2021
1838e43
feat(pages): add dark theme support (#1088)
balazsorban44 Jan 11, 2021
ad791ea
feat(provider): add LINE provider (#1091)
bebax Jan 12, 2021
54a28b5
refactor: be explicit about path in jsonconfig [skip release]
balazsorban44 Jan 12, 2021
47621b5
refactor: show signin page in dev app [skip release]
balazsorban44 Jan 12, 2021
72b6050
fix: export getSession [skip release]
balazsorban44 Jan 12, 2021
0c40529
style: make p system theme aware [skip release]
balazsorban44 Jan 12, 2021
ecbaa14
feat(provider): finish Reddit provider and add documentation (#1094)
afoyer Jan 12, 2021
1a1a1f9
chore: define providers in single file for docs [skip release]
balazsorban44 Jan 12, 2021
b1f6901
chore: Comply to Vercel Open Source sponsorship [skip release] (#1087)
suraj10k Jan 13, 2021
2f88880
chore: fix lint issues [skip release]
balazsorban44 Jan 14, 2021
d520687
feat: add native hkdf (#1124)
koolii Jan 17, 2021
d02c415
chore(deps): upgrade typeorm to v0.2.30 (#1145)
YuriGor Jan 18, 2021
3bec8ea
docs: remove v1 documentation (#1142)
96RadhikaJadhav Jan 18, 2021
de9538d
chore(adapters): remove fauna (#1148)
balazsorban44 Jan 18, 2021
0987f72
feat: forward signIn auth params to /authorize (#1149)
balazsorban44 Jan 18, 2021
b3c7617
fix(adapter): fix ISO Datetime type error in Prisma updateSession (#640)
HaNdTriX Jan 19, 2021
47bcd1e
feat(provider): add option to generate email verification token (#541)
firede Jan 19, 2021
4f93e6a
docs: update info about TypeScript [skip release]
balazsorban44 Jan 19, 2021
536f0ad
feat: add PKCE support (#941)
balazsorban44 Jan 20, 2021
d56fa6a
fix: correct logger import
balazsorban44 Jan 20, 2021
6c84325
feat(provider): add Salesforce provider (#1027)
mmahalwy Jan 21, 2021
47a5c9d
docs(provider): add Salesforce provider
balazsorban44 Jan 21, 2021
b61bfc2
fix(provider): use authed_user on slack instead of spotify (#1174)
lukel97 Jan 21, 2021
af36bd5
fix: use startsWith for protocol matching in parseUrl
balazsorban44 Jan 21, 2021
cf2f899
fix: fix lint issues
balazsorban44 Jan 21, 2021
1aeca00
docs: clear things up around using access_token [skip release]
balazsorban44 Jan 21, 2021
e9fd979
docs: fix typo in callbacks.md [skip release]
balazsorban44 Jan 21, 2021
393bd4a
chore(provider): remove Mixer (#1178)
balazsorban44 Jan 21, 2021
f4d9e54
feat(provider): re-add state, expand protection provider options (#1…
balazsorban44 Jan 22, 2021
ba35ada
fix: send /authorize params through url
balazsorban44 Jan 25, 2021
d2cd02a
fix: Add a null check to the window 'storage' event listener (#1198)
dmmulroy Jan 27, 2021
6c5db3c
docs(provider): fix typos in providers code snippets [skip release] (…
cascandaliato Jan 27, 2021
5dca70e
docs(adapter): add adapter repo to documentation [skip release] (#1173)
aissshah Jan 28, 2021
4f481ee
fix: forward second argument to fetch body in signIn
balazsorban44 Jan 28, 2021
63c9d83
Merge branch 'main' into canary
balazsorban44 Jan 29, 2021
048c1f2
docs: Fix grammar in "Feature Requests" section of FAQs [skip release…
wwsalmon Jan 29, 2021
10c4d9d
refactor: provide raw idToken through account object (#1211)
balazsorban44 Jan 29, 2021
4ab2cca
feat: send all params to logger function (#1214)
balazsorban44 Jan 29, 2021
7dc147a
feat(provider): Add Medium (#1213)
defvova Jan 29, 2021
4a8da44
fix: leave accessTokenExpires as null
balazsorban44 Jan 30, 2021
a2e68cd
docs: more emphasis on req methods [skip release]
balazsorban44 Jan 30, 2021
f106a9e
docs: remove announcement bar [skip release]
balazsorban44 Jan 30, 2021
8a1798f
fix: make OAuth 1 work after refactoring (#1218)
balazsorban44 Jan 30, 2021
da01aa6
docs: Update Providers.Credential Example Block [skip release] (#1225)
codyogden Jan 31, 2021
909acab
Merge branch 'main' into canary [skip release]
balazsorban44 Feb 1, 2021
690f81e
feat(provider): option to disable client-side redirects (credentials)…
balazsorban44 Feb 1, 2021
562bcf2
feat(ts): preliminary TypeScript support (#1223)
balazsorban44 Feb 1, 2021
c387f32
feat(provider): add EVE Online provider (#1227)
geraldm74 Feb 2, 2021
afb5082
docs: clarify custom pages usage [skip release] (#1239)
jeremycaine Feb 2, 2021
21f2231
Merge branch 'canary' into feature/token-endpoint
balazsorban44 Feb 2, 2021
4634374
Merge branch 'main' into feature/token-endpoint
balazsorban44 Feb 9, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 3 additions & 5 deletions pages/api/auth/[...nextauth].js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
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()
import Adapters from 'next-auth/adapters'
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()

export default NextAuth({
providers: [
Expand Down Expand Up @@ -49,7 +48,6 @@ export default NextAuth({
},
debug: false,
theme: 'auto'

// Default Database Adapter (TypeORM)
// database: process.env.DATABASE_URL

Expand Down
1 change: 0 additions & 1 deletion src/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,6 @@ export async function signIn (provider, options = {}, authorizationParams = {})
}

const error = new URL(data.url).searchParams.get('error')

if (res.ok) {
await __NEXTAUTH._getSession({ event: 'storage' })
}
Expand Down
25 changes: 25 additions & 0 deletions src/server/cookies.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export interface CookieOption {
name?: string
options?: {
httpOnly?: boolean
sameSite?: "lax" | "none" | "strict"
path?: string
secure?: boolean
}
}


export type CookieType = "sessionToken" | "callbackUrl" | "csrfToken" | "accessToken" | "idToken"

export interface AccessToken {
accessToken: string
refreshToken?: string
/** Saved as ISO string */
accessTokenExpires?: Date
}

export type IdToken = string

export interface CookieOptions extends Record<CookieType, CookieOption> {

}
2 changes: 2 additions & 0 deletions src/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ async function NextAuthHandler (req, res, userOptions) {
return routes.providers(req, res)
case 'session':
return routes.session(req, res)
case 'tokens':
return routes.tokens(req, res)
case 'csrf':
return res.json({ csrfToken })
case 'signin':
Expand Down
6 changes: 6 additions & 0 deletions src/server/lib/oauth/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface Account {
accessToken?: string
accessTokenExpires?: Date | null
refreshToken?: string
idToken?: string
}
53 changes: 53 additions & 0 deletions src/server/routes/callback.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,3 +281,56 @@ export default async function callback (req, res) {
}
return res.status(500).end(`Error: Callback for provider type ${provider.type} not supported`)
}

/**
* A common usecase is to put the access_token into the session token.
* While nothing wrong with it, the access_token quickly fills up the 4096 bytes
* allowed by most browsers. We therefore put id_token, and access_token/refresh_token
* in their own cookies.
* @param {{
* cookies: import('../cookies').CookieOptions
* res: import('next').NextApiResponse
* maxAge: number
* account: import('../lib/oauth/index').Account
* secret: string
* }} SaveOAuthTokensInCookieParams
*/
function saveOAuthTokensInCookie ({ res, cookies, account, maxAge, secret }) {
const expires = new Date()
expires.setTime(expires.getTime() + (maxAge * 1000))

if (account.accessToken) {
let accessTokenExpires = account.accessTokenExpires
if (accessTokenExpires) {
accessTokenExpires = new Date(accessTokenExpires)
} else {
accessTokenExpires = expires
}
// Save the access_token and refresh_token together
const accessToken = {
accessToken: account.accessToken,
refreshToken: account.refreshToken
}

// TODO: Encrypt with secret
const encryptedAccessToken = JSON.stringify(accessToken)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs encryption


cookie.set(
res,
cookies.accessToken.name,
encryptedAccessToken,
{ expires: accessTokenExpires.toISOString(), ...cookies.accessToken.options }
)
}

if (account.idToken) {
// TODO: Encrypt with secret
const encryptedIdToken = account.idToken
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs encryption

cookie.set(
res,
cookies.idToken.name,
encryptedIdToken,
{ expires: expires, ...cookies.idToken.options }
)
}
}
163 changes: 163 additions & 0 deletions src/server/routes/tokens.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@

// NOTE: fetch() is built in to Next.js 9.4
/* global fetch:false */
import cookie from '../lib/cookie'
import logger from '../../lib/logger'

/**
*
* @param {import('next').NextApiRequest} req
* @param {import('next').NextApiResponse} res
* @param {import('..').NextAuthOptions} options
* @param {(value: any) => void} done
*/
export default async function tokens (req, res, options, done) {
const { query } = req
const {
nextauth,
tokenType = nextauth[2],
action = nextauth[3]
} = query
const providerName = options?.provider

const { cookies } = options
const useJwtSession = options.session.jwt
const hasAccessToken = cookies.sessionToken.name in req.cookies
const sessionToken = req.cookies[cookies.sessionToken.name]

let response = {}
res.setHeader('Content-Type', 'application/json')

if (!hasAccessToken && !sessionToken) {
res.json(response)
return done()
}

if (req.method === 'GET') {
if (useJwtSession) {
const provider = options.providers[providerName]
if (provider?.type !== 'oauth') {
res.json(response)
logger.error('INVALID_TOKEN_PROVIDER', 'Invalid ')
return done()
}
// TODO: decrypt with options.secret
/** @type {import('../cookies').AccessToken} */
let accessToken = req[cookies.accessToken.name]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs decryption


if (new Date().toISOString() < accessToken.accessTokenExpires) {
// The access token is still fresh, return it
response = {
token: accessToken.accessToken,
expires: accessToken.accessTokenExpires
}
} else {
// If the provider is OIDC compliant, we can try to refresh the token
if (provider.oidc) {
try {
accessToken = await refreshAccessToken({ accessToken, provider })
response = accessToken
} catch (error) {
logger.error('REFRESH_TOKEN_ERROR', error)
}
} else {
// REVIEW: redirect to signin(?)
}
}
} else {
const { adapter } = options
try {
const { getSession, updateSession, getAccounts, getAccount } = await adapter.getAdapter(options)
const session = await getSession(sessionToken)
if (session) {
// Trigger update to session object to update session expiry
await updateSession(session)

if (!providerName) {
response = await getAccounts(session.userId)
} else {
let expired
// TODO: Determine if tokens have expired

if (action === 'renew' || expired) {
// TODO: Exchange refresh token for access token
}

const account = await getAccount(session.userId, providerName)
if (account) {
switch (tokenType) {
case undefined:
case 'access':
response = {
type: 'access',
token: account.accessToken
}
break
case 'refresh':
response = {
type: 'refresh',
token: account.refreshToken
}
break
default:
res.status(404).end()
return done()
}
} else {
res.status(404).end()
return done()
}
}
} else if (sessionToken) {
// If sessionToken was found set but it's not valid for a session then
// remove the sessionToken cookie from browser.
cookie.set(res, cookies.sessionToken.name, '', { ...cookies.sessionToken.options, maxAge: 0 })
}
} catch (error) {
logger.error('TOKEN_ERROR', error)
}
}
}

res.json(response)
return done()
}

/**
* @param {{
* token: import('../cookies').AccessToken,
* provider: {
* accessTokenUrl: string
* clientId: string
* clientSecret: string
* }
* }} params
*/
async function refreshAccessToken ({ token, provider }) {
const response = await fetch(provider.accessTokenUrl, {
body: new URLSearchParams({
client_id: provider.clientId,
client_secret: provider.clientSecret,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pkce does not have a secret, may need some others params also

grant_type: 'refresh_token',
refresh_token: token.refreshToken
}),
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
method: 'POST'
})

const newTokens = await response.json()

if (!response.ok) {
throw new Error({ newTokens })
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also update the cookie with these new tokens

return {
...token,
accessToken: newTokens.access_token,
accessTokenExpires: new Date(newTokens.expires_in).toISOString(),
// Fallback to the previous refresh_token, if it is not rotating/sliding
refreshToken: newTokens.refresh_token || token.refreshToken
}
}