AI Guide - Security
Checklists for working on authentication and permissions. See the main AI guide for the repo map and conventions, and the Fundamentals AI guide for a table of failed-request causes.
Key files
| Concern | File |
|---|---|
| Request authentication | packages/api-http/src/middlewares/http.middlewares.authorization.ts |
| Access control | packages/api-http/src/interceptors/http.interceptors.accessControl.ts |
| Permission evaluation | packages/domain-iam/src/services/authorization/iam.authorization.service.ts |
| Login | packages/domain-iam/src/services/authenticationManager/iam.authenticationManager.service.ts |
| Tokens | packages/domain-iam/src/services/tokenManager/iam.tokenManager.service.ts |
| Login methods | packages/domain-iam/src/services/authentication*/, packages/domain-iam-okta/src/services/authenticationOkta/ |
| CORS | packages/api-http/src/middlewares/http.middlewares.cors.ts |
| User with permissions (reference app) | apps/test/src/domain/iam/services/users/users.service.ts |
| Permission seeds (reference app) | apps/test/src/data/db/seeds/common/ |
Before changing a permission
- List the user's permissions for the same module and resource context in id order - only the first one that passes applies.
- Check that every
userFieldNamerelation is loaded ingetUserWithPermissionsData. - Check that every input path starts with
body,headers,paramsorquery, and that it exists before DTO validation (access control sees the raw input). - Anchor regexes with
^...$. - Remember the user has to log in again to get the new permissions.
Before changing authentication
- Find the auth service's default step config (
getUserAuthenticationConfig) - most behaviour is driven by it, and overrides go inauthServiceSettings.<authType>.steps. - New public routes (sign-up, callbacks) go in
anonymousAccessRoutes. - Keep every failure as
ApplicationError('Authentication failed.')and log the reason - don't leak it to the client. - MFA has no implementation - an
IAMMFAServicesubclass has to be written and passed to the user-local auth service. - An API module without IAM providers only serves anonymous routes - adding protected routes means providing the authorization service, token manager and (to load users) authentication manager.
Things that are easy to get wrong
- Access control runs before the handler's DTO validation, so query values are still strings (
testValuecompares them as numbers and booleans too). req.locals.usercomes from the id token, not the database - permission and relation changes need a new login. The users cache can make even a new login stale.- Only the granting permission's output rules apply, and
forbiddenInputDataclears the whole field, not just the matching items. - A route param is
params.id, notquery.params.id- a path that never exists is set from the user value and grants access. - The 403 body never says why access was denied - only the log does.
- API-key requests skip access control entirely - always set
allowedApiKeyRoutes.