mirror of
https://github.com/modrinth/code.git
synced 2026-09-05 06:19:11 +00:00
should fix auth token passing
This commit is contained in:
@@ -63,6 +63,7 @@
|
|||||||
"highlight.js": "^11.7.0",
|
"highlight.js": "^11.7.0",
|
||||||
"intl-messageformat": "^10.7.7",
|
"intl-messageformat": "^10.7.7",
|
||||||
"iso-3166-2": "1.0.0",
|
"iso-3166-2": "1.0.0",
|
||||||
|
"jose": "^6.2.2",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"lru-cache": "^11.2.4",
|
"lru-cache": "^11.2.4",
|
||||||
|
|||||||
@@ -1335,18 +1335,11 @@ async function initializeIntercom() {
|
|||||||
if (!auth.value?.user) return
|
if (!auth.value?.user) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const intercomData = await $fetch<{
|
const intercomData = await $fetch<{ token: string }>('/api/intercom/messenger-jwt')
|
||||||
token: string
|
|
||||||
user: AuthUser
|
|
||||||
}>('/api/intercom/messenger-jwt')
|
|
||||||
|
|
||||||
Intercom({
|
Intercom({
|
||||||
app_id: config.public.intercomAppId,
|
app_id: config.public.intercomAppId,
|
||||||
intercom_user_jwt: intercomData.token,
|
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,
|
session_duration: 1000 * 60 * 60 * 24,
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} 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:
|
iso-3166-2:
|
||||||
specifier: 1.0.0
|
specifier: 1.0.0
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
jose:
|
||||||
|
specifier: ^6.2.2
|
||||||
|
version: 6.2.2
|
||||||
js-yaml:
|
js-yaml:
|
||||||
specifier: ^4.1.0
|
specifier: ^4.1.0
|
||||||
version: 4.1.1
|
version: 4.1.1
|
||||||
@@ -6727,6 +6730,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
|
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
jose@6.2.2:
|
||||||
|
resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==}
|
||||||
|
|
||||||
js-beautify@1.15.4:
|
js-beautify@1.15.4:
|
||||||
resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==}
|
resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
@@ -16355,6 +16361,8 @@ snapshots:
|
|||||||
|
|
||||||
jiti@2.6.1: {}
|
jiti@2.6.1: {}
|
||||||
|
|
||||||
|
jose@6.2.2: {}
|
||||||
|
|
||||||
js-beautify@1.15.4:
|
js-beautify@1.15.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
config-chain: 1.1.13
|
config-chain: 1.1.13
|
||||||
|
|||||||
Reference in New Issue
Block a user