diff --git a/src/exchange-device-code.ts b/src/exchange-device-code.ts index 373ab1e..ea51e1d 100644 --- a/src/exchange-device-code.ts +++ b/src/exchange-device-code.ts @@ -18,6 +18,7 @@ export type ExchangeDeviceCodeOAuthAppOptionsWithoutClientSecret = { redirectUrl?: string; state?: string; request?: RequestInterface; + scopes?: string[]; }; export type ExchangeDeviceCodeOAuthAppOptions = ExchangeDeviceCodeOAuthAppOptionsWithoutClientSecret & { clientSecret: string; @@ -128,7 +129,7 @@ export async function exchangeDeviceCode( clientType: options.clientType, clientId: options.clientId, token: response.data.access_token, - scopes: response.data.scope.split(/,\s*/).filter(Boolean), + scopes: response.data.scope.split(/\s+/).filter(Boolean), }; if ("clientSecret" in options) { diff --git a/src/exchange-web-flow-code.ts b/src/exchange-web-flow-code.ts index cebf01e..79ee233 100644 --- a/src/exchange-web-flow-code.ts +++ b/src/exchange-web-flow-code.ts @@ -83,7 +83,7 @@ export async function exchangeWebFlowCode( clientId: options.clientId, clientSecret: options.clientSecret, token: response.data.access_token, - scopes: response.data.scope.split(/,\s*/).filter(Boolean), + scopes: response.data.scope.split(/\s+/).filter(Boolean), }; if (options.clientType === "github-app") { diff --git a/test/exchange-device-code.test.ts b/test/exchange-device-code.test.ts index 67e5e24..971d3bf 100644 --- a/test/exchange-device-code.test.ts +++ b/test/exchange-device-code.test.ts @@ -56,6 +56,63 @@ describe("exchangeDeviceCode()", () => { `); }); + it("with scopes", async () => { + const mock = fetchMock.sandbox().postOnce( + "https://github.com/login/oauth/access_token", + { + access_token: "secret123", + scope: "repo gist", + token_type: "bearer", + }, + { + headers: { + accept: "application/json", + "user-agent": "test", + "content-type": "application/json; charset=utf-8", + }, + body: { + client_id: "1234567890abcdef1234", + device_code: "code123", + grant_type: "urn:ietf:params:oauth:grant-type:device_code", + }, + } + ); + + const { data, authentication } = await exchangeDeviceCode({ + clientType: "oauth-app", + clientId: "1234567890abcdef1234", + code: "code123", + scopes: ["repo", "gist"], + request: request.defaults({ + headers: { + "user-agent": "test", + }, + request: { + fetch: mock, + }, + }), + }); + + expect(data).toMatchInlineSnapshot(` + Object { + "access_token": "secret123", + "scope": "repo gist", + "token_type": "bearer", + } + `); + expect(authentication).toMatchInlineSnapshot(` + Object { + "clientId": "1234567890abcdef1234", + "clientType": "oauth-app", + "scopes": Array [ + "repo", + "gist", + ], + "token": "secret123", + } + `); + }); + it("authorization_pending error", async () => { const mock = fetchMock.sandbox().postOnce( "https://github.com/login/oauth/access_token", diff --git a/test/exchange-web-flow-code.test.ts b/test/exchange-web-flow-code.test.ts index 6f32ae1..6544925 100644 --- a/test/exchange-web-flow-code.test.ts +++ b/test/exchange-web-flow-code.test.ts @@ -60,6 +60,66 @@ describe("exchangeWebFlowCode()", () => { `); }); + it("with scopes", async () => { + const mock = fetchMock.sandbox().postOnce( + "https://github.com/login/oauth/access_token", + { + access_token: "secret123", + scope: "repo gist", + token_type: "bearer", + }, + { + headers: { + accept: "application/json", + "user-agent": "test", + "content-type": "application/json; charset=utf-8", + }, + body: { + client_id: "1234567890abcdef1234", + client_secret: "1234567890abcdef12347890abcdef12345678", + code: "code123", + state: "state123", + }, + } + ); + + const { data, authentication } = await exchangeWebFlowCode({ + clientType: "oauth-app", + clientId: "1234567890abcdef1234", + clientSecret: "1234567890abcdef12347890abcdef12345678", + code: "code123", + state: "state123", + request: request.defaults({ + headers: { + "user-agent": "test", + }, + request: { + fetch: mock, + }, + }), + }); + + expect(data).toMatchInlineSnapshot(` + Object { + "access_token": "secret123", + "scope": "repo gist", + "token_type": "bearer", + } + `); + expect(authentication).toMatchInlineSnapshot(` + Object { + "clientId": "1234567890abcdef1234", + "clientSecret": "1234567890abcdef12347890abcdef12345678", + "clientType": "oauth-app", + "scopes": Array [ + "repo", + "gist", + ], + "token": "secret123", + } + `); + }); + it("All options for OAuth Apps", async () => { const mock = fetchMock.sandbox().postOnce( "https://ghe.acme-inc.com/login/oauth/access_token",