diff --git a/webapp/api/oauth/_middleware.ts b/webapp/api/oauth/_middleware.ts new file mode 100644 index 0000000..a857f71 --- /dev/null +++ b/webapp/api/oauth/_middleware.ts @@ -0,0 +1,39 @@ +import { MiddlewareHandler } from 'hono' +import { HonoEnv } from 'load-context' + +const AUTHORIZATION_REGEX = /^Bearer (.+)$/ + +export const authMiddleware: MiddlewareHandler = async (c, next) => { + const authorization = c.req.header('Authroization') + const accessToken = authorization?.match(AUTHORIZATION_REGEX)?.[1] + if (!accessToken) { + return c.text('Unauthorized', 401) + } + + const nowUnixMs = Date.now() + const nowDate = new Date(nowUnixMs) + + const tokenInfo = await c.var.dbClient.query.token.findFirst({ + where: (token, { eq, and, gt }) => + and( + eq(token.access_token, accessToken), + gt(token.code_expires_at, nowDate), + ), + with: { + client: true, + scopes: { + with: { + scope: true, + }, + }, + }, + }) + + if (!tokenInfo) { + return c.text('Unauthorized', 401) + } + + c.set('tokenInfo', tokenInfo) + + await next() +} diff --git a/webapp/api/oauth/verifyToken.ts b/webapp/api/oauth/verifyToken.ts index 7710439..6f00688 100644 --- a/webapp/api/oauth/verifyToken.ts +++ b/webapp/api/oauth/verifyToken.ts @@ -1,11 +1,11 @@ -import { zValidator } from '@hono/zod-validator' import { Hono } from 'hono' -import { z } from 'zod' import { READ_SCOPES } from '../../constants/scope' import { HonoEnv } from '../../load-context' import { IUserInfo } from '../../repository/idp' +import { authMiddleware } from './_middleware' + const app = new Hono() // 仕様はここ参照: https://github.com/saitamau-maximum/auth/issues/43 @@ -41,64 +41,32 @@ const INVALID_REQUEST_RESPONSE: InvalidResponseType = { scopes: null, } -app.post( - '/', - zValidator( - 'form', - z.object({ - access_token: z.string(), - }), - async (res, c) => { - if (!res.success) - return c.json(INVALID_REQUEST_RESPONSE, 400) - }, - ), - async c => { - const { access_token } = c.req.valid('form') - - const nowUnixMs = Date.now() - const nowDate = new Date(nowUnixMs) +app.get('/', authMiddleware, async c => { + const tokenInfo = c.var.tokenInfo - const tokenInfo = await c.var.dbClient.query.token.findFirst({ - where: (token, { eq, and, gt }) => - and( - eq(token.access_token, access_token), - gt(token.code_expires_at, nowDate), - ), - with: { - client: true, - scopes: { - with: { - scope: true, - }, - }, - }, - }) + c.header('Cache-Control', 'no-store') + c.header('Pragma', 'no-cache') - c.header('Cache-Control', 'no-store') - c.header('Pragma', 'no-cache') - - // Token が見つからない場合 - if (!tokenInfo) { - return c.json(INVALID_REQUEST_RESPONSE, 404) - } + // Token が見つからない場合 + if (!tokenInfo) { + return c.json(INVALID_REQUEST_RESPONSE, 404) + } - const res: ValidResponseType = { - valid: true, - client: tokenInfo.client, - user_id: tokenInfo.user_id, - expires_at: tokenInfo.access_token_expires_at.getTime(), - scopes: tokenInfo.scopes.map(s => s.scope.name), - } + const res: ValidResponseType = { + valid: true, + client: tokenInfo.client, + user_id: tokenInfo.user_id, + expires_at: tokenInfo.access_token_expires_at.getTime(), + scopes: tokenInfo.scopes.map(s => s.scope.name), + } - if (res.scopes.includes(READ_SCOPES.BASIC_INFO)) { - const user = await c.var.idpClient.findUserById(res.user_id) - if (user) res.user_info = user - } + if (res.scopes.includes(READ_SCOPES.BASIC_INFO)) { + const user = await c.var.idpClient.findUserById(res.user_id) + if (user) res.user_info = user + } - return c.json(res) - }, -) + return c.json(res) +}) // POST 以外は許容しない app.all('/', async c => { diff --git a/webapp/load-context.ts b/webapp/load-context.ts index 50a9c29..fbcf218 100644 --- a/webapp/load-context.ts +++ b/webapp/load-context.ts @@ -1,3 +1,4 @@ +import { InferSelectModel } from 'drizzle-orm' import { DrizzleD1Database } from 'drizzle-orm/d1' import { type PlatformProxy } from 'wrangler' @@ -22,6 +23,12 @@ export interface HonoEnv { Variables: { dbClient: DrizzleD1Database idpClient: IdpRepository + tokenInfo: InferSelectModel & { + client: InferSelectModel + scopes: (InferSelectModel & { + scope: InferSelectModel + })[] + } } }