@octokit/oauth-app
Advanced tools
Comparing version 5.0.0-beta.1 to 5.0.0-beta.2
@@ -15,3 +15,3 @@ 'use strict'; | ||
const VERSION = "5.0.0-beta.1"; | ||
const VERSION = "5.0.0-beta.2"; | ||
@@ -23,10 +23,7 @@ function addEventHandler(state, eventName, eventHandler) { | ||
} | ||
return; | ||
} | ||
if (!state.eventHandlers[eventName]) { | ||
state.eventHandlers[eventName] = []; | ||
} | ||
state.eventHandlers[eventName].push(eventHandler); | ||
@@ -44,3 +41,2 @@ } | ||
} = context; | ||
if (state.eventHandlers[`${name}.${action}`]) { | ||
@@ -51,3 +47,2 @@ for (const eventHandler of state.eventHandlers[`${name}.${action}`]) { | ||
} | ||
if (state.eventHandlers[name]) { | ||
@@ -64,3 +59,2 @@ for (const eventHandler of state.eventHandlers[name]) { | ||
...options, | ||
async factory(options) { | ||
@@ -84,3 +78,2 @@ const octokit = new state.Octokit({ | ||
} | ||
}); | ||
@@ -94,4 +87,5 @@ } | ||
...options, | ||
allowSignup: options.allowSignup || state.allowSignup, | ||
scopes: options.scopes || state.defaultScopes | ||
allowSignup: state.allowSignup ?? options.allowSignup, | ||
redirectUrl: options.redirectUrl ?? state.redirectUrl, | ||
scopes: options.scopes ?? state.defaultScopes | ||
}; | ||
@@ -157,3 +151,2 @@ return OAuthMethods.getWebFlowAuthorizationUrl({ | ||
}; | ||
if (state.clientType === "oauth-app") { | ||
@@ -185,7 +178,7 @@ const response = await OAuthMethods.resetToken({ | ||
}); | ||
return { ...response, | ||
return { | ||
...response, | ||
authentication | ||
}; | ||
} | ||
const response = await OAuthMethods.resetToken({ | ||
@@ -214,3 +207,4 @@ clientType: "github-app", | ||
}); | ||
return { ...response, | ||
return { | ||
...response, | ||
authentication | ||
@@ -224,3 +218,2 @@ }; | ||
} | ||
const response = await OAuthMethods.refreshToken({ | ||
@@ -252,3 +245,4 @@ clientType: "github-app", | ||
}); | ||
return { ...response, | ||
return { | ||
...response, | ||
authentication | ||
@@ -262,3 +256,2 @@ }; | ||
} | ||
const response = await OAuthMethods.scopeToken({ | ||
@@ -290,3 +283,4 @@ clientType: "github-app", | ||
}); | ||
return { ...response, | ||
return { | ||
...response, | ||
authentication | ||
@@ -306,3 +300,4 @@ }; | ||
...optionsWithDefaults | ||
}) : // istanbul ignore next | ||
}) : | ||
// istanbul ignore next | ||
await OAuthMethods.deleteToken({ | ||
@@ -336,3 +331,4 @@ clientType: "github-app", | ||
...optionsWithDefaults | ||
}) : // istanbul ignore next | ||
}) : | ||
// istanbul ignore next | ||
await OAuthMethods.deleteAuthorization({ | ||
@@ -391,14 +387,11 @@ clientType: "github-app", | ||
}; | ||
} // request.url may include ?query parameters which we don't want for `route` | ||
} | ||
// request.url may include ?query parameters which we don't want for `route` | ||
// hence the workaround using new URL() | ||
let { | ||
pathname | ||
} = new URL(request.url, "http://localhost"); | ||
if (!pathname.startsWith(`${pathPrefix}/`)) { | ||
return undefined; | ||
} | ||
pathname = pathname.slice(pathPrefix.length + 1); | ||
@@ -416,10 +409,8 @@ const route = [request.method, pathname].join(" "); | ||
deleteGrant: `DELETE grant` | ||
}; // handle unknown routes | ||
}; | ||
// handle unknown routes | ||
if (!Object.values(routes).includes(route)) { | ||
return unknownRouteResponse(request); | ||
} | ||
let json; | ||
try { | ||
@@ -440,3 +431,2 @@ const text = await request.text(); | ||
} | ||
const { | ||
@@ -447,6 +437,4 @@ searchParams | ||
const headers = request.headers; | ||
try { | ||
var _headers$authorizatio6; | ||
if (route === routes.getLogin) { | ||
@@ -458,3 +446,3 @@ const { | ||
scopes: query.scopes ? query.scopes.split(",") : undefined, | ||
allowSignup: query.allowSignup !== "false", | ||
allowSignup: query.allowSignup ? query.allowSignup === "true" : undefined, | ||
redirectUrl: query.redirectUrl | ||
@@ -469,3 +457,2 @@ }); | ||
} | ||
if (route === routes.getCallback) { | ||
@@ -475,7 +462,5 @@ if (query.error) { | ||
} | ||
if (!query.code) { | ||
throw new Error('[@octokit/oauth-app] "code" parameter is required'); | ||
} | ||
const { | ||
@@ -498,3 +483,2 @@ authentication: { | ||
} | ||
if (route === routes.createToken) { | ||
@@ -505,12 +489,10 @@ const { | ||
} = json; | ||
if (!code) { | ||
throw new Error('[@octokit/oauth-app] "code" parameter is required'); | ||
} | ||
const result = await app.createToken({ | ||
code, | ||
redirectUrl | ||
}); // @ts-ignore | ||
}); | ||
// @ts-ignore | ||
delete result.authentication.clientSecret; | ||
@@ -526,16 +508,12 @@ return { | ||
} | ||
if (route === routes.getToken) { | ||
var _headers$authorizatio; | ||
const token = (_headers$authorizatio = headers.authorization) === null || _headers$authorizatio === void 0 ? void 0 : _headers$authorizatio.substr("token ".length); | ||
if (!token) { | ||
throw new Error('[@octokit/oauth-app] "Authorization" header is required'); | ||
} | ||
const result = await app.checkToken({ | ||
token | ||
}); // @ts-ignore | ||
}); | ||
// @ts-ignore | ||
delete result.authentication.clientSecret; | ||
@@ -551,16 +529,12 @@ return { | ||
} | ||
if (route === routes.patchToken) { | ||
var _headers$authorizatio2; | ||
const token = (_headers$authorizatio2 = headers.authorization) === null || _headers$authorizatio2 === void 0 ? void 0 : _headers$authorizatio2.substr("token ".length); | ||
if (!token) { | ||
throw new Error('[@octokit/oauth-app] "Authorization" header is required'); | ||
} | ||
const result = await app.resetToken({ | ||
token | ||
}); // @ts-ignore | ||
}); | ||
// @ts-ignore | ||
delete result.authentication.clientSecret; | ||
@@ -576,24 +550,18 @@ return { | ||
} | ||
if (route === routes.patchRefreshToken) { | ||
var _headers$authorizatio3; | ||
const token = (_headers$authorizatio3 = headers.authorization) === null || _headers$authorizatio3 === void 0 ? void 0 : _headers$authorizatio3.substr("token ".length); | ||
if (!token) { | ||
throw new Error('[@octokit/oauth-app] "Authorization" header is required'); | ||
} | ||
const { | ||
refreshToken | ||
} = json; | ||
if (!refreshToken) { | ||
throw new Error("[@octokit/oauth-app] refreshToken must be sent in request body"); | ||
} | ||
const result = await app.refreshToken({ | ||
refreshToken | ||
}); // @ts-ignore | ||
}); | ||
// @ts-ignore | ||
delete result.authentication.clientSecret; | ||
@@ -609,17 +577,13 @@ return { | ||
} | ||
if (route === routes.scopeToken) { | ||
var _headers$authorizatio4; | ||
const token = (_headers$authorizatio4 = headers.authorization) === null || _headers$authorizatio4 === void 0 ? void 0 : _headers$authorizatio4.substr("token ".length); | ||
if (!token) { | ||
throw new Error('[@octokit/oauth-app] "Authorization" header is required'); | ||
} | ||
const result = await app.scopeToken({ | ||
token, | ||
...json | ||
}); // @ts-ignore | ||
}); | ||
// @ts-ignore | ||
delete result.authentication.clientSecret; | ||
@@ -635,12 +599,8 @@ return { | ||
} | ||
if (route === routes.deleteToken) { | ||
var _headers$authorizatio5; | ||
const token = (_headers$authorizatio5 = headers.authorization) === null || _headers$authorizatio5 === void 0 ? void 0 : _headers$authorizatio5.substr("token ".length); | ||
if (!token) { | ||
throw new Error('[@octokit/oauth-app] "Authorization" header is required'); | ||
} | ||
await app.deleteToken({ | ||
@@ -655,11 +615,8 @@ token | ||
}; | ||
} // route === routes.deleteGrant | ||
} | ||
// route === routes.deleteGrant | ||
const token = (_headers$authorizatio6 = headers.authorization) === null || _headers$authorizatio6 === void 0 ? void 0 : _headers$authorizatio6.substr("token ".length); | ||
if (!token) { | ||
throw new Error('[@octokit/oauth-app] "Authorization" header is required'); | ||
} | ||
await app.deleteAuthorization({ | ||
@@ -694,3 +651,2 @@ token | ||
} = request; | ||
async function text() { | ||
@@ -703,3 +659,2 @@ const text = await new Promise((resolve, reject) => { | ||
} | ||
return { | ||
@@ -722,3 +677,2 @@ method, | ||
const octokitResponse = await handleRequest(app, options, octokitRequest); | ||
if (octokitResponse) { | ||
@@ -771,5 +725,3 @@ sendResponse(octokitResponse, response); | ||
const headers = request.headers; | ||
const text = async () => request.body || ""; | ||
return { | ||
@@ -800,2 +752,13 @@ method, | ||
class OAuthApp { | ||
static defaults(defaults) { | ||
const OAuthAppWithDefaults = class extends this { | ||
constructor(...args) { | ||
super({ | ||
...defaults, | ||
...args[0] | ||
}); | ||
} | ||
}; | ||
return OAuthAppWithDefaults; | ||
} | ||
constructor(options) { | ||
@@ -820,2 +783,3 @@ const Octokit = options.Octokit || OAuthAppOctokit; | ||
baseUrl: options.baseUrl, | ||
redirectUrl: options.redirectUrl, | ||
log: options.log, | ||
@@ -826,4 +790,4 @@ Octokit, | ||
}; | ||
this.on = addEventHandler.bind(null, state); // @ts-expect-error TODO: figure this out | ||
this.on = addEventHandler.bind(null, state); | ||
// @ts-expect-error TODO: figure this out | ||
this.octokit = octokit; | ||
@@ -840,15 +804,2 @@ this.getUserOctokit = getUserOctokitWithState.bind(null, state); | ||
} | ||
static defaults(defaults) { | ||
const OAuthAppWithDefaults = class extends this { | ||
constructor(...args) { | ||
super({ ...defaults, | ||
...args[0] | ||
}); | ||
} | ||
}; | ||
return OAuthAppWithDefaults; | ||
} | ||
} | ||
@@ -855,0 +806,0 @@ OAuthApp.VERSION = VERSION; |
{ | ||
"name": "@octokit/oauth-app", | ||
"description": "GitHub OAuth toolset for Node.js", | ||
"version": "5.0.0-beta.1", | ||
"version": "5.0.0-beta.2", | ||
"license": "MIT", | ||
@@ -37,14 +37,14 @@ "files": [ | ||
"@pika/plugin-ts-standard-pkg": "^0.9.2", | ||
"@types/jest": "^28.0.0", | ||
"@types/node": "^16.0.0", | ||
"@types/jest": "^29.0.0", | ||
"@types/node": "^18.0.0", | ||
"@types/node-fetch": "^2.5.4", | ||
"express": "^4.17.1", | ||
"fetch-mock": "^9.0.0", | ||
"jest": "^28.0.0", | ||
"jest": "^29.0.0", | ||
"nock": "^13.0.0", | ||
"node-fetch": "^2.6.0", | ||
"prettier": "2.7.1", | ||
"prettier": "2.8.7", | ||
"semantic-release-plugin-update-version-in-files": "^1.0.0", | ||
"ts-jest": "^28.0.0", | ||
"typescript": "^4.0.2" | ||
"ts-jest": "^29.0.0", | ||
"typescript": "^5.0.0" | ||
}, | ||
@@ -51,0 +51,0 @@ "engines": { |
138
README.md
@@ -13,27 +13,29 @@ # oauth-app.js | ||
- [Usage](#usage) | ||
- [For OAuth Apps](#for-oauth-apps) | ||
- [For GitHub Apps](#for-github-apps) | ||
- [Examples](#examples) | ||
- [`OAuthApp.defaults(options)`](#oauthappdefaultsoptions) | ||
- [Constructor options](#constructor-options) | ||
- [`app.on(eventName, eventHandler)`](#apponeventname-eventhandler) | ||
- [`app.octokit`](#appoctokit) | ||
- [`app.getUserOctokit(options)`](#appgetuseroctokitoptions) | ||
- [`app.getWebFlowAuthorizationUrl(options)`](#appgetwebflowauthorizationurloptions) | ||
- [`app.createToken(options)`](#appcreatetokenoptions) | ||
- [For OAuth Web flow](#for-oauth-web-flow) | ||
- [For OAuth Device flow](#for-oauth-device-flow) | ||
- [`app.checkToken(options)`](#appchecktokenoptions) | ||
- [`app.resetToken(options)`](#appresettokenoptions) | ||
- [`app.refreshToken(options)`](#apprefreshtokenoptions) | ||
- [`app.scopeToken(options)`](#appscopetokenoptions) | ||
- [`app.deleteToken(options)`](#appdeletetokenoptions) | ||
- [`app.deleteAuthorization(options)`](#appdeleteauthorizationoptions) | ||
- [Middlewares](#middlewares) | ||
- [`createNodeMiddleware(app, options)`](#createnodemiddlewareapp-options) | ||
- [`createWebWorkerHandler(app, options)`](#createwebworkerhandlerapp-options) | ||
- [`createAWSLambdaAPIGatewayV2Handler(app, options)`](#createawslambdaapigatewayv2handlerapp-options) | ||
- [Contributing](#contributing) | ||
- [License](#license) | ||
- [oauth-app.js](#oauth-appjs) | ||
- [Usage](#usage) | ||
- [For OAuth Apps](#for-oauth-apps) | ||
- [For GitHub Apps](#for-github-apps) | ||
- [Examples](#examples) | ||
- [`OAuthApp.defaults(options)`](#oauthappdefaultsoptions) | ||
- [Constructor options](#constructor-options) | ||
- [`app.on(eventName, eventHandler)`](#apponeventname-eventhandler) | ||
- [`app.octokit`](#appoctokit) | ||
- [`app.getUserOctokit(options)`](#appgetuseroctokitoptions) | ||
- [`app.getWebFlowAuthorizationUrl(options)`](#appgetwebflowauthorizationurloptions) | ||
- [`app.createToken(options)`](#appcreatetokenoptions) | ||
- [For OAuth Web flow](#for-oauth-web-flow) | ||
- [For OAuth Device flow](#for-oauth-device-flow) | ||
- [`app.checkToken(options)`](#appchecktokenoptions) | ||
- [`app.resetToken(options)`](#appresettokenoptions) | ||
- [`app.refreshToken(options)`](#apprefreshtokenoptions) | ||
- [`app.scopeToken(options)`](#appscopetokenoptions) | ||
- [`app.deleteToken(options)`](#appdeletetokenoptions) | ||
- [`app.deleteAuthorization(options)`](#appdeleteauthorizationoptions) | ||
- [Middlewares](#middlewares) | ||
- [`createNodeMiddleware(app, options)`](#createnodemiddlewareapp-options) | ||
- [`createWebWorkerHandler(app, options)`](#createwebworkerhandlerapp-options) | ||
- [`createAWSLambdaAPIGatewayV2Handler(app, options)`](#createawslambdaapigatewayv2handlerapp-options) | ||
- [Build Custom Middlewares](#build-custom-middlewares) | ||
- [Contributing](#contributing) | ||
- [License](#license) | ||
@@ -194,2 +196,13 @@ <!-- tocstop --> | ||
<th> | ||
<code>redirectUrl</code> | ||
</th> | ||
<th> | ||
<code>string</code> | ||
</th> | ||
<td> | ||
The URL in your application where users will be sent after authorization. See <a href="https://docs.github.com/en/developers/apps/authorizing-oauth-apps#redirect-urls">Redirect URLs</a> in GitHub’s Developer Guide. | ||
</td> | ||
</tr> | ||
<tr> | ||
<th> | ||
<code>defaultScopes</code> | ||
@@ -1075,2 +1088,77 @@ </th> | ||
### Build Custom Middlewares | ||
When above middlewares do not meet your needs, you can build your own | ||
using the exported `handleRequest` function. | ||
[`handleRequest`](./src/middleware/handle-request.ts) function is an abstract HTTP handler which accepts an `OctokitRequest` and returns an `OctokitResponse` if the request matches any predefined route. | ||
> Different environments (e.g., Node.js, Cloudflare Workers, Deno, etc.) exposes different APIs when processing HTTP requests (e.g., [`IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage) for Node.js, [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) for Cloudflare workers, etc.). Two HTTP-related types ([`OctokitRequest` and `OctokitResponse`](.src/middleware/types.ts)) are generalized to make an abstract HTTP handler possible. | ||
To share the behavior and capability with the existing Node.js middleware (and be compatible with [OAuth user authentication strategy in the browser](https://github.com/octokit/auth-oauth-user-client.js)), it is better to implement your HTTP handler/middleware based on `handleRequest` function. | ||
`handleRequest` function takes three parameters: | ||
<table width="100%"> | ||
<thead align=left> | ||
<tr> | ||
<th width=150> | ||
name | ||
</th> | ||
<th width=70> | ||
type | ||
</th> | ||
<th> | ||
description | ||
</th> | ||
</tr> | ||
</thead> | ||
<tbody align=left valign=top> | ||
<tr> | ||
<th> | ||
<code>app</code> | ||
</th> | ||
<th> | ||
<code>OAuthApp instance</code> | ||
</th> | ||
<td> | ||
<strong>Required</strong>. | ||
</td> | ||
</tr> | ||
<tr> | ||
<th> | ||
<code>options.pathPrefix</code> | ||
</th> | ||
<th> | ||
<code>string</code> | ||
</th> | ||
<td> | ||
All exposed paths will be prefixed with the provided prefix. Defaults to `"/api/github/oauth"` | ||
</td> | ||
</tr> | ||
<tr> | ||
<th> | ||
<code>request</code> | ||
</th> | ||
<th> | ||
<code>OctokitRequest</code> | ||
</th> | ||
<td> | ||
Generalized HTTP request in `OctokitRequest` type. | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
Implementing an HTTP handler/middleware for a certain environment involves three steps: | ||
1. Write a function to parse the HTTP request (e.g., `IncomingMessage` in Node.js) into an `OctokitRequest` object. See [`node/parse-request.ts`](.src/middleware/node/parse-request.ts) for reference. | ||
2. Write a function to render an `OctokitResponse` object (e.g., as `ServerResponse` in Node.js). See [`node/send-response.ts`](.src/middleware/node/send-response.ts) for reference. | ||
3. Expose an HTTP handler/middleware in the dialect of the environment which performs three steps: | ||
1. Parse the HTTP request using (1). | ||
2. Process the `OctokitRequest` object using `handleRequest`. | ||
3. Render the `OctokitResponse` object using (2). | ||
## Contributing | ||
@@ -1077,0 +1165,0 @@ |
1168
59940
4
746