Skip to content

Commit

Permalink
feat: auth({ type: "oauth-app" })
Browse files Browse the repository at this point in the history
  • Loading branch information
gr2m committed Mar 24, 2021
1 parent 78b42f9 commit ac7eb5b
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 25 deletions.
14 changes: 9 additions & 5 deletions src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ export async function auth(
state: State,
options: AuthOptions
): Promise<Authentication> {
const { type } = options;

switch (type) {
switch (options.type) {
case "app":
return getAppAuthentication(state);
case "oauth-app":
return state.oauthApp({ type: "oauth-app" });
case "installation":
return getInstallationAuthentication(state, options);
return getInstallationAuthentication(state, {
...options,
// TypeScript is making us do this
type: "installation",
});
case "oauth":
state.log.warn(
// @ts-expect-error
Expand All @@ -26,6 +30,6 @@ export async function auth(
case "oauth-user":
return getOAuthAuthentication(state, options);
default:
throw new Error(`Invalid auth type: ${type}`);
throw new Error(`Invalid auth type: ${options.type}`);
}
}
9 changes: 6 additions & 3 deletions src/get-installation-authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ export async function getInstallationAuthentication(
}

if (options.factory) {
const { type, factory, ...factoryAuthOptions } = options;
// @ts-ignore if `options.factory` is set, the return type for `auth()` should be `Promise<ReturnType<options.factory>>`
return factory(Object.assign({}, state, factoryAuthOptions));
const { type, factory, oauthApp, ...factoryAuthOptions } = {
...state,
...options,
};
// @ts-expect-error if `options.factory` is set, the return type for `auth()` should be `Promise<ReturnType<options.factory>>`
return factory(factoryAuthOptions);
}

const optionsWithInstallationTokenFromState = Object.assign(
Expand Down
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getUserAgent } from "universal-user-agent";
import { request } from "@octokit/request";
import { createOAuthAppAuth } from "@octokit/auth-oauth-app";

import { auth } from "./auth";
import { hook } from "./hook";
Expand Down Expand Up @@ -34,6 +35,11 @@ export const createAppAuth: StrategyInterface = function createAppAuth(
: {},
{
log,
oauthApp: createOAuthAppAuth({
clientType: "github-app",
clientId: options.clientId || "",
clientSecret: options.clientSecret || "",
}),
}
);

Expand Down
19 changes: 15 additions & 4 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import * as OctokitTypes from "@octokit/types";
import LRUCache from "lru-cache";
import {
GitHubAuthInterface,
AppAuthentication as OAuthAppAuthentication,
} from "@octokit/auth-oauth-app";

export type AnyResponse = OctokitTypes.OctokitResponse<any>;
export type EndpointDefaults = OctokitTypes.EndpointDefaults;
Expand Down Expand Up @@ -69,6 +73,7 @@ export type OAuthAccesTokenAuthentication = {

export type Authentication =
| AppAuthentication
| OAuthAppAuthentication
| InstallationAccessTokenAuthentication
| OAuthAccesTokenAuthentication;

Expand Down Expand Up @@ -99,7 +104,7 @@ export type Permissions = {
[name: string]: string;
};

export type InstallationAuthOptions = {
type CommonAuthOptions = {
installationId?: number | string;
repositoryIds?: number[];
permissions?: Permissions;
Expand All @@ -110,15 +115,19 @@ export type InstallationAuthOptions = {
[key: string]: unknown;
};

export type InstallationAuthOptions = CommonAuthOptions & {
type: "installation";
};

export type OAuthOptions = {
code?: string;
redirectUrl?: string;
state?: string;
};

export type AuthOptions = InstallationAuthOptions &
export type AuthOptions = CommonAuthOptions &
OAuthOptions & {
type: "app" | "installation" | "oauth" | "oauth-user";
type: "app" | "installation" | "oauth" | "oauth-app" | "oauth-user";
};

export type WithInstallationId = {
Expand All @@ -127,4 +136,6 @@ export type WithInstallationId = {

export type State = Required<Omit<CommonStrategyOptions, "installationId">> & {
installationId?: number;
} & OAuthStrategyOptions;
} & OAuthStrategyOptions & {
oauthApp: GitHubAuthInterface;
};
51 changes: 38 additions & 13 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,29 @@ test("README example for app auth", async () => {
});
});

test("README example for OAuth app auth", async () => {
const auth = createAppAuth({
appId: APP_ID,
privateKey: PRIVATE_KEY,
clientId: "lv1.1234567890abcdef",
clientSecret: "1234567890abcdef1234567890abcdef12345678",
});

const authentication = await auth({ type: "oauth-app" });

expect(authentication).toMatchInlineSnapshot(`
Object {
"clientId": "lv1.1234567890abcdef",
"clientSecret": "1234567890abcdef1234567890abcdef12345678",
"clientType": "github-app",
"headers": Object {
"authorization": "basic bHYxLjEyMzQ1Njc4OTBhYmNkZWY6MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3OA==",
},
"type": "oauth-app",
}
`);
});

test("throws if invalid 'type' is provided", async () => {
const auth = createAppAuth({
appId: APP_ID,
Expand Down Expand Up @@ -149,8 +172,8 @@ test("README example for oauth", async () => {
"content-type": "application/json; charset=utf-8",
});
expect(JSON.parse(String(body))).toStrictEqual({
client_id: "12345678901234567890",
client_secret: "1234567890123456789012345678901234567890",
client_id: "lv1.1234567890abcdef",
client_secret: "1234567890abcdef1234567890abcdef12345678",
code: "123456",
});
return true;
Expand All @@ -165,8 +188,8 @@ test("README example for oauth", async () => {
const auth = createAppAuth({
appId: APP_ID,
privateKey: PRIVATE_KEY,
clientId: "12345678901234567890",
clientSecret: "1234567890123456789012345678901234567890",
clientId: "lv1.1234567890abcdef",
clientSecret: "1234567890abcdef1234567890abcdef12345678",
request: request.defaults({
headers: {
"user-agent": "test",
Expand Down Expand Up @@ -720,8 +743,8 @@ test("oauth with `code`, `redirectUrl` and `state`", async () => {
"content-type": "application/json; charset=utf-8",
});
expect(JSON.parse(String(body))).toStrictEqual({
client_id: "12345678901234567890",
client_secret: "1234567890123456789012345678901234567890",
client_id: "lv1.1234567890abcdef",
client_secret: "1234567890abcdef1234567890abcdef12345678",
code: "123456",
redirect_uri: "https://example.com/login",
state: "mystate123",
Expand All @@ -739,8 +762,8 @@ test("oauth with `code`, `redirectUrl` and `state`", async () => {
const auth = createAppAuth({
appId: APP_ID,
privateKey: PRIVATE_KEY,
clientId: "12345678901234567890",
clientSecret: "1234567890123456789012345678901234567890",
clientId: "lv1.1234567890abcdef",
clientSecret: "1234567890abcdef1234567890abcdef12345678",
request: request.defaults({
headers: {
"user-agent": "test",
Expand Down Expand Up @@ -780,8 +803,8 @@ test("oauth with custom baseUrl (GHE)", async () => {
const auth = createAppAuth({
appId: APP_ID,
privateKey: PRIVATE_KEY,
clientId: "12345678901234567890",
clientSecret: "1234567890123456789012345678901234567890",
clientId: "lv1.1234567890abcdef",
clientSecret: "1234567890abcdef1234567890abcdef12345678",
request: request.defaults({
baseUrl: "https://github.acme-inc.com/api/v3",
headers: {
Expand Down Expand Up @@ -1713,15 +1736,15 @@ test("oauth endpoint error", async () => {
const auth = createAppAuth({
appId: APP_ID,
privateKey: PRIVATE_KEY,
clientId: "12345678901234567890",
clientSecret: "1234567890123456789012345678901234567890",
clientId: "lv1.1234567890abcdef",
clientSecret: "1234567890abcdef1234567890abcdef12345678",
request: requestMock,
});

await expect(
auth({
type: "oauth-user",
code: "12345678901234567890",
code: "lv1.1234567890abcdef",
redirectUrl: "https://example.com/login",
})
).rejects.toThrow("client_id");
Expand Down Expand Up @@ -1849,6 +1872,7 @@ test("id and installationId can be passed as options", async () => {
installationId: "123",
});

// @ts-expect-error TBD
expect(authentication.token).toEqual("secret123");
});

Expand Down Expand Up @@ -1929,6 +1953,7 @@ test("factory auth option", async () => {
factory,
});

// @ts-expect-error TBD
expect(customAuth.token).toStrictEqual("secret");

const factoryOptions = factory.mock.calls[0][0];
Expand Down

0 comments on commit ac7eb5b

Please sign in to comment.