diff options
Diffstat (limited to 'packages/backend/src/server/api/endpoints/auth')
4 files changed, 302 insertions, 0 deletions
diff --git a/packages/backend/src/server/api/endpoints/auth/accept.ts b/packages/backend/src/server/api/endpoints/auth/accept.ts new file mode 100644 index 0000000000..1d1d8ac227 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/auth/accept.ts @@ -0,0 +1,76 @@ +import * as crypto from 'crypto'; +import $ from 'cafy'; +import define from '../../define'; +import { ApiError } from '../../error'; +import { AuthSessions, AccessTokens, Apps } from '@/models/index'; +import { genId } from '@/misc/gen-id'; +import { secureRndstr } from '@/misc/secure-rndstr'; + +export const meta = { + tags: ['auth'], + + requireCredential: true as const, + + secure: true, + + params: { + token: { + validator: $.str + } + }, + + errors: { + noSuchSession: { + message: 'No such session.', + code: 'NO_SUCH_SESSION', + id: '9c72d8de-391a-43c1-9d06-08d29efde8df' + }, + } +}; + +export default define(meta, async (ps, user) => { + // Fetch token + const session = await AuthSessions + .findOne({ token: ps.token }); + + if (session == null) { + throw new ApiError(meta.errors.noSuchSession); + } + + // Generate access token + const accessToken = secureRndstr(32, true); + + // Fetch exist access token + const exist = await AccessTokens.findOne({ + appId: session.appId, + userId: user.id, + }); + + if (exist == null) { + // Lookup app + const app = await Apps.findOneOrFail(session.appId); + + // Generate Hash + const sha256 = crypto.createHash('sha256'); + sha256.update(accessToken + app.secret); + const hash = sha256.digest('hex'); + + const now = new Date(); + + // Insert access token doc + await AccessTokens.insert({ + id: genId(), + createdAt: now, + lastUsedAt: now, + appId: session.appId, + userId: user.id, + token: accessToken, + hash: hash + }); + } + + // Update session + await AuthSessions.update(session.id, { + userId: user.id + }); +}); diff --git a/packages/backend/src/server/api/endpoints/auth/session/generate.ts b/packages/backend/src/server/api/endpoints/auth/session/generate.ts new file mode 100644 index 0000000000..859cf52ed3 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/auth/session/generate.ts @@ -0,0 +1,70 @@ +import { v4 as uuid } from 'uuid'; +import $ from 'cafy'; +import config from '@/config/index'; +import define from '../../../define'; +import { ApiError } from '../../../error'; +import { Apps, AuthSessions } from '@/models/index'; +import { genId } from '@/misc/gen-id'; + +export const meta = { + tags: ['auth'], + + requireCredential: false as const, + + params: { + appSecret: { + validator: $.str, + } + }, + + res: { + type: 'object' as const, + optional: false as const, nullable: false as const, + properties: { + token: { + type: 'string' as const, + optional: false as const, nullable: false as const, + }, + url: { + type: 'string' as const, + optional: false as const, nullable: false as const, + format: 'url', + }, + } + }, + + errors: { + noSuchApp: { + message: 'No such app.', + code: 'NO_SUCH_APP', + id: '92f93e63-428e-4f2f-a5a4-39e1407fe998' + } + } +}; + +export default define(meta, async (ps) => { + // Lookup app + const app = await Apps.findOne({ + secret: ps.appSecret + }); + + if (app == null) { + throw new ApiError(meta.errors.noSuchApp); + } + + // Generate token + const token = uuid(); + + // Create session token document + const doc = await AuthSessions.save({ + id: genId(), + createdAt: new Date(), + appId: app.id, + token: token + }); + + return { + token: doc.token, + url: `${config.authUrl}/${doc.token}` + }; +}); diff --git a/packages/backend/src/server/api/endpoints/auth/session/show.ts b/packages/backend/src/server/api/endpoints/auth/session/show.ts new file mode 100644 index 0000000000..23f1a56a37 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/auth/session/show.ts @@ -0,0 +1,58 @@ +import $ from 'cafy'; +import define from '../../../define'; +import { ApiError } from '../../../error'; +import { AuthSessions } from '@/models/index'; + +export const meta = { + tags: ['auth'], + + requireCredential: false as const, + + params: { + token: { + validator: $.str, + } + }, + + errors: { + noSuchSession: { + message: 'No such session.', + code: 'NO_SUCH_SESSION', + id: 'bd72c97d-eba7-4adb-a467-f171b8847250' + } + }, + + res: { + type: 'object' as const, + optional: false as const, nullable: false as const, + properties: { + id: { + type: 'string' as const, + optional: false as const, nullable: false as const, + format: 'id' + }, + app: { + type: 'object' as const, + optional: false as const, nullable: false as const, + ref: 'App' + }, + token: { + type: 'string' as const, + optional: false as const, nullable: false as const + } + } + } +}; + +export default define(meta, async (ps, user) => { + // Lookup session + const session = await AuthSessions.findOne({ + token: ps.token + }); + + if (session == null) { + throw new ApiError(meta.errors.noSuchSession); + } + + return await AuthSessions.pack(session, user); +}); diff --git a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts new file mode 100644 index 0000000000..72201cb207 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts @@ -0,0 +1,98 @@ +import $ from 'cafy'; +import define from '../../../define'; +import { ApiError } from '../../../error'; +import { Apps, AuthSessions, AccessTokens, Users } from '@/models/index'; + +export const meta = { + tags: ['auth'], + + requireCredential: false as const, + + params: { + appSecret: { + validator: $.str, + }, + + token: { + validator: $.str, + } + }, + + res: { + type: 'object' as const, + optional: false as const, nullable: false as const, + properties: { + accessToken: { + type: 'string' as const, + optional: false as const, nullable: false as const, + }, + + user: { + type: 'object' as const, + optional: false as const, nullable: false as const, + ref: 'User', + }, + } + }, + + errors: { + noSuchApp: { + message: 'No such app.', + code: 'NO_SUCH_APP', + id: 'fcab192a-2c5a-43b7-8ad8-9b7054d8d40d' + }, + + noSuchSession: { + message: 'No such session.', + code: 'NO_SUCH_SESSION', + id: '5b5a1503-8bc8-4bd0-8054-dc189e8cdcb3' + }, + + pendingSession: { + message: 'This session is not completed yet.', + code: 'PENDING_SESSION', + id: '8c8a4145-02cc-4cca-8e66-29ba60445a8e' + } + } +}; + +export default define(meta, async (ps) => { + // Lookup app + const app = await Apps.findOne({ + secret: ps.appSecret + }); + + if (app == null) { + throw new ApiError(meta.errors.noSuchApp); + } + + // Fetch token + const session = await AuthSessions.findOne({ + token: ps.token, + appId: app.id + }); + + if (session == null) { + throw new ApiError(meta.errors.noSuchSession); + } + + if (session.userId == null) { + throw new ApiError(meta.errors.pendingSession); + } + + // Lookup access token + const accessToken = await AccessTokens.findOneOrFail({ + appId: app.id, + userId: session.userId + }); + + // Delete session + AuthSessions.delete(session.id); + + return { + accessToken: accessToken.token, + user: await Users.pack(session.userId, null, { + detail: true + }) + }; +}); |