Files
speckle-server/packages/server/modules/acc/rest/oidc.ts
T
Oğuzhan Koral 05e00d2c5c feat(acc): revamp (#5501)
* chore(acc): put permission gql in correct place

* feat(acc): swap to new rvt import

* fix(acc): add oda secrets

* feat(acc): auth cookies

* feat(acc): introduce integrations as workspace setting

* feat(acc): create sync item from models

* fix(acc): bump

* fix(acc): naming lost in merge

* feat(acc): no acc tab - table under settings

* chore(acc): new sync but will disapper

* feat(acc): see statuses over model list

* chore(acc): fix return type

* chore(acc): type saga

* chore(acc): status badge

* chore(acc): refactor acc gql (#5556)

* checkpoint

* fix(acc): refactor gql items

* feat(acc): double button

* chore(acc): gqlgen

* fix(acc): model ids are not project ids

* chore(acc): bump function version

* chore(acc): split up clients

* feat(acc): more-optimised gql folder fetching schema

* feat(acc): acc folder contents gql impl

* feat(acc): apollo cache optimisations

* chore(acc): gqlgen

* fix(acc): return something for

* fix(acc): handle null values correctly

* chore(acc): specify prod functions

---------

Co-authored-by: Chuck Driesler <chuck@speckle.systems>
2025-10-03 13:54:17 +01:00

116 lines
3.3 KiB
TypeScript

/* eslint-disable camelcase */
import {
buildAuthorizeUrl,
exchangeCodeForTokens,
exchangeRefreshTokenForTokens,
generateCodeVerifier
} from '@/modules/acc/clients/autodesk/tokens'
import { sessionMiddlewareFactory } from '@/modules/auth/middleware'
import { corsMiddlewareFactory } from '@/modules/core/configs/cors'
import {
getAutodeskIntegrationClientId,
getAutodeskIntegrationClientSecret,
getServerOrigin
} from '@/modules/shared/helpers/envHelper'
import { logger } from '@/observability/logging'
import type { Express } from 'express'
export const setupAccOidcEndpoints = (app: Express) => {
const corsMiddleware = corsMiddlewareFactory()
const sessionMiddleware = sessionMiddlewareFactory()
app.post(
'/api/v1/acc/auth/login',
corsMiddleware,
sessionMiddleware,
async (req, res) => {
const { callbackEndpoint } = req.body
req.session.callbackEndpoint = callbackEndpoint
const { codeVerifier, codeChallenge } = generateCodeVerifier()
req.session.codeVerifier = codeVerifier
const redirectUri = `${getServerOrigin()}/api/v1/acc/auth/callback`
const authorizeUrl = buildAuthorizeUrl({
clientId: getAutodeskIntegrationClientId(),
redirectUri,
codeChallenge,
scopes: ['user-profile:read', 'data:read', 'viewables:read', 'openid']
})
return res.json({ authorizeUrl })
}
)
app.get(
'/api/v1/acc/auth/callback',
corsMiddleware,
sessionMiddleware,
async (req, res) => {
const { code } = req.query
const codeVerifier = req.session.codeVerifier
if (!code || !codeVerifier) {
return res.status(400).send({ error: 'Missing code or verifier' })
}
try {
const tokens = await exchangeCodeForTokens({
code: String(code),
codeVerifier,
clientId: getAutodeskIntegrationClientId(),
clientSecret: getAutodeskIntegrationClientSecret(),
redirectUri: `${getServerOrigin()}/api/v1/acc/auth/callback`
})
req.session.accTokens = tokens
logger.warn(req.session)
if (!req.session.callbackEndpoint) {
return res.status(500)
}
return res.redirect(req.session.callbackEndpoint)
} catch (error) {
console.error('Token exchange failed:', error)
return res.status(500).send({ error: 'Token exchange failed' })
}
}
)
app.get('/api/v1/acc/auth/status', corsMiddleware, sessionMiddleware, (req, res) => {
try {
if (!req.session.accTokens) {
return res.status(404).send({ error: 'No ACC tokens found' })
}
res.send(req.session.accTokens)
} finally {
req.session.accTokens = undefined // we wanna return it just once
}
})
app.post(
'/api/v1/acc/auth/refresh',
corsMiddleware,
sessionMiddleware,
async (req, res) => {
const { refresh_token } = req.body || {}
if (!refresh_token) {
return res.status(401).json({ error: 'No refresh token found' })
}
try {
const newTokens = await exchangeRefreshTokenForTokens({ refresh_token })
req.session.accTokens = newTokens
res.json(newTokens)
} catch (error) {
console.error('Error refreshing token:', error)
res.status(500).json({ error })
}
}
)
}