-
Notifications
You must be signed in to change notification settings - Fork 17
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
refactor(authlify): get authlify token from header #326
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
3466f62
feat: get netlify graph token from headers
dwwoelfel 3344101
chore: cleanup
dwwoelfel ef15884
chore: be a little more careful about backwards compatibility
dwwoelfel 2eb97b1
Merge branch 'main' of github.com:netlify/functions into dww/get-secrets
dwwoelfel 035a0ff
feat: add getGraphToken and address some feedback
dwwoelfel eaa0d3a
chore: implement remaining review feedback
dwwoelfel ce0fb5d
fix: fix copy/paste error
dwwoelfel 5d50dbc
chore: cleanup comments
dwwoelfel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { Buffer } from 'buffer' | ||
import { request } from 'https' | ||
import { env } from 'process' | ||
|
||
const siteId = env.SITE_ID | ||
|
||
const GRAPH_HOST = 'graph.netlify.com' | ||
|
||
export const graphRequest = function (secretToken: string, requestBody: Uint8Array): Promise<string> { | ||
return new Promise((resolve, reject) => { | ||
const port = 443 | ||
|
||
const options = { | ||
host: GRAPH_HOST, | ||
path: `/graphql?app_id=${siteId}`, | ||
port, | ||
method: 'POST', | ||
headers: { | ||
Authorization: `Bearer ${secretToken}`, | ||
'Content-Type': 'application/json', | ||
Accept: 'application/json', | ||
'Content-Length': requestBody ? Buffer.byteLength(requestBody) : 0, | ||
}, | ||
} | ||
|
||
const req = request(options, (res) => { | ||
if (res.statusCode !== 200) { | ||
return reject(new Error(String(res.statusCode))) | ||
} | ||
|
||
const body: Array<Uint8Array> = [] | ||
|
||
res.on('data', (chunk) => { | ||
body.push(chunk) | ||
}) | ||
|
||
res.on('end', () => { | ||
const data = Buffer.concat(body).toString() | ||
try { | ||
resolve(data) | ||
} catch (error) { | ||
reject(error) | ||
} | ||
}) | ||
}) | ||
|
||
req.on('error', (error) => { | ||
reject(error) | ||
}) | ||
|
||
req.write(requestBody) | ||
|
||
req.end() | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import { env } from 'process' | ||
|
||
export type GraphTokenResponseError = { | ||
type: 'missing-event-in-function' | 'provided-event-in-build' | ||
message: string | ||
} | ||
|
||
export type GraphTokenResponse = { | ||
errors?: GraphTokenResponseError[] | ||
token?: string | null | ||
} | ||
|
||
const TOKEN_HEADER = 'X-Nf-Graph-Token' | ||
|
||
// Matches Web API Headers type (https://developer.mozilla.org/en-US/docs/Web/API/Headers) | ||
interface RequestHeaders { | ||
get(name: string): string | null | ||
} | ||
|
||
// Matches http.IncomingHttpHeaders | ||
interface IncomingHttpHeaders { | ||
[key: string]: string | string[] | undefined | ||
} | ||
|
||
export interface HasHeaders { | ||
headers: RequestHeaders | IncomingHttpHeaders | ||
} | ||
|
||
const hasRequestStyleHeaders = function (headers: RequestHeaders | IncomingHttpHeaders): headers is RequestHeaders { | ||
return (headers as RequestHeaders).get !== undefined && typeof headers.get === 'function' | ||
} | ||
|
||
const graphTokenFromIncomingHttpStyleHeaders = function ( | ||
headers: RequestHeaders | IncomingHttpHeaders, | ||
): string | null | undefined { | ||
if (TOKEN_HEADER in headers) { | ||
const header = headers[TOKEN_HEADER] | ||
if (header == null || typeof header === 'string') { | ||
return header | ||
} | ||
return header[0] | ||
} | ||
} | ||
|
||
// Backwards compatibility with older version of cli that doesn't inject header | ||
const authlifyTokenFallback = function (event: HasHeaders): GraphTokenResponse { | ||
const token = (event as { authlifyToken?: string | null })?.authlifyToken | ||
return { token } | ||
} | ||
|
||
const graphTokenFromEvent = function (event: HasHeaders): GraphTokenResponse { | ||
const { headers } = event | ||
// Check if object first in case there is a header with key `get` | ||
const token = graphTokenFromIncomingHttpStyleHeaders(headers) | ||
if (token) { | ||
return { token } | ||
} | ||
|
||
if (hasRequestStyleHeaders(headers)) { | ||
return { token: headers.get(TOKEN_HEADER) } | ||
} | ||
|
||
return authlifyTokenFallback(event) | ||
} | ||
|
||
const graphTokenFromEnv = function (): GraphTokenResponse { | ||
// _NETLIFY_GRAPH_TOKEN injected by next plugin | ||
// eslint-disable-next-line no-underscore-dangle | ||
const token = env._NETLIFY_GRAPH_TOKEN || env.NETLIFY_GRAPH_TOKEN | ||
return { token } | ||
} | ||
|
||
const isEventRequired = function (): boolean { | ||
const localDev = env.NETLIFY_DEV === 'true' | ||
const localBuild = !localDev && env.NETLIFY_LOCAL === 'true' | ||
const remoteBuild = env.NETLIFY === 'true' | ||
// neither `localBuild` nor `remoteBuild` will be true in the on-demand builder case | ||
const inBuildPhase = localBuild || remoteBuild | ||
|
||
const inGetStaticProps = | ||
// Set by the nextjs plugin | ||
// eslint-disable-next-line no-underscore-dangle | ||
typeof env._NETLIFY_GRAPH_TOKEN !== 'undefined' | ||
|
||
return !inBuildPhase && !inGetStaticProps | ||
} | ||
|
||
const incorrectArgumentsErrors = function ( | ||
event: HasHeaders | null | undefined, | ||
): undefined | GraphTokenResponseError[] { | ||
const requiresEvent = isEventRequired() | ||
|
||
if (requiresEvent && event == null) { | ||
const errorMessage = | ||
'You must provide an event or request to `getNetlifyGraphToken` when used in functions and on-demand builders.' | ||
return [{ type: 'missing-event-in-function', message: errorMessage }] | ||
} | ||
|
||
if (!requiresEvent && event != null) { | ||
const errorMessage = 'You must not pass arguments to `getNetlifyGraphToken` when used in builds.' | ||
return [{ type: 'provided-event-in-build', message: errorMessage }] | ||
} | ||
} | ||
|
||
const logErrors = function (errors: GraphTokenResponseError[]) { | ||
for (const error of errors) { | ||
// Log errors to help guide developer | ||
console.error(error.message) | ||
} | ||
} | ||
|
||
export const getNetlifyGraphToken = function ( | ||
event?: HasHeaders | null | undefined, | ||
// caller can prevent error log. Allows getSecrets to provide better errors | ||
supressLog?: boolean, | ||
): GraphTokenResponse { | ||
const errors = incorrectArgumentsErrors(event) | ||
|
||
if (errors) { | ||
if (!supressLog) { | ||
logErrors(errors) | ||
} | ||
return { errors } | ||
} | ||
|
||
return event ? graphTokenFromEvent(event) : graphTokenFromEnv() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we only changing the function name for the error message?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. I thought of making a function to create the error message, but thought it was overkill.