Skip to content

Commit

Permalink
fix(command-dev): inject the Authlify Token (#4167)
Browse files Browse the repository at this point in the history
  • Loading branch information
anmonteiro authored Feb 4, 2022
1 parent fc1c991 commit 4ffba32
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 8 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
"is-plain-obj": "^3.0.0",
"is-wsl": "^2.2.0",
"isexe": "^2.0.0",
"jsonwebtoken": "^8.5.1",
"jwt-decode": "^3.0.0",
"lambda-local": "2.0.1",
"listr": "^0.14.3",
Expand Down Expand Up @@ -197,7 +198,6 @@
"graphviz": "^0.0.9",
"husky": "^7.0.4",
"ini": "^2.0.0",
"jsonwebtoken": "^8.5.1",
"mock-fs": "^5.1.2",
"p-timeout": "^4.0.0",
"proxyquire": "^2.1.3",
Expand Down
47 changes: 43 additions & 4 deletions src/commands/dev/dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const {
detectServerSettings,
error,
exit,
generateAuthlifyJWT,
getSiteInformation,
injectEnvVariables,
log,
Expand Down Expand Up @@ -253,7 +254,29 @@ const dev = async (options, command) => {
)
}

await injectEnvVariables({ env: command.netlify.cachedConfig.env, site })
const startNetlifyGraphWatcher = Boolean(options.graph)
let authlifyJWT

if (startNetlifyGraphWatcher) {
const netlifyToken = await command.authenticate()
authlifyJWT = generateAuthlifyJWT(netlifyToken, siteInfo.authlify_token_id, site.id)
}

await injectEnvVariables({
env: Object.assign(
command.netlify.cachedConfig.env,
authlifyJWT == null
? {}
: {
ONEGRAPH_AUTHLIFY_TOKEN: {
sources: ['general'],
value: authlifyJWT,
},
},
),
site,
})

const { addonsUrls, capabilities, siteUrl, timeouts } = await getSiteInformation({
// inherited from base command --offline
offline: options.offline,
Expand All @@ -273,8 +296,26 @@ const dev = async (options, command) => {

command.setAnalyticsPayload({ projectType: settings.framework || 'custom', live: options.live })

let configWithAuthlify

if (siteInfo.authlify_token_id) {
const netlifyToken = command.authenticate()
// Only inject the authlify config if a token ID exists. This prevents
// calling command.authenticate() (which opens a browser window) if the
// user hasn't enabled API Authentication
configWithAuthlify = Object.assign(config, {
authlify: {
netlifyToken,
authlifyTokenId: siteInfo.authlify_token_id,
siteId: site.id,
},
})
} else {
configWithAuthlify = config
}

await startFunctionsServer({
config,
config: configWithAuthlify,
settings,
site,
siteUrl,
Expand All @@ -295,8 +336,6 @@ const dev = async (options, command) => {
process.env.URL = url
process.env.DEPLOY_URL = url

const startNetlifyGraphWatcher = Boolean(options.graph)

if (startNetlifyGraphWatcher && options.offline) {
warn(`Unable to start Netlify Graph in offline mode`)
} else if (startNetlifyGraphWatcher && !site.id) {
Expand Down
13 changes: 10 additions & 3 deletions src/lib/functions/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const {
NETLIFYDEVERR,
NETLIFYDEVLOG,
error: errorExit,
generateAuthlifyJWT,
getInternalFunctionsDir,
log,
} = require('../../utils')
Expand Down Expand Up @@ -44,7 +45,7 @@ const buildClientContext = function (headers) {
}
}

const createHandler = function ({ functionsRegistry }) {
const createHandler = function ({ config, functionsRegistry }) {
return async function handler(request, response) {
// handle proxies without path re-writes (http-servr)
const cleanPath = request.path.replace(/^\/.netlify\/(functions|builders)/, '')
Expand Down Expand Up @@ -105,6 +106,11 @@ const createHandler = function ({ functionsRegistry }) {
rawQuery,
}

if (config && config.authlify && config.authlify.authlifyTokenId != null) {
const { authlifyTokenId, netlifyToken, siteId } = config.authlify
event.authlifyToken = generateAuthlifyJWT(netlifyToken, authlifyTokenId, siteId)
}

const clientContext = buildClientContext(request.headers) || {}

if (func.isBackground) {
Expand Down Expand Up @@ -154,14 +160,14 @@ const createHandler = function ({ functionsRegistry }) {
}
}

const getFunctionsServer = function ({ buildersPrefix, functionsPrefix, functionsRegistry, siteUrl }) {
const getFunctionsServer = function ({ buildersPrefix, config, functionsPrefix, functionsRegistry, siteUrl }) {
// performance optimization, load express on demand
// eslint-disable-next-line node/global-require
const express = require('express')
// eslint-disable-next-line node/global-require
const expressLogging = require('express-logging')
const app = express()
const functionHandler = createHandler({ functionsRegistry })
const functionHandler = createHandler({ config, functionsRegistry })

app.set('query parser', 'simple')

Expand Down Expand Up @@ -218,6 +224,7 @@ const startFunctionsServer = async ({
await functionsRegistry.scan(functionsDirectories)

const server = getFunctionsServer({
config,
functionsRegistry,
siteUrl,
functionsPrefix,
Expand Down
23 changes: 23 additions & 0 deletions src/utils/dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const process = require('process')

const { get } = require('dot-prop')
const getPort = require('get-port')
const jwt = require('jsonwebtoken')
const isEmpty = require('lodash/isEmpty')

const { supportsBackgroundFunctions } = require('../lib/account')
Expand Down Expand Up @@ -193,8 +194,30 @@ const acquirePort = async ({ configuredPort, defaultPort, errorMessage }) => {
return acquiredPort
}

// Generates an Authlify JWT with the following claims:
// - site_id
// - netlify_token -- the bearer token for the Netlify API
// - authlify_token_id -- the authlify token ID stored for the site after
// enabling API Authentication.
const generateAuthlifyJWT = (netlifyToken, authlifyTokenId, siteId) => {
const claims = {
netlify_token: netlifyToken,
authlify_token_id: authlifyTokenId,
site_id: siteId,
}

return jwt.sign(
{ 'https://netlify.com/jwt/claims': claims },
// doesn't matter. OneGraph doesn't check the signature. The presence of
// the Netlify API bearer token is enough because we've authenticated the
// user through `command.authenticate()`
'NOT_SIGNED',
)
}

module.exports = {
getSiteInformation,
injectEnvVariables,
acquirePort,
generateAuthlifyJWT,
}

1 comment on commit 4ffba32

@github-actions
Copy link

Choose a reason for hiding this comment

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

📊 Benchmark results

Package size: 362 MB

Please sign in to comment.