feat(misskey-js): MiAuth認証機能

This commit is contained in:
kakkokari-gtyih 2024-05-02 16:38:37 +09:00
parent c530a46e54
commit fe0ace51c7
6 changed files with 128 additions and 7 deletions

View File

@ -37,8 +37,42 @@ import * as Misskey from 'misskey-js';
import { api as misskeyApi } from 'misskey-js'; import { api as misskeyApi } from 'misskey-js';
``` ```
## Authenticate ## Authenticate (MiAuth)
todo MiAuthでの認証に対応しています。
### Step 1: 認証URLを生成
`APIClient`クラスの`getMiAuthURL`メソッドを使用して認証URLを生成しますこれは同期関数です。生成したURLにユーザーを誘導し、認可させてください。
``` ts
const cli = new Misskey.api.APIClient({
origin: 'https://misskey.test',
});
const { url } = cli.getMiAuthURL({
name: 'My app',
callback: 'https://example.com/callback',
permission: ['read:account'],
});
// URLに飛ばす
location.href = url;
```
### Step 2: セッションIDからアクセストークンを取得
`APIClient`クラスの`authWithMiAuth`メソッドを使用してセッションIDからアクセストークンを取得します。アクセストークンは返却されるほか、以降同一のインスタンスを利用したリクエストに自動で設定されますこの挙動は第引数に`false`を与えることで回避できます)。
コールバックURLを指定した場合、セッションIDはURLパラメータの`session`から取得できます。
``` ts
const cli = new Misskey.api.APIClient({
origin: 'https://misskey.test',
});
const { token } = await cli.authWithMiAuth(sessionId);
// 以後、同じAPIClientを使い続ける場合はトークンが自動で設定されます
const i = await cli.request('i');
```
## API request ## API request
APIを利用する際は、利用するサーバーの情報とアクセストークンを与えて`APIClient`クラスのインスタンスを初期化し、そのインスタンスの`request`メソッドを呼び出してリクエストを行います。 APIを利用する際は、利用するサーバーの情報とアクセストークンを与えて`APIClient`クラスのインスタンスを初期化し、そのインスタンスの`request`メソッドを呼び出してリクエストを行います。

View File

@ -394,10 +394,22 @@ class APIClient {
fetch?: APIClient['fetch'] | null | undefined; fetch?: APIClient['fetch'] | null | undefined;
}); });
// (undocumented) // (undocumented)
authWithMiAuth(sessionId: string, setToken?: boolean): Promise<MiAuthCheckResponse>;
// (undocumented)
credential: string | null | undefined; credential: string | null | undefined;
// (undocumented) // (undocumented)
fetch: FetchLike; fetch: FetchLike;
// (undocumented) // (undocumented)
getMiAuthURL(options: {
name?: string;
icon?: string;
callback?: string;
permission?: typeof permissions_2[number][];
}, sessionId?: string): {
sessionId: string;
url: string;
};
// (undocumented)
origin: string; origin: string;
} }
@ -1122,6 +1134,7 @@ declare namespace entities {
SignupPendingResponse, SignupPendingResponse,
SigninRequest, SigninRequest,
SigninResponse, SigninResponse,
MiAuthCheckResponse,
EmptyRequest, EmptyRequest,
EmptyResponse, EmptyResponse,
AdminMetaResponse, AdminMetaResponse,
@ -2248,6 +2261,12 @@ type MetaRequest = operations['meta']['requestBody']['content']['application/jso
// @public (undocumented) // @public (undocumented)
type MetaResponse = operations['meta']['responses']['200']['content']['application/json']; type MetaResponse = operations['meta']['responses']['200']['content']['application/json'];
// @public (undocumented)
type MiAuthCheckResponse = {
token: string;
user: UserDetailedNotMe;
};
// @public (undocumented) // @public (undocumented)
type MiauthGenTokenRequest = operations['miauth___gen-token']['requestBody']['content']['application/json']; type MiauthGenTokenRequest = operations['miauth___gen-token']['requestBody']['content']['application/json'];
@ -3110,6 +3129,7 @@ type UsersUpdateMemoRequest = operations['users___update-memo']['requestBody']['
// Warnings were encountered during analysis: // Warnings were encountered during analysis:
// //
// src/api.ts:91:3 - (ae-forgotten-export) The symbol "permissions_2" needs to be exported by the entry point index.d.ts
// src/entities.ts:25:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/entities.ts:25:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
// (No @packageDocumentation comment for this package) // (No @packageDocumentation comment for this package)

View File

@ -38,26 +38,28 @@
"@swc/jest": "0.2.31", "@swc/jest": "0.2.31",
"@types/jest": "29.5.12", "@types/jest": "29.5.12",
"@types/node": "20.11.22", "@types/node": "20.11.22",
"@types/uuid": "9.0.8",
"@typescript-eslint/eslint-plugin": "7.1.0", "@typescript-eslint/eslint-plugin": "7.1.0",
"@typescript-eslint/parser": "7.1.0", "@typescript-eslint/parser": "7.1.0",
"esbuild": "0.19.11",
"eslint": "8.57.0", "eslint": "8.57.0",
"execa": "8.0.1",
"glob": "10.3.10",
"jest": "29.7.0", "jest": "29.7.0",
"jest-fetch-mock": "3.0.3", "jest-fetch-mock": "3.0.3",
"jest-websocket-mock": "2.5.0", "jest-websocket-mock": "2.5.0",
"mock-socket": "9.3.1", "mock-socket": "9.3.1",
"ncp": "2.0.0", "ncp": "2.0.0",
"nodemon": "3.1.0", "nodemon": "3.1.0",
"execa": "8.0.1",
"tsd": "0.30.7", "tsd": "0.30.7",
"typescript": "5.3.3", "typescript": "5.3.3"
"esbuild": "0.19.11",
"glob": "10.3.10"
}, },
"files": [ "files": [
"built" "built"
], ],
"dependencies": { "dependencies": {
"eventemitter3": "5.0.1", "eventemitter3": "5.0.1",
"reconnecting-websocket": "4.4.0" "reconnecting-websocket": "4.4.0",
"uuid": "9.0.1"
} }
} }

View File

@ -1,7 +1,10 @@
import './autogen/apiClientJSDoc.js'; import './autogen/apiClientJSDoc.js';
import { v4 as uuid } from 'uuid';
import { SwitchCaseResponseType } from './api.types.js'; import { SwitchCaseResponseType } from './api.types.js';
import { permissions } from './consts.js';
import type { Endpoints } from './api.types.js'; import type { Endpoints } from './api.types.js';
import type { MiAuthCheckResponse } from './entities.js';
export type { export type {
SwitchCaseResponseType, SwitchCaseResponseType,
@ -80,4 +83,55 @@ export class APIClient {
}).catch(reject); }).catch(reject);
}); });
} }
public getMiAuthURL(options: {
name?: string;
icon?: string;
callback?: string;
permission?: typeof permissions[number][];
}, sessionId?: string): {
sessionId: string;
url: string;
} {
const params = new URLSearchParams();
if (options.name) params.set('name', options.name);
if (options.icon) params.set('icon', options.icon);
if (options.callback) params.set('callback', options.callback);
if (options.permission) params.set('permission', options.permission.join(','));
const _sessionId = sessionId ?? uuid();
return {
sessionId: _sessionId,
url: `${this.origin}/miauth/${_sessionId}?${params.toString()}`,
};
}
public authWithMiAuth(sessionId: string, setToken = true): Promise<MiAuthCheckResponse> {
return new Promise((resolve, reject) => {
this.fetch(`${this.origin}/api/miauth/${sessionId}/check`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'omit',
cache: 'no-cache',
}).then(async (res) => {
const body = res.status === 204 ? null : await res.json();
if (res.status === 200 && body) {
if (setToken) {
this.credential = body.token;
}
resolve(body as MiAuthCheckResponse);
} else {
reject({
[MK_API_ERROR]: true,
...body.error,
});
}
}).catch(reject);
});
}
} }

View File

@ -221,3 +221,8 @@ export type SigninResponse = {
id: User['id'], id: User['id'],
i: string, i: string,
}; };
export type MiAuthCheckResponse = {
token: string,
user: UserDetailedNotMe,
};

View File

@ -1098,6 +1098,9 @@ importers:
reconnecting-websocket: reconnecting-websocket:
specifier: 4.4.0 specifier: 4.4.0
version: 4.4.0 version: 4.4.0
uuid:
specifier: 9.0.1
version: 9.0.1
devDependencies: devDependencies:
'@microsoft/api-extractor': '@microsoft/api-extractor':
specifier: 7.39.1 specifier: 7.39.1
@ -1114,6 +1117,9 @@ importers:
'@types/node': '@types/node':
specifier: 20.11.22 specifier: 20.11.22
version: 20.11.22 version: 20.11.22
'@types/uuid':
specifier: 9.0.8
version: 9.0.8
'@typescript-eslint/eslint-plugin': '@typescript-eslint/eslint-plugin':
specifier: 7.1.0 specifier: 7.1.0
version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)