From f57239fc58965c047e31e38aac005ddbf259b83b Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Fri, 29 Sep 2023 00:49:01 +0200 Subject: [PATCH] [mastodon-client] POST /apps --- .../src/server/api/mastodon/endpoints/auth.ts | 19 +-- .../api/mastodon/entities/oauth/oauth.ts | 123 ++++++++++++++++++ .../src/server/api/mastodon/helpers/auth.ts | 43 ++++++ 3 files changed, 171 insertions(+), 14 deletions(-) create mode 100644 packages/backend/src/server/api/mastodon/entities/oauth/oauth.ts create mode 100644 packages/backend/src/server/api/mastodon/helpers/auth.ts diff --git a/packages/backend/src/server/api/mastodon/endpoints/auth.ts b/packages/backend/src/server/api/mastodon/endpoints/auth.ts index b55cb6388..14e02e6d9 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/auth.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/auth.ts @@ -1,8 +1,6 @@ -import megalodon, { MegalodonInterface } from "megalodon"; import Router from "@koa/router"; -import { koaBody } from "koa-body"; -import { getClient } from "../ApiMastodonCompatibleService.js"; -import bodyParser from "koa-bodyparser"; +import { AuthHelpers } from "@/server/api/mastodon/helpers/auth.js"; +import { convertId, IdType } from "@/misc/convert-id.js"; const readScope = [ "read:account", @@ -43,8 +41,6 @@ const writeScope = [ export function apiAuthMastodon(router: Router): void { router.post("/v1/apps", async (ctx) => { - const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; - const client = getClient(BASE_URL, ""); const body: any = ctx.request.body || ctx.request.query; try { let scope = body.scopes; @@ -57,20 +53,15 @@ export function apiAuthMastodon(router: Router): void { const scopeArr = Array.from(pushScope); const red = body.redirect_uris; - const appData = await client.registerApp(body.client_name, { - scopes: scopeArr, - redirect_uris: red, - website: body.website, - }); + const appData = await AuthHelpers.registerApp(body['client_name'], scopeArr, red, body['website']); const returns = { - id: Math.floor(Math.random() * 100).toString(), + id: convertId(appData.id, IdType.MastodonId), name: appData.name, website: body.website, redirect_uri: red, - client_id: Buffer.from(appData.url || "").toString("base64"), + client_id: Buffer.from(appData.url ?? "").toString("base64"), client_secret: appData.clientSecret, }; - console.log(returns); ctx.body = returns; } catch (e: any) { console.error(e); diff --git a/packages/backend/src/server/api/mastodon/entities/oauth/oauth.ts b/packages/backend/src/server/api/mastodon/entities/oauth/oauth.ts new file mode 100644 index 000000000..3ef1cd666 --- /dev/null +++ b/packages/backend/src/server/api/mastodon/entities/oauth/oauth.ts @@ -0,0 +1,123 @@ +/** + * OAuth + * Response data when oauth request. + **/ +namespace OAuth { + export type AppDataFromServer = { + id: string; + name: string; + website: string | null; + redirect_uri: string; + client_id: string; + client_secret: string; + }; + + export type TokenDataFromServer = { + access_token: string; + token_type: string; + scope: string; + created_at: number; + expires_in: number | null; + refresh_token: string | null; + }; + + export class AppData { + public url: string | null; + public session_token: string | null; + constructor( + public id: string, + public name: string, + public website: string | null, + public redirect_uri: string, + public client_id: string, + public client_secret: string, + ) { + this.url = null; + this.session_token = null; + } + + /** + * Serialize raw application data from server + * @param raw from server + */ + static from(raw: AppDataFromServer) { + return new this( + raw.id, + raw.name, + raw.website, + raw.redirect_uri, + raw.client_id, + raw.client_secret, + ); + } + + get redirectUri() { + return this.redirect_uri; + } + get clientId() { + return this.client_id; + } + get clientSecret() { + return this.client_secret; + } + } + + export class TokenData { + public _scope: string; + constructor( + public access_token: string, + public token_type: string, + scope: string, + public created_at: number, + public expires_in: number | null = null, + public refresh_token: string | null = null, + ) { + this._scope = scope; + } + + /** + * Serialize raw token data from server + * @param raw from server + */ + static from(raw: TokenDataFromServer) { + return new this( + raw.access_token, + raw.token_type, + raw.scope, + raw.created_at, + raw.expires_in, + raw.refresh_token, + ); + } + + /** + * OAuth Aceess Token + */ + get accessToken() { + return this.access_token; + } + get tokenType() { + return this.token_type; + } + get scope() { + return this._scope; + } + /** + * Application ID + */ + get createdAt() { + return this.created_at; + } + get expiresIn() { + return this.expires_in; + } + /** + * OAuth Refresh Token + */ + get refreshToken() { + return this.refresh_token; + } + } +} + +export default OAuth; diff --git a/packages/backend/src/server/api/mastodon/helpers/auth.ts b/packages/backend/src/server/api/mastodon/helpers/auth.ts new file mode 100644 index 000000000..2fa898161 --- /dev/null +++ b/packages/backend/src/server/api/mastodon/helpers/auth.ts @@ -0,0 +1,43 @@ +import OAuth from "@/server/api/mastodon/entities/oauth/oauth.js"; +import { secureRndstr } from "@/misc/secure-rndstr.js"; +import { Apps, AuthSessions } from "@/models/index.js"; +import { genId } from "@/misc/gen-id.js"; +import { v4 as uuid } from "uuid"; +import config from "@/config/index.js"; + +export class AuthHelpers { + public static async registerApp(name: string, scopes: string[], redirect_uris: string, website: string | null): Promise { + const secret = secureRndstr(32); + const app = await Apps.insert({ + id: genId(), + createdAt: new Date(), + userId: null, + name: name, + description: '', + permission: scopes, + callbackUrl: redirect_uris, + secret: secret, + }).then((x) => Apps.findOneByOrFail(x.identifiers[0])); + + const appdataPre: OAuth.AppDataFromServer = { + id: app.id, + name: app.name, + website: website, + client_id: "", + client_secret: app.secret, + redirect_uri: redirect_uris! + } + const appdata = OAuth.AppData.from(appdataPre); + const token = uuid(); + const session = await AuthSessions.insert({ + id: genId(), + createdAt: new Date(), + appId: app.id, + token: token, + }).then((x) => AuthSessions.findOneByOrFail(x.identifiers[0])); + + appdata.url = `${config.authUrl}/${session.token}`; + appdata.session_token = session.token; + return appdata; + } +}