以下のサンプルアプリの実装を見てみる。
サンプルアプリにおける GitHub App の認証方法がなんなのか確認
サンプルアプリにおける、以下の App の実装を追ってみる
https://github.com/github/github-app-js-sample/blob/0c35a78328d103ffd8d4e6a568569d6c3237977b/app.js#L18-L30
// Create an authenticated Octokit client authenticated as a GitHub App
const app = new App({
appId,
privateKey,
webhooks: {
secret
},
...(enterpriseHostname && {
Octokit: Octokit.defaults({
baseUrl: `https://${enterpriseHostname}/api/v3`
})
})
})Appクラスは octokit モジュールからimport されたもの https://github.com/github/github-app-js-sample/blob/0c35a78328d103ffd8d4e6a568569d6c3237977b/app.js#L4
import { Octokit, App } from 'octokit'octokit モジュールを見てみる。
Appクラスがexportされているのは以下。app.jsの App を exportしている。 https://github.com/octokit/octokit.js/blob/910bc2d83a823d5decb3eeef0e56f5e5e1bba361/src/index.ts#L3
export { App, OAuthApp, createNodeMiddleware } from "./app.js";app.js の詳細も見てみる。
app.js では、@octokit/app の App を DefaultApp として import し、DefaultApp.defaults()の返り値を App として export していた https://github.com/octokit/octokit.js/blob/910bc2d83a823d5decb3eeef0e56f5e5e1bba361/src/app.ts#L1-L6
import { App as DefaultApp } from "@octokit/app";
import { OAuthApp as DefaultOAuthApp } from "@octokit/oauth-app";
import { Octokit } from "./octokit.js";
export const App = DefaultApp.defaults({ Octokit });⇒defaults メソッドの引数: { Octokit } はオブジェクト省略記法(ES6)により{ Octokit: Octokit }と同義
DefaultApp.defaults({ Octokit: Octokit }) の実装を確認するため、@octokit/appで export された App の defaultsメソッドを見てみる
@octokit/appから export される App の実装は以下 https://github.com/octokit/app.js/blob/5bb92eeb59cf9240475ecc811480559ca202347f/src/index.ts#L44C1-L164
defaults メソッドの実装は以下 https://github.com/octokit/app.js/blob/5bb92eeb59cf9240475ecc811480559ca202347f/src/index.ts#L47-L58
static defaults<
TDefaults extends Options,
S extends Constructor<App<TDefaults>>,
>(this: S, defaults: Partial<TDefaults>) {
const AppWithDefaults = class extends this {
constructor(...args: any[]) {
super({
...defaults,
...args[0],
});
}
};⇒github-app-js-sample/app.js の内容をふまえると、以下の内容(①)が App のコンストラクタに渡される
super({
...defaults, // { Octokit: Octokit }
...args[0], // { appId, privateKey, webhooks: { secret } }
});
App のコンストラクタは以下 https://github.com/octokit/app.js/blob/5bb92eeb59cf9240475ecc811480559ca202347f/src/index.ts#L85-L163
constructor(options: ConstructorOptions<TOptions>) {
⇒コンストラクタの引数(①)が options に渡される
コンストラクタ内の処理について、認証の観点から確認。
以下の実装が関係してきそう
constructor(options: ConstructorOptions<TOptions>) {
const Octokit = (options.Octokit ||
OctokitCore) as OctokitClassType<TOptions>;
const authOptions = Object.assign(
{
appId: options.appId,
privateKey: options.privateKey,
},
options.oauth
? {
clientId: options.oauth.clientId,
clientSecret: options.oauth.clientSecret,
}
: {},
);
const octokitOptions: OctokitOptions = {
authStrategy: createAppAuth,
auth: authOptions,
};
if ("log" in options && typeof options.log !== "undefined") {
octokitOptions.log = options.log;
}
this.octokit = new Octokit(octokitOptions) as OctokitType<TOptions>;⇒変数: authOptionsにoptionsの内容をassign コンストラクタの引数(①)のappId, privateKeyなどがassign
⇒OctokitOptions タイプの変数: octokitOptions に以下が設定される authStrategy プロパティにcreateAppAuthを設定 auth プロパティに authOptions 変数を設定 ⇒変数: octokitOptions をOctokitクラスのコンストラクタに渡し、インスタンス化してthis.octokitに格納
Octokitクラスのコンストラクタを確認。 Octokit クラスは@octokit/coreからimportされている https://github.com/octokit/app.js/blob/5bb92eeb59cf9240475ecc811480559ca202347f/src/index.ts#L1C62-L1C76
import { Octokit as OctokitCore, type OctokitOptions } from "@octokit/core";Octokitクラスの実装。 https://github.com/octokit/core.js/blob/main/src/index.ts
Octokitクラスのコンストラクトは OctokitOptions タイプのオブジェクトを受け取り、options 変数に格納される https://github.com/octokit/core.js/blob/b1b81171507fa4a025e08bc5f5d84cf90572c2c2/src/index.ts#L109-L125
constructor(options: OctokitOptions = {}) {
コンストラクト内では、options の内容をもとに行われる処理についてコメントで説明されている https://github.com/octokit/core.js/blob/b1b81171507fa4a025e08bc5f5d84cf90572c2c2/src/index.ts#L149-L188
// (1) If neither `options.authStrategy` nor `options.auth` are set, the `octokit` instance
// is unauthenticated. The `this.auth()` method is a no-op and no request hook is registered.
// (2) If only `options.auth` is set, use the default token authentication strategy.
// (3) If `options.authStrategy` is set then use it and pass in `options.auth`. Always pass own request as many strategies accept a custom request instance.
// TODO: type `options.auth` based on `options.authStrategy`.
if (!options.authStrategy) {
if (!options.auth) {
// (1)
this.auth = async () => ({
type: "unauthenticated",
});
} else {
// (2)
const auth = createTokenAuth(options.auth as string);
// @ts-ignore ¯\_(ツ)_/¯
hook.wrap("request", auth.hook);
this.auth = auth;
}
} else {
const { authStrategy, ...otherOptions } = options;
const auth = authStrategy(
Object.assign(
{
request: this.request,
log: this.log,
// we pass the current octokit instance as well as its constructor options
// to allow for authentication strategies that return a new octokit instance
// that shares the same internal state as the current one. The original
// requirement for this was the "event-octokit" authentication strategy
// of https://github.com/probot/octokit-auth-probot.
octokit: this,
octokitOptions: otherOptions,
},
options.auth,
),
);
// @ts-ignore ¯\_(ツ)_/¯
hook.wrap("request", auth.hook);
this.auth = auth;
}
⇒コメント部分
- //(1) options.AuthStrategy と options.auth のどちらも設定されていない場合、octokitインスタンスは認証されません。this.auth () メソッドは何もしませんし、リクエストフックも登録されていません。
- //(2) options.authだけが設定されている場合は、デフォルトのトークン認証方法を使用してください。
- //(3)「Options.AuthStrategy」が設定されている場合は、それを使用して「options.auth」を渡してください。多くのストラテジーはカスタムリクエストインスタンスを受け入れるので、常に自分のリクエストを渡してください。
⇒ authStrategy には createAppAuth が渡されていたので、154行目のif (!options.authStrategy) {には入らず、167行目のelseに入りそう
⇒167行目のelse中では、分割代入によりauthStrategyメソッドとotherOptios に分けられ、authStrategyメソッドが実行されそう https://github.com/octokit/core.js/blob/b1b81171507fa4a025e08bc5f5d84cf90572c2c2/src/index.ts#L168-L185
const { authStrategy, ...otherOptions } = options;
const auth = authStrategy(
Object.assign(
{
request: this.request,
log: this.log,
// we pass the current octokit instance as well as its constructor options
// to allow for authentication strategies that return a new octokit instance
// that shares the same internal state as the current one. The original
// requirement for this was the "event-octokit" authentication strategy
// of https://github.com/probot/octokit-auth-probot.
octokit: this,
octokitOptions: otherOptions,
},
options.auth,
),
);
// @ts-ignore ¯\_(ツ)_/¯
⇒authStrategyメソッドには options.authが渡されそう
これは遡るとgithub-app-js-sample/app.jsのAppコンストラクトに渡されていた以下の内容に相当
https://github.com/github/github-app-js-sample/blob/0c35a78328d103ffd8d4e6a568569d6c3237977b/app.js#L19-L24
const app = new App({
appId,
privateKey,
webhooks: {
secret
},
つまり、authStrategyメソッドに対して、上記のようなappId, privateKey, webhooksが渡されることになる。
ここでの authStrategy とは?
authStrategy には createAppAuth が渡されていた。
認証の方法はcreateAppAuthで決定されそう。ここの詳細を追ってみる
createAppAuthは@octokit/auth-appからimprtされてきたもの https://github.com/octokit/app.js/blob/5bb92eeb59cf9240475ecc811480559ca202347f/src/index.ts#L2C32-L2C49
import { createAppAuth } from "@octokit/auth-app";createAppAuthの詳細を見てみる https://github.com/octokit/auth-app.js/blob/4486c10028249f4f2ffaabeafde986edca92e7b5/src/index.ts#L30C17-L30C30
export function createAppAuth(options: StrategyOptions): AuthInterface {⇒createAppAuth はメソッド
また、利用する認証方法ごとに createAppAuth メソッドのプロパティをどう指定すれば良いかについてREADME.mdで説明されている。 https://github.com/octokit/auth-app.js/?tab=readme-ov-file#standalone-usage
結局よくわからず