Skip to content

Commit

Permalink
feat: support new token formats and user-to-server token type (#200)
Browse files Browse the repository at this point in the history
  • Loading branch information
gr2m committed Sep 16, 2021
1 parent d7fc507 commit 43a6d24
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 26 deletions.
39 changes: 29 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ const { createTokenAuth } = require("@octokit/auth-token");
</table>

```js
const auth = createTokenAuth("1234567890abcdef1234567890abcdef12345678");
const auth = createTokenAuth("ghp_PersonalAccessToken01245678900000000");
const authentication = await auth();
// {
// type: 'token',
// token: '1234567890abcdef1234567890abcdef12345678',
// token: 'ghp_PersonalAccessToken01245678900000000',
// tokenType: 'oauth'
// }
```
Expand All @@ -73,17 +73,36 @@ The `createTokenAuth` method accepts a single argument of type string, which is

- [Personal access token](https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line)
- [OAuth access token](https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/)
- Installation access token ([GitHub App Installation](https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-an-installation))
- [GITHUB_TOKEN provided to GitHub Actions](https://developer.github.com/actions/creating-github-actions/accessing-the-runtime-environment/#environment-variables)
- Installation access token ([server-to-server](https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-an-installation))
- User authentication for installation ([user-to-server](https://docs.github.com/en/developers/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps))

Examples

```js
// Personal access token or OAuth access token
createTokenAuth("1234567890abcdef1234567890abcdef12345678");
createTokenAuth("ghp_PersonalAccessToken01245678900000000");
// {
// type: 'token',
// token: 'ghp_PersonalAccessToken01245678900000000',
// tokenType: 'oauth'
// }

// Installation access token or GitHub Action token
createTokenAuth("ghs_InstallallationOrActionToken00000000");
// {
// type: 'token',
// token: 'ghs_InstallallationOrActionToken00000000',
// tokenType: 'installation'
// }

// Installation access token or GitHub Action token
createTokenAuth("v1.1234567890abcdef1234567890abcdef12345678");
createTokenAuth("ghu_InstallationUserToServer000000000000");
// {
// type: 'token',
// token: 'ghu_InstallationUserToServer000000000000',
// tokenType: 'user-to-server'
// }
```

## `auth()`
Expand Down Expand Up @@ -137,7 +156,7 @@ The `auth()` method has no options. It returns a promise which resolves with the
<code>string</code>
</th>
<td>
Can be either <code>"oauth"</code> for personal access tokens and OAuth tokens, or <code>"installation"</code> for installation access tokens (includes <code>GITHUB_TOKEN</code> provided to GitHub Actions)
Can be either <code>"oauth"</code> for personal access tokens and OAuth tokens, <code>"installation"</code> for installation access tokens (includes <code>GITHUB_TOKEN</code> provided to GitHub Actions), <code>"app"</code> for a GitHub App JSON Web Token, or <code>"user-to-server"</code> for a user authentication token through an app installation.
</td>
</tr>
</tbody>
Expand Down Expand Up @@ -181,7 +200,7 @@ Here is a list of things you can do to retrieve further information
Note that this does not work for installations. There is no way to retrieve permissions based on an installation access tokens.

```js
const TOKEN = "1234567890abcdef1234567890abcdef12345678";
const TOKEN = "ghp_PersonalAccessToken01245678900000000";

const auth = createTokenAuth(TOKEN);
const authentication = await auth();
Expand All @@ -203,7 +222,7 @@ if (scopes.length) {
### Find out if token is a personal access token or if it belongs to an OAuth app

```js
const TOKEN = "1234567890abcdef1234567890abcdef12345678";
const TOKEN = "ghp_PersonalAccessToken01245678900000000";

const auth = createTokenAuth(TOKEN);
const authentication = await auth();
Expand All @@ -227,7 +246,7 @@ if (clientId) {
Note that the `permissions` key is not set when authenticated using an installation access token.

```js
const TOKEN = "1234567890abcdef1234567890abcdef12345678";
const TOKEN = "ghp_PersonalAccessToken01245678900000000";

const auth = createTokenAuth(TOKEN);
const authentication = await auth();
Expand All @@ -253,7 +272,7 @@ Both OAuth and installation access tokens can be used for git operations. Howeve
This example is using the [`execa`](https://github.com/sindresorhus/execa) package to run a `git push` command.

```js
const TOKEN = "1234567890abcdef1234567890abcdef12345678";
const TOKEN = "ghp_PersonalAccessToken01245678900000000";

const auth = createTokenAuth(TOKEN);
const { token, tokenType } = await auth();
Expand Down
23 changes: 17 additions & 6 deletions src/auth.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import { Token, Authentication } from "./types";

const REGEX_IS_INSTALLATION_LEGACY = /^v1\./;
const REGEX_IS_INSTALLATION = /^ghs_/;
const REGEX_IS_USER_TO_SERVER = /^ghu_/;

export async function auth(token: Token): Promise<Authentication> {
const tokenType =
token.split(/\./).length === 3
? "app"
: /^v\d+\./.test(token)
? "installation"
: "oauth";
const isApp = token.split(/\./).length === 3;
const isInstallation =
REGEX_IS_INSTALLATION_LEGACY.test(token) ||
REGEX_IS_INSTALLATION.test(token);
const isUserToServer = REGEX_IS_USER_TO_SERVER.test(token);

const tokenType = isApp
? "app"
: isInstallation
? "installation"
: isUserToServer
? "user-to-server"
: "oauth";

return {
type: "token",
Expand Down
8 changes: 7 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ export type AppAuthentication = {
tokenType: "app";
token: Token;
};
export type UserToServerAuthentication = {
type: "token";
tokenType: "user-to-server";
token: Token;
};
export type Authentication =
| OAuthTokenAuthentication
| InstallationTokenAuthentication
| AppAuthentication;
| AppAuthentication
| UserToServerAuthentication;
40 changes: 31 additions & 9 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ import fetchMock, { MockMatcherFunction } from "fetch-mock";
import { createTokenAuth } from "../src/index";

test("README example", async () => {
const auth = createTokenAuth("1234567890abcdef1234567890abcdef12345678");
const auth = createTokenAuth("ghp_PersonalAccessToken01245678900000000");
const authentication = await auth();

expect(authentication).toEqual({
type: "token",
token: "1234567890abcdef1234567890abcdef12345678",
token: "ghp_PersonalAccessToken01245678900000000",
tokenType: "oauth",
});
});

test("installation token", async () => {
test("installation token (old format)", async () => {
const auth = createTokenAuth("v1.1234567890abcdef1234567890abcdef12345678");
const authentication = await auth();

Expand All @@ -25,6 +25,17 @@ test("installation token", async () => {
});
});

test("installation token (new format)", async () => {
const auth = createTokenAuth("ghs_InstallallationOrActionToken00000000");
const authentication = await auth();

expect(authentication).toEqual({
type: "token",
token: "ghs_InstallallationOrActionToken00000000",
tokenType: "installation",
});
});

test("JSON Web Token (GitHub App Authentication)", async () => {
const auth = createTokenAuth(
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOi0zMCwiZXhwIjo1NzAsImlzcyI6MX0.q3foRa78U3WegM5PrWLEh5N0bH1SD62OqW66ZYzArp95JBNiCbo8KAlGtiRENCIfBZT9ibDUWy82cI4g3F09mdTq3bD1xLavIfmTksIQCz5EymTWR5v6gL14LSmQdWY9lSqkgUG0XCFljWUglEP39H4yeHbFgdjvAYg3ifDS12z9oQz2ACdSpvxPiTuCC804HkPVw8Qoy0OSXvCkFU70l7VXCVUxnuhHnk8-oCGcKUspmeP6UdDnXk-Aus-eGwDfJbU2WritxxaXw6B4a3flTPojkYLSkPBr6Pi0H2-mBsW_Nvs0aLPVLKobQd4gqTkosX3967DoAG8luUMhrnxe8Q"
Expand All @@ -39,6 +50,17 @@ test("JSON Web Token (GitHub App Authentication)", async () => {
});
});

test("User-to-server token (User authentication through app installation)", async () => {
const auth = createTokenAuth("ghu_InstallationUserToServer000000000000");
const authentication = await auth();

expect(authentication).toEqual({
type: "token",
token: "ghu_InstallationUserToServer000000000000",
tokenType: "user-to-server",
});
});

test("invalid token", async () => {
const auth = createTokenAuth("whatislove");
const authentication = await auth();
Expand Down Expand Up @@ -88,26 +110,26 @@ test("OAuth token with prefix", async () => {

test("JWT with prefix", async () => {
const auth = createTokenAuth(
"token 1234567890abcdef1234567890abcdef12345678"
"token ghp_PersonalAccessToken01245678900000000"
);
const authentication = await auth();

expect(authentication).toEqual({
type: "token",
token: "1234567890abcdef1234567890abcdef12345678",
token: "ghp_PersonalAccessToken01245678900000000",
tokenType: "oauth",
});
});

test("JWT with capitalized prefix", async () => {
const auth = createTokenAuth(
"Token 1234567890abcdef1234567890abcdef12345678"
"Token ghp_PersonalAccessToken01245678900000000"
);
const authentication = await auth();

expect(authentication).toEqual({
type: "token",
token: "1234567890abcdef1234567890abcdef12345678",
token: "ghp_PersonalAccessToken01245678900000000",
tokenType: "oauth",
});
});
Expand All @@ -129,7 +151,7 @@ test("JWT with capitalized prefix", async () => {
test('auth.hook(request, "GET /user")', async () => {
const expectedRequestHeaders = {
accept: "application/vnd.github.v3+json",
authorization: "token 1234567890abcdef1234567890abcdef12345678",
authorization: "token ghp_PersonalAccessToken01245678900000000",
"user-agent": "test",
};

Expand All @@ -148,7 +170,7 @@ test('auth.hook(request, "GET /user")', async () => {
},
});

const { hook } = createTokenAuth("1234567890abcdef1234567890abcdef12345678");
const { hook } = createTokenAuth("ghp_PersonalAccessToken01245678900000000");
const { data } = await hook(requestMock, "GET /user");

expect(data).toStrictEqual({ id: 123 });
Expand Down

0 comments on commit 43a6d24

Please sign in to comment.