Skip to content

Commit

Permalink
fix: use header for getting access token in verify-token (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
sor4chi authored Dec 17, 2024
1 parent eb114f3 commit 3bbec15
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 55 deletions.
39 changes: 39 additions & 0 deletions webapp/api/oauth/_middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { MiddlewareHandler } from 'hono'
import { HonoEnv } from 'load-context'

const AUTHORIZATION_REGEX = /^Bearer (.+)$/

export const authMiddleware: MiddlewareHandler<HonoEnv> = 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()
}
78 changes: 23 additions & 55 deletions webapp/api/oauth/verifyToken.ts
Original file line number Diff line number Diff line change
@@ -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<HonoEnv>()

// 仕様はここ参照: https://github.com/saitamau-maximum/auth/issues/43
Expand Down Expand Up @@ -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<InvalidResponseType>(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<InvalidResponseType>(INVALID_REQUEST_RESPONSE, 404)
}
// Token が見つからない場合
if (!tokenInfo) {
return c.json<InvalidResponseType>(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<ValidResponseType>(res)
},
)
return c.json<ValidResponseType>(res)
})

// POST 以外は許容しない
app.all('/', async c => {
Expand Down
7 changes: 7 additions & 0 deletions webapp/load-context.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { InferSelectModel } from 'drizzle-orm'
import { DrizzleD1Database } from 'drizzle-orm/d1'
import { type PlatformProxy } from 'wrangler'

Expand All @@ -22,6 +23,12 @@ export interface HonoEnv {
Variables: {
dbClient: DrizzleD1Database<typeof schema>
idpClient: IdpRepository
tokenInfo: InferSelectModel<typeof schema.token> & {
client: InferSelectModel<typeof schema.client>
scopes: (InferSelectModel<typeof schema.tokenScope> & {
scope: InferSelectModel<typeof schema.scope>
})[]
}
}
}

Expand Down

0 comments on commit 3bbec15

Please sign in to comment.