mirror of
https://github.com/modrinth/code.git
synced 2026-08-30 11:36:05 +00:00
should fix auth token passing
This commit is contained in:
@@ -63,6 +63,7 @@
|
||||
"highlight.js": "^11.7.0",
|
||||
"intl-messageformat": "^10.7.7",
|
||||
"iso-3166-2": "1.0.0",
|
||||
"jose": "^6.2.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jszip": "^3.10.1",
|
||||
"lru-cache": "^11.2.4",
|
||||
|
||||
@@ -1335,18 +1335,11 @@ async function initializeIntercom() {
|
||||
if (!auth.value?.user) return
|
||||
|
||||
try {
|
||||
const intercomData = await $fetch<{
|
||||
token: string
|
||||
user: AuthUser
|
||||
}>('/api/intercom/messenger-jwt')
|
||||
const intercomData = await $fetch<{ token: string }>('/api/intercom/messenger-jwt')
|
||||
|
||||
Intercom({
|
||||
app_id: config.public.intercomAppId,
|
||||
intercom_user_jwt: intercomData.token,
|
||||
user_id: intercomData.user.id,
|
||||
name: intercomData.user.username,
|
||||
email: intercomData.user.email,
|
||||
created_at: Math.floor(new Date(intercomData.user.created).getTime() / 1000),
|
||||
session_duration: 1000 * 60 * 60 * 24,
|
||||
})
|
||||
} catch (error) {
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import { type Labrinth, ModrinthApiError } from '@modrinth/api-client'
|
||||
import { SignJWT } from 'jose'
|
||||
|
||||
import { useServerModrinthClient } from '~/server/utils/api-client'
|
||||
|
||||
type IntercomTokenResponse = {
|
||||
token: string
|
||||
}
|
||||
|
||||
async function signIntercomUserJwt(
|
||||
user: { id: string; username: string; email?: string; created: string },
|
||||
secret: string,
|
||||
): Promise<string> {
|
||||
const createdAt = Math.floor(new Date(user.created).getTime() / 1000)
|
||||
|
||||
const payload: Record<string, string | number> = {
|
||||
user_id: user.id,
|
||||
name: user.username,
|
||||
}
|
||||
|
||||
if (user.email) {
|
||||
payload.email = user.email
|
||||
}
|
||||
|
||||
if (Number.isFinite(createdAt)) {
|
||||
payload.created_at = createdAt
|
||||
}
|
||||
|
||||
return await new SignJWT(payload)
|
||||
.setProtectedHeader({ alg: 'HS256', typ: 'JWT' })
|
||||
.setIssuedAt()
|
||||
.setExpirationTime('1h')
|
||||
.sign(new TextEncoder().encode(secret))
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event): Promise<IntercomTokenResponse> => {
|
||||
if (event.method !== 'GET') {
|
||||
throw createError({
|
||||
statusCode: 405,
|
||||
message: 'Method not allowed',
|
||||
})
|
||||
}
|
||||
|
||||
const authToken = getCookie(event, 'auth-token')
|
||||
if (!authToken) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
message: 'Authentication required',
|
||||
})
|
||||
}
|
||||
|
||||
setHeader(event, 'cache-control', 'private, no-store, max-age=0')
|
||||
|
||||
const config = useRuntimeConfig(event)
|
||||
if (!config.intercomIdentitySecret) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
message: 'Intercom identity secret is not configured',
|
||||
})
|
||||
}
|
||||
|
||||
const client = useServerModrinthClient({
|
||||
event,
|
||||
authToken,
|
||||
})
|
||||
|
||||
let user: { id: string; username: string; email?: string; created: string }
|
||||
try {
|
||||
const currentUser = await client.request<Labrinth.Users.v2.User>('/user', {
|
||||
api: 'labrinth',
|
||||
version: 2,
|
||||
method: 'GET',
|
||||
})
|
||||
user = {
|
||||
id: currentUser.id,
|
||||
username: currentUser.username,
|
||||
email: currentUser.email,
|
||||
created: currentUser.created,
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof ModrinthApiError && error.statusCode === 401) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
message: 'Authentication required',
|
||||
})
|
||||
}
|
||||
|
||||
throw createError({
|
||||
statusCode: 502,
|
||||
message: 'Failed to resolve current user',
|
||||
})
|
||||
}
|
||||
|
||||
const token = await signIntercomUserJwt(user, config.intercomIdentitySecret)
|
||||
|
||||
return {
|
||||
token,
|
||||
}
|
||||
})
|
||||
@@ -1,119 +0,0 @@
|
||||
import { createHmac } from 'node:crypto'
|
||||
|
||||
import { ModrinthApiError } from '@modrinth/api-client'
|
||||
|
||||
import { useServerModrinthClient } from '~/server/utils/api-client'
|
||||
|
||||
type AuthenticatedUser = {
|
||||
id: string
|
||||
username: string
|
||||
email?: string
|
||||
created: string
|
||||
}
|
||||
|
||||
type IntercomTokenResponse = {
|
||||
token: string
|
||||
user: AuthenticatedUser
|
||||
}
|
||||
|
||||
function base64UrlEncode(input: string | Buffer): string {
|
||||
return Buffer.from(input)
|
||||
.toString('base64')
|
||||
.replace(/\+/g, '-')
|
||||
.replace(/\//g, '_')
|
||||
.replace(/=+$/g, '')
|
||||
}
|
||||
|
||||
function signIntercomUserJwt(payload: Record<string, unknown>, secret: string): string {
|
||||
const header = {
|
||||
alg: 'HS256',
|
||||
typ: 'JWT',
|
||||
}
|
||||
|
||||
const encodedHeader = base64UrlEncode(JSON.stringify(header))
|
||||
const encodedPayload = base64UrlEncode(JSON.stringify(payload))
|
||||
const unsignedToken = `${encodedHeader}.${encodedPayload}`
|
||||
|
||||
const signature = createHmac('sha256', secret).update(unsignedToken).digest()
|
||||
|
||||
return `${unsignedToken}.${base64UrlEncode(signature)}`
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event): Promise<IntercomTokenResponse> => {
|
||||
if (getMethod(event) !== 'GET') {
|
||||
throw createError({
|
||||
statusCode: 405,
|
||||
message: 'Method not allowed',
|
||||
})
|
||||
}
|
||||
|
||||
const headerToken = getHeader(event, 'authorization')
|
||||
const parsedHeaderToken = headerToken?.replace(/^Bearer\s+/i, '').trim()
|
||||
const authToken = parsedHeaderToken || getCookie(event, 'auth-token')
|
||||
if (!authToken) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
message: 'Authentication required',
|
||||
})
|
||||
}
|
||||
|
||||
setHeader(event, 'cache-control', 'private, no-store, max-age=0')
|
||||
|
||||
const config = useRuntimeConfig(event)
|
||||
if (!config.intercomIdentitySecret) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
message: 'Intercom identity secret is not configured',
|
||||
})
|
||||
}
|
||||
|
||||
const client = useServerModrinthClient({
|
||||
event,
|
||||
authToken,
|
||||
})
|
||||
|
||||
let user: AuthenticatedUser
|
||||
try {
|
||||
const currentUser = await client.request<AuthenticatedUser>('/user', {
|
||||
api: 'labrinth',
|
||||
version: 2,
|
||||
method: 'GET',
|
||||
})
|
||||
user = {
|
||||
id: currentUser.id,
|
||||
username: currentUser.username,
|
||||
email: currentUser.email,
|
||||
created: currentUser.created,
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof ModrinthApiError && error.statusCode === 401) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
message: 'Authentication required',
|
||||
})
|
||||
}
|
||||
|
||||
throw createError({
|
||||
statusCode: 502,
|
||||
message: 'Failed to resolve current user',
|
||||
})
|
||||
}
|
||||
|
||||
const now = Math.floor(Date.now() / 1000)
|
||||
const token = signIntercomUserJwt(
|
||||
{
|
||||
user_id: user.id,
|
||||
email: user.email,
|
||||
name: user.username,
|
||||
created_at: Math.floor(new Date(user.created).getTime() / 1000),
|
||||
iat: now,
|
||||
exp: now + 60 * 60,
|
||||
},
|
||||
config.intercomIdentitySecret,
|
||||
)
|
||||
|
||||
return {
|
||||
token,
|
||||
user,
|
||||
}
|
||||
})
|
||||
Generated
+8
@@ -326,6 +326,9 @@ importers:
|
||||
iso-3166-2:
|
||||
specifier: 1.0.0
|
||||
version: 1.0.0
|
||||
jose:
|
||||
specifier: ^6.2.2
|
||||
version: 6.2.2
|
||||
js-yaml:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.1
|
||||
@@ -6727,6 +6730,9 @@ packages:
|
||||
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
|
||||
hasBin: true
|
||||
|
||||
jose@6.2.2:
|
||||
resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==}
|
||||
|
||||
js-beautify@1.15.4:
|
||||
resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -16355,6 +16361,8 @@ snapshots:
|
||||
|
||||
jiti@2.6.1: {}
|
||||
|
||||
jose@6.2.2: {}
|
||||
|
||||
js-beautify@1.15.4:
|
||||
dependencies:
|
||||
config-chain: 1.1.13
|
||||
|
||||
Reference in New Issue
Block a user