Skip to content

Commit

Permalink
feat: "event-octokit" auth: resolve with unauthenticated octokit if "…
Browse files Browse the repository at this point in the history
…installation" key is missing (#5)
  • Loading branch information
gr2m authored Oct 28, 2020
1 parent efcb852 commit 78e2d41
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 3 deletions.
17 changes: 15 additions & 2 deletions src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,24 @@ export async function auth(state: State, options: AuthOptions) {
}

const action = options.event.payload.action;
const installationId = options.event.payload.installation.id;
const installationId =
options.event.payload.installation && options.event.payload.installation.id;
const fullEventName = options.event.name + (action ? "." + action : "");

const OctokitWithEventAuth = (state.octokit
.constructor as unknown) as typeof Octokit;

if (!installationId) {
const { auth, ...octokitOptions } = state.octokitOptions;
return new OctokitWithEventAuth({
authStrategy: createUnauthenticatedAuth,
auth: {
reason: `Handling a "${fullEventName}" event: an "installation" key is missing. The installation ID cannot be determined`,
},
...octokitOptions,
});
}

if (
options.event.name === "installation" &&
["suspend", "deleted"].includes(String(action))
Expand All @@ -42,7 +55,7 @@ export async function auth(state: State, options: AuthOptions) {
return new OctokitWithEventAuth({
authStrategy: createUnauthenticatedAuth,
auth: {
reason: `Handling an installation.${action} event: The app's access has been revoked from @octokit (id: ${installationId})`,
reason: `Handling a "${fullEventName}" event: The app's access has been revoked from @octokit (id: ${installationId})`,
},
...octokitOptions,
});
Expand Down
86 changes: 86 additions & 0 deletions test/auth.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,41 @@
import { Octokit } from "@octokit/core";
import fetchMock from "fetch-mock";

import { createProbotAuth } from "../src";

const ProbotOctokit = Octokit.defaults({
authStrategy: createProbotAuth,
});
const sandbox = fetchMock.sandbox.bind(fetchMock);

const APP_ID = 1;
const PRIVATE_KEY = `-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA1c7+9z5Pad7OejecsQ0bu3aozN3tihPmljnnudb9G3HECdnH
lWu2/a1gB9JW5TBQ+AVpum9Okx7KfqkfBKL9mcHgSL0yWMdjMfNOqNtrQqKlN4kE
p6RD++7sGbzbfZ9arwrlD/HSDAWGdGGJTSOBM6pHehyLmSC3DJoR/CTu0vTGTWXQ
rO64Z8tyXQPtVPb/YXrcUhbBp8i72b9Xky0fD6PkEebOy0Ip58XVAn2UPNlNOSPS
ye+Qjtius0Md4Nie4+X8kwVI2Qjk3dSm0sw/720KJkdVDmrayeljtKBx6AtNQsSX
gzQbeMmiqFFkwrG1+zx6E7H7jqIQ9B6bvWKXGwIDAQABAoIBAD8kBBPL6PPhAqUB
K1r1/gycfDkUCQRP4DbZHt+458JlFHm8QL6VstKzkrp8mYDRhffY0WJnYJL98tr4
4tohsDbqFGwmw2mIaHjl24LuWXyyP4xpAGDpl9IcusjXBxLQLp2m4AKXbWpzb0OL
Ulrfc1ZooPck2uz7xlMIZOtLlOPjLz2DuejVe24JcwwHzrQWKOfA11R/9e50DVse
hnSH/w46Q763y4I0E3BIoUMsolEKzh2ydAAyzkgabGQBUuamZotNfvJoDXeCi1LD
8yNCWyTlYpJZJDDXooBU5EAsCvhN1sSRoaXWrlMSDB7r/E+aQyKua4KONqvmoJuC
21vSKeECgYEA7yW6wBkVoNhgXnk8XSZv3W+Q0xtdVpidJeNGBWnczlZrummt4xw3
xs6zV+rGUDy59yDkKwBKjMMa42Mni7T9Fx8+EKUuhVK3PVQyajoyQqFwT1GORJNz
c/eYQ6VYOCSC8OyZmsBM2p+0D4FF2/abwSPMmy0NgyFLCUFVc3OECpkCgYEA5OAm
I3wt5s+clg18qS7BKR2DuOFWrzNVcHYXhjx8vOSWV033Oy3yvdUBAhu9A1LUqpwy
Ma+unIgxmvmUMQEdyHQMcgBsVs10dR/g2xGjMLcwj6kn+xr3JVIZnbRT50YuPhf+
ns1ScdhP6upo9I0/sRsIuN96Gb65JJx94gQ4k9MCgYBO5V6gA2aMQvZAFLUicgzT
u/vGea+oYv7tQfaW0J8E/6PYwwaX93Y7Q3QNXCoCzJX5fsNnoFf36mIThGHGiHY6
y5bZPPWFDI3hUMa1Hu/35XS85kYOP6sGJjf4kTLyirEcNKJUWH7CXY+00cwvTkOC
S4Iz64Aas8AilIhRZ1m3eQKBgQCUW1s9azQRxgeZGFrzC3R340LL530aCeta/6FW
CQVOJ9nv84DLYohTVqvVowdNDTb+9Epw/JDxtDJ7Y0YU0cVtdxPOHcocJgdUGHrX
ZcJjRIt8w8g/s4X6MhKasBYm9s3owALzCuJjGzUKcDHiO2DKu1xXAb0SzRcTzUCn
7daCswKBgQDOYPZ2JGmhibqKjjLFm0qzpcQ6RPvPK1/7g0NInmjPMebP0K6eSPx0
9/49J6WTD++EajN7FhktUSYxukdWaCocAQJTDNYP0K88G4rtC2IYy5JFn9SWz5oh
x//0u+zd/R/QRUzLOw4N72/Hu+UG6MNt5iDZFCtapRaKt6OvSBwy8w==
-----END RSA PRIVATE KEY-----`;

describe("octokit.auth()", () => {
describe("Token authentication", () => {
Expand Down Expand Up @@ -44,4 +75,59 @@ describe("octokit.auth()", () => {
});
});
});

describe("App authentication", () => {
test(".auth({ type: 'event-octokit', event }) with event.payload.installation missing", async () => {
const mock = sandbox().get("path:/", 404, { repeat: 2 });

const octokit = new ProbotOctokit({
auth: {
appId: APP_ID,
privateKey: PRIVATE_KEY,
},
request: {
fetch: mock,
},
});

const eventOctokit = (await octokit.auth({
type: "event-octokit",
event: {
name: "test",
payload: {
// no "installation" key
},
},
})) as typeof octokit;

try {
await eventOctokit.request("/");
throw new Error("should not resolve");
} catch (error) {
expect(error.message).toEqual(
'Not found. May be due to lack of authentication. Reason: Handling a "test" event: an "installation" key is missing. The installation ID cannot be determined'
);
}

const eventOctokit2 = (await octokit.auth({
type: "event-octokit",
event: {
name: "test",
payload: {
action: "test",
// no "installation" key
},
},
})) as typeof octokit;

try {
await eventOctokit2.request("/");
throw new Error("should not resolve");
} catch (error) {
expect(error.message).toEqual(
'Not found. May be due to lack of authentication. Reason: Handling a "test.test" event: an "installation" key is missing. The installation ID cannot be determined'
);
}
});
});
});
2 changes: 1 addition & 1 deletion test/readme-examples.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ describe("README examples", () => {
throw new Error("request should not resolve");
} catch (error) {
expect(error.message).toEqual(
`Not found. May be due to lack of authentication. Reason: Handling an installation.deleted event: The app's access has been revoked from @octokit (id: 123)`
`Not found. May be due to lack of authentication. Reason: Handling a "installation.deleted" event: The app's access has been revoked from @octokit (id: 123)`
);
}
});
Expand Down

0 comments on commit 78e2d41

Please sign in to comment.