Skip to content

Commit 2f222c1

Browse files
authored
fix: prevent refreshToken from lost after resetToken (#5)
1 parent ae58203 commit 2f222c1

File tree

4 files changed

+81
-14
lines changed

4 files changed

+81
-14
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
runs-on: ubuntu-latest
1212
strategy:
1313
matrix:
14-
node_version: ["12", "14"]
14+
node_version: ["14"]
1515

1616
steps:
1717
- uses: actions/checkout@v2

README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -209,17 +209,17 @@ createOAuthAppAuth({
209209

210210
The async `auth()` method returned by `createOAuthUserClientAuth(options)` accepts the following commands:
211211

212-
| Command | `{type: }` | Optional Arguments |
213-
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- |
214-
| [Sign in](https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#1-request-a-users-github-identity) | `"signIn"` | <ul><li><code>login: "user"</code></li><li><code>allowSignup: false</code></li><li><code>scopes: ["repo"]</code> (only relevant for OAuth Apps)</li></ul> |
215-
| Get (local) token | `"getToken"` ||
216-
| [Create an app token](https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#2-users-are-redirected-back-to-your-site-by-github) | `"createToken"` ||
217-
| [Check a token](https://docs.github.com/en/rest/reference/apps#check-a-token) | `"checkToken"` ||
218-
| [Create a scoped access token](https://docs.github.com/en/rest/reference/apps#create-a-scoped-access-token) (for OAuth App) | `"createScopedToken"` ||
219-
| [Reset a token](https://docs.github.com/en/rest/reference/apps#reset-a-token) | `"resetToken"` ||
220-
| [Renewing a user token with a refresh token](https://docs.github.com/en/developers/apps/building-github-apps/reshing-user-to-server-access-tokens#renewing-a-user-token-with-a-refresh-token) (for GitHub App with token expiration enabled) | `"refreshToken"` ||
221-
| [Delete an app token](https://docs.github.com/en/rest/reference/apps#delete-an-app-token) (sign out) | `"deleteToken"` | `offline: true` (only deletes session from local session store) |
222-
| [Delete an app authorization](https://docs.github.com/en/rest/reference/apps#delete-an-app-authorization) | `"deleteAuthorization"` ||
212+
| Command | `{type: }` | Optional Arguments |
213+
| :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- |
214+
| [Sign in](https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#1-request-a-users-github-identity) | `"signIn"` | <ul><li><code>login: "user"</code></li><li><code>allowSignup: false</code></li><li><code>scopes: ["repo"]</code> (only relevant for OAuth Apps)</li></ul> |
215+
| Get (local) token | `"getToken"` ||
216+
| [Create an app token](https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#2-users-are-redirected-back-to-your-site-by-github) | `"createToken"` ||
217+
| [Check a token](https://docs.github.com/en/rest/reference/apps#check-a-token) | `"checkToken"` ||
218+
| [Create a scoped access token](https://docs.github.com/en/rest/reference/apps#create-a-scoped-access-token) (for OAuth App) | `"createScopedToken"` ||
219+
| [Reset a token](https://docs.github.com/en/rest/reference/apps#reset-a-token) | `"resetToken"` ||
220+
| [Renewing a user token with a refresh token](https://docs.github.com/en/developers/apps/building-github-apps/refreshing-user-to-server-access-tokens#renewing-a-user-token-with-a-refresh-token) (for GitHub App with token expiration enabled) | `"refreshToken"` ||
221+
| [Delete an app token](https://docs.github.com/en/rest/reference/apps#delete-an-app-token) (sign out) | `"deleteToken"` | `offline: true` (only deletes session from local session store) |
222+
| [Delete an app authorization](https://docs.github.com/en/rest/reference/apps#delete-an-app-authorization) | `"deleteAuthorization"` ||
223223

224224
## Session object
225225

src/auth.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,10 @@ export async function auth<
7777
// Auto refresh for user-to-server token.
7878
const expiresAt = this.session.authentication.expiresAt;
7979
if (new Date(expiresAt) > new Date()) return this.session;
80-
// @ts-ignore
81-
return await auth.call(this, { type: "refreshToken" });
80+
return await auth.call(this, { type: "refreshToken" } as Command<
81+
Client,
82+
Expiration
83+
>);
8284
}
8385
}
8486

@@ -118,6 +120,7 @@ export async function auth<
118120
this.session ||= await auth.call(this);
119121
if (!this.session) throw errors.unauthorized;
120122
}
123+
const oldSession = this.session;
121124

122125
// Prepare payload for `refreshToken` command.
123126
if (this.session && "refreshToken" in this.session.authentication) {
@@ -133,6 +136,18 @@ export async function auth<
133136
this.session = response.data || null;
134137
}
135138

139+
// Some `oauth-app.js` endpoints (such as `resetToken`) do not (and can
140+
// not) return `refreshToken`. Original `refreshToken` and
141+
// `refreshTokenExpiresAt` are kept to `refreshToken` later.
142+
if (oldSession && "refreshToken" in oldSession.authentication) {
143+
if (this.session && !("refreshToken" in this.session.authentication))
144+
Object.assign(this.session.authentication, {
145+
refreshToken: oldSession.authentication.refreshToken,
146+
refreshTokenExpiresAt:
147+
oldSession.authentication.refreshTokenExpiresAt,
148+
});
149+
}
150+
136151
if (this.sessionStore) await this.sessionStore.set(this.session);
137152
return this.session;
138153
}

test/standalone.test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,4 +663,56 @@ describe("standalone tests under node environment", () => {
663663
expect(sessionStore.set.mock.calls.length).toEqual(1);
664664
expect(sessionStore.set.mock.calls[0][0]).toBeNull();
665665
});
666+
667+
it("keeps refresh token", async () => {
668+
const oldSession = {
669+
authentication: {
670+
token: "token123",
671+
refreshToken: "refreshToken123",
672+
refreshTokenExpiresAt: "2000-01-03T00:00:00.000Z",
673+
},
674+
};
675+
const newSession = { authentication: { token: "token456" } };
676+
677+
const sessionStore = {
678+
get: jest.fn().mockResolvedValue(oldSession),
679+
set: jest.fn().mockResolvedValue(undefined),
680+
};
681+
682+
const fetch = fetchMock
683+
.sandbox()
684+
.patchOnce("http://acme.com/api/github/oauth/token", newSession, {
685+
headers: {
686+
accept: "application/vnd.github.v3+json",
687+
"user-agent": "test",
688+
authorization: "token token123",
689+
},
690+
});
691+
692+
const auth = createOAuthUserClientAuth({
693+
clientId: "clientId123",
694+
sessionStore,
695+
request: request.defaults({
696+
headers: { "user-agent": "test" },
697+
request: { fetch },
698+
}),
699+
});
700+
701+
expect(await auth({ type: "resetToken" })).toEqual({
702+
authentication: {
703+
token: "token456",
704+
refreshToken: "refreshToken123",
705+
refreshTokenExpiresAt: "2000-01-03T00:00:00.000Z",
706+
},
707+
});
708+
expect(sessionStore.get.mock.calls.length).toBe(1);
709+
expect(sessionStore.set.mock.calls.length).toBe(1);
710+
expect(await auth()).toEqual({
711+
authentication: {
712+
token: "token456",
713+
refreshToken: "refreshToken123",
714+
refreshTokenExpiresAt: "2000-01-03T00:00:00.000Z",
715+
},
716+
});
717+
});
666718
});

0 commit comments

Comments
 (0)