Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vähennetään apigw:stä tulevia turhia virhehälyjä #6382

Merged
merged 3 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions apigw/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ export function apiRouter(config: Config, redisClient: RedisClient) {
redisClient
)
)
router.all('/citizen/auth/*', (_, res) => res.redirect('/'))
router.use('/citizen/public/map-api', mapRoutes)
router.all('/citizen/public/*', citizenProxy)
router.all('/citizen/*', citizenSessions.requireAuthentication, citizenProxy)
Expand All @@ -341,6 +342,7 @@ export function apiRouter(config: Config, redisClient: RedisClient) {

router.use('/employee/', employeeSessions.middleware)
router.get('/employee/auth/status', internalAuthStatus(employeeSessions))
router.all('/employee/auth/*', (_, res) => res.redirect('/employee'))
router.all('/employee/public/*', employeeProxy)
router.all(
'/employee/*',
Expand Down Expand Up @@ -376,6 +378,9 @@ export function apiRouter(config: Config, redisClient: RedisClient) {
express.json(),
pinLogoutRequestHandler(employeeMobileSessions, redisClient)
)
router.all('/employee-mobile/auth/*', (_, res) =>
res.redirect('/employee/mobile')
)
router.all('/employee-mobile/public/*', employeeMobileProxy)
router.all(
'/employee-mobile/*',
Expand Down
1 change: 0 additions & 1 deletion apigw/src/shared/middleware/error-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ interface LogResponse {
export const errorHandler: (v: boolean) => ErrorRequestHandler =
(includeErrorMessage: boolean) => (error, req, res, next) => {
if (error instanceof InvalidAntiCsrfToken) {
logError('Anti-CSRF token error', req, undefined, error)
if (!res.headersSent) {
res
.status(403)
Expand Down
52 changes: 29 additions & 23 deletions apigw/src/shared/routes/saml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,30 @@ export function createSamlIntegration<T extends SessionType>(
return samlMessage.profile
}

const validateSamlLogoutMessage = async (
req: express.Request
): Promise<Profile | null> => {
let samlMessage: { profile: Profile | null; loggedOut: boolean }
if (req.method === 'GET') {
const originalQuery = url.parse(req.url).query ?? ''
samlMessage = await saml.validateRedirectAsync(req.query, originalQuery)
} else if (req.method === 'POST') {
samlMessage = isSamlPostRequest(req)
? // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
await saml.validatePostRequestAsync(req.body)
: // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
await saml.validatePostResponseAsync(req.body)
} else {
throw new SamlError(`Unsupported HTTP method ${req.method}`)
}
if (!samlMessage.loggedOut) {
throw new SamlError(
'Invalid SAML message type: expected logout request/response'
)
}
return samlMessage.profile
}

const login: AsyncRequestHandler = async (req, res) => {
logAuditEvent(eventCode('sign_in_started'), req, 'Login endpoint called')
try {
Expand Down Expand Up @@ -234,33 +258,15 @@ export function createSamlIntegration<T extends SessionType>(
const logoutCallback: AsyncRequestHandler = async (req, res) => {
logAuditEvent(eventCode('sign_out'), req, 'Logout callback called')

let samlMessage: { profile: Profile | null; loggedOut: boolean }
if (req.method === 'GET') {
const originalQuery = url.parse(req.url).query ?? ''
samlMessage = await saml.validateRedirectAsync(req.query, originalQuery)
} else if (req.method === 'POST') {
samlMessage = isSamlPostRequest(req)
? // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
await saml.validatePostRequestAsync(req.body)
: // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
await saml.validatePostResponseAsync(req.body)
} else {
throw new SamlError(`Unsupported HTTP method ${req.method}`)
}
if (!samlMessage.loggedOut) {
throw new SamlError(
'Invalid SAML message type: expected logout request/response'
)
}

try {
const profile = await validateSamlLogoutMessage(req)
let url: string
// There are two scenarios:
// 1. IDP-initiated logout, and we've just received a logout request -> profile is not null, the SAML transaction
// is still in progress, and we should redirect the user back to the IDP
// 2. SP-initiated logout, and we've just received a logout response -> profile is null, the SAML transaction
// is complete, and we should redirect the user to some meaningful page
if (samlMessage.profile) {
if (profile) {
let user: unknown
const sessionUser = sessions.getUser(req)
if (sessionUser) {
Expand All @@ -271,16 +277,16 @@ export function createSamlIntegration<T extends SessionType>(
} else {
// We're possibly doing SLO without a real session (e.g. browser has
// 3rd party cookies disabled)
const logoutToken = createLogoutToken(samlMessage.profile)
const logoutToken = createLogoutToken(profile)
const sessionUser = await sessions.logoutWithToken(logoutToken)
const userId = SamlProfileIdSchema.safeParse(sessionUser)
user = userId.success ? userId.data : undefined
}
const profileId = SamlProfileIdSchema.safeParse(samlMessage.profile)
const profileId = SamlProfileIdSchema.safeParse(profile)
const success = profileId.success && _.isEqual(user, profileId.data)

url = await saml.getLogoutResponseUrlAsync(
samlMessage.profile,
profile,
// not validated, because the value and its format are specified by the IDP and we're supposed to
// just pass it back
getRawUnvalidatedRelayState(req) ?? '',
Expand Down
Loading