From adb57e647a44c6567e36db5cdfdde4d0dcd0bd2d Mon Sep 17 00:00:00 2001 From: Sayak Mukhopadhyay Date: Wed, 7 Dec 2022 02:50:09 +0530 Subject: [PATCH] feat: add redirectUrl as a constructor option (#371) --- README.md | 63 ++++++++++------- src/index.ts | 1 + src/methods/get-web-flow-authorization-url.ts | 1 + src/types.ts | 2 + test/node-middleware.test.ts | 67 +++++++++++++++++++ 5 files changed, 109 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 5c1db4475..5c2251ca8 100644 --- a/README.md +++ b/README.md @@ -10,31 +10,33 @@ -- [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) @@ -188,6 +190,17 @@ const app = new MyOAuthApp({ clientId, clientSecret }); Sets the default value for app.getWebFlowAuthorizationUrl(options). + + + redirectUrl + + + string + + + The URL in your application where users will be sent after authorization. See Redirect URLs in GitHub’s Developer Guide. + + defaultScopes diff --git a/src/index.ts b/src/index.ts index 353d4de5b..131e56f51 100644 --- a/src/index.ts +++ b/src/index.ts @@ -112,6 +112,7 @@ export class OAuthApp< defaultScopes: options.defaultScopes || [], allowSignup: options.allowSignup, baseUrl: options.baseUrl, + redirectUrl: options.redirectUrl, log: options.log, Octokit, octokit, diff --git a/src/methods/get-web-flow-authorization-url.ts b/src/methods/get-web-flow-authorization-url.ts index b103a420e..d0dd4a324 100644 --- a/src/methods/get-web-flow-authorization-url.ts +++ b/src/methods/get-web-flow-authorization-url.ts @@ -38,6 +38,7 @@ export function getWebFlowAuthorizationUrlWithState( request: state.octokit.request, ...options, allowSignup, + redirectUrl: options.redirectUrl || state.redirectUrl, scopes: options.scopes || state.defaultScopes, }; diff --git a/src/types.ts b/src/types.ts index b31d3b275..8cf2e1855 100644 --- a/src/types.ts +++ b/src/types.ts @@ -36,6 +36,7 @@ type CommonOptions = { clientSecret?: ClientSecret; allowSignup?: boolean; baseUrl?: string; + redirectUrl?: string; log?: typeof console; Octokit?: TOctokit; }; @@ -82,6 +83,7 @@ export type State = { defaultScopes: Scope[]; allowSignup?: boolean; baseUrl?: string; + redirectUrl?: string; log?: typeof console; Octokit: OAuthAppOctokitClassType; octokit: OctokitInstance; diff --git a/test/node-middleware.test.ts b/test/node-middleware.test.ts index d36493a00..0d278ceb0 100644 --- a/test/node-middleware.test.ts +++ b/test/node-middleware.test.ts @@ -1080,4 +1080,71 @@ describe("createNodeMiddleware(app)", () => { expect(url.searchParams.get("state")).toMatch(/^\w+$/); expect(url.searchParams.get("allow_signup")).toEqual("true"); }); + + it("GET /api/github/oauth/login?redirectUrl=http://localhost:12345 with redirectUrl option not set", async () => { + const app = new OAuthApp({ + clientId: "0123", + clientSecret: "0123secret", + }); + + const server = createServer(createNodeMiddleware(app)).listen(); + // @ts-expect-error complains about { port } although it's included in returned AddressInfo interface + const { port } = server.address(); + + const { status, headers } = await fetch( + `http://localhost:${port}/api/github/oauth/login?redirectUrl=http://localhost:12345`, + { + redirect: "manual", + } + ); + + server.close(); + + expect(status).toEqual(302); + + const url = new URL(headers.get("location") as string); + expect(url).toMatchObject({ + origin: "https://github.com", + pathname: "/login/oauth/authorize", + }); + expect(url.searchParams.get("client_id")).toEqual("0123"); + expect(url.searchParams.get("state")).toMatch(/^\w+$/); + expect(url.searchParams.get("redirect_uri")).toEqual( + "http://localhost:12345" + ); + }); + + it("GET /api/github/oauth/login with redirectUrl option set to http://localhost:1234", async () => { + const app = new OAuthApp({ + clientId: "0123", + clientSecret: "0123secret", + redirectUrl: "http://localhost:12345", + }); + + const server = createServer(createNodeMiddleware(app)).listen(); + // @ts-expect-error complains about { port } although it's included in returned AddressInfo interface + const { port } = server.address(); + + const { status, headers } = await fetch( + `http://localhost:${port}/api/github/oauth/login`, + { + redirect: "manual", + } + ); + + server.close(); + + expect(status).toEqual(302); + + const url = new URL(headers.get("location") as string); + expect(url).toMatchObject({ + origin: "https://github.com", + pathname: "/login/oauth/authorize", + }); + expect(url.searchParams.get("client_id")).toEqual("0123"); + expect(url.searchParams.get("state")).toMatch(/^\w+$/); + expect(url.searchParams.get("redirect_uri")).toEqual( + "http://localhost:12345" + ); + }); });