mirror of
https://github.com/sasjs/server.git
synced 2026-07-23 21:25:29 +00:00
398dfb515c
Add a new diagram covering the authentication mechanism: a session-based /SASLogon/login + /SASLogon/authorize handshake that mints a short-lived auth code, exchanged at /SASjsApi/auth/token for a revocable JWT pair, with optional LDAP-backed credential verification. Add WHY comments (not present before) at the handful of spots in the auth code whose behaviour isn't obvious from reading them in isolation: why /auth/token can return an existing token pair instead of rotating it, why the auth-code exchange never checks a client's clientSecret, how verifyTokenInDB turns self-verifying JWTs into revocable ones, why two /SASjsApi/user routes stay reachable in desktop mode, why the static authorized-route list exists on top of plain authentication, and why /SASjsApi/client's real protection lives at its router mount point in routes/api/index.ts rather than in client.ts itself.
41 lines
1.3 KiB
TypeScript
41 lines
1.3 KiB
TypeScript
import { Request } from 'express'
|
|
|
|
export const TopLevelRoutes = ['/AppStream', '/SASjsApi']
|
|
|
|
// Being authenticated is enough for most routes. These specifically also
|
|
// require a granted Permission (checked by the authorize middleware) because
|
|
// they run arbitrary submitted code or manipulate arbitrary files on disk -
|
|
// a narrower blast radius than everything else a logged-in user can do.
|
|
const StaticAuthorizedRoutes = [
|
|
'/SASjsApi/code/execute',
|
|
'/SASjsApi/stp/execute',
|
|
'/SASjsApi/drive/deploy',
|
|
'/SASjsApi/drive/deploy/upload',
|
|
'/SASjsApi/drive/file',
|
|
'/SASjsApi/drive/folder',
|
|
'/SASjsApi/drive/fileTree',
|
|
'/SASjsApi/drive/rename'
|
|
]
|
|
|
|
export const getAuthorizedRoutes = () => {
|
|
const streamingApps = Object.keys(process.appStreamConfig)
|
|
const streamingAppsRoutes = streamingApps.map((app) => `/AppStream/${app}`)
|
|
return [...TopLevelRoutes, ...StaticAuthorizedRoutes, ...streamingAppsRoutes]
|
|
}
|
|
|
|
export const getPath = (req: Request) => {
|
|
const { baseUrl, path: reqPath } = req
|
|
|
|
if (baseUrl === '/AppStream') {
|
|
const appStream = reqPath.split('/')[1]
|
|
|
|
// removing trailing slash of URLs
|
|
return (baseUrl + '/' + appStream).replace(/\/$/, '')
|
|
}
|
|
|
|
return (baseUrl + reqPath).replace(/\/$/, '')
|
|
}
|
|
|
|
export const isAuthorizingRoute = (req: Request): boolean =>
|
|
getAuthorizedRoutes().includes(getPath(req))
|