Skip to content

Commit 1fc4039

Browse files
authored
fix: split scopes correctly into separate strings in exchangeWebFlowCode() and exchangeWebFlowCode() (#6)
1 parent c4768a3 commit 1fc4039

File tree

4 files changed

+120
-2
lines changed

4 files changed

+120
-2
lines changed

src/exchange-device-code.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export type ExchangeDeviceCodeOAuthAppOptionsWithoutClientSecret = {
1818
redirectUrl?: string;
1919
state?: string;
2020
request?: RequestInterface;
21+
scopes?: string[];
2122
};
2223
export type ExchangeDeviceCodeOAuthAppOptions = ExchangeDeviceCodeOAuthAppOptionsWithoutClientSecret & {
2324
clientSecret: string;
@@ -128,7 +129,7 @@ export async function exchangeDeviceCode(
128129
clientType: options.clientType,
129130
clientId: options.clientId,
130131
token: response.data.access_token,
131-
scopes: response.data.scope.split(/,\s*/).filter(Boolean),
132+
scopes: response.data.scope.split(/\s+/).filter(Boolean),
132133
};
133134

134135
if ("clientSecret" in options) {

src/exchange-web-flow-code.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export async function exchangeWebFlowCode(
8383
clientId: options.clientId,
8484
clientSecret: options.clientSecret,
8585
token: response.data.access_token,
86-
scopes: response.data.scope.split(/,\s*/).filter(Boolean),
86+
scopes: response.data.scope.split(/\s+/).filter(Boolean),
8787
};
8888

8989
if (options.clientType === "github-app") {

test/exchange-device-code.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,63 @@ describe("exchangeDeviceCode()", () => {
5656
`);
5757
});
5858

59+
it("with scopes", async () => {
60+
const mock = fetchMock.sandbox().postOnce(
61+
"https://github.com/login/oauth/access_token",
62+
{
63+
access_token: "secret123",
64+
scope: "repo gist",
65+
token_type: "bearer",
66+
},
67+
{
68+
headers: {
69+
accept: "application/json",
70+
"user-agent": "test",
71+
"content-type": "application/json; charset=utf-8",
72+
},
73+
body: {
74+
client_id: "1234567890abcdef1234",
75+
device_code: "code123",
76+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
77+
},
78+
}
79+
);
80+
81+
const { data, authentication } = await exchangeDeviceCode({
82+
clientType: "oauth-app",
83+
clientId: "1234567890abcdef1234",
84+
code: "code123",
85+
scopes: ["repo", "gist"],
86+
request: request.defaults({
87+
headers: {
88+
"user-agent": "test",
89+
},
90+
request: {
91+
fetch: mock,
92+
},
93+
}),
94+
});
95+
96+
expect(data).toMatchInlineSnapshot(`
97+
Object {
98+
"access_token": "secret123",
99+
"scope": "repo gist",
100+
"token_type": "bearer",
101+
}
102+
`);
103+
expect(authentication).toMatchInlineSnapshot(`
104+
Object {
105+
"clientId": "1234567890abcdef1234",
106+
"clientType": "oauth-app",
107+
"scopes": Array [
108+
"repo",
109+
"gist",
110+
],
111+
"token": "secret123",
112+
}
113+
`);
114+
});
115+
59116
it("authorization_pending error", async () => {
60117
const mock = fetchMock.sandbox().postOnce(
61118
"https://github.com/login/oauth/access_token",

test/exchange-web-flow-code.test.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,66 @@ describe("exchangeWebFlowCode()", () => {
6060
`);
6161
});
6262

63+
it("with scopes", async () => {
64+
const mock = fetchMock.sandbox().postOnce(
65+
"https://github.com/login/oauth/access_token",
66+
{
67+
access_token: "secret123",
68+
scope: "repo gist",
69+
token_type: "bearer",
70+
},
71+
{
72+
headers: {
73+
accept: "application/json",
74+
"user-agent": "test",
75+
"content-type": "application/json; charset=utf-8",
76+
},
77+
body: {
78+
client_id: "1234567890abcdef1234",
79+
client_secret: "1234567890abcdef12347890abcdef12345678",
80+
code: "code123",
81+
state: "state123",
82+
},
83+
}
84+
);
85+
86+
const { data, authentication } = await exchangeWebFlowCode({
87+
clientType: "oauth-app",
88+
clientId: "1234567890abcdef1234",
89+
clientSecret: "1234567890abcdef12347890abcdef12345678",
90+
code: "code123",
91+
state: "state123",
92+
request: request.defaults({
93+
headers: {
94+
"user-agent": "test",
95+
},
96+
request: {
97+
fetch: mock,
98+
},
99+
}),
100+
});
101+
102+
expect(data).toMatchInlineSnapshot(`
103+
Object {
104+
"access_token": "secret123",
105+
"scope": "repo gist",
106+
"token_type": "bearer",
107+
}
108+
`);
109+
expect(authentication).toMatchInlineSnapshot(`
110+
Object {
111+
"clientId": "1234567890abcdef1234",
112+
"clientSecret": "1234567890abcdef12347890abcdef12345678",
113+
"clientType": "oauth-app",
114+
"scopes": Array [
115+
"repo",
116+
"gist",
117+
],
118+
"token": "secret123",
119+
}
120+
`);
121+
});
122+
63123
it("All options for OAuth Apps", async () => {
64124
const mock = fetchMock.sandbox().postOnce(
65125
"https://ghe.acme-inc.com/login/oauth/access_token",

0 commit comments

Comments
 (0)