There are a few options.
One is simple third-party cookies; when the user authenticates, the server returns a page that makes simple requests - each with a short-lived, single-use, verifiable token - to each subdomain that the user has access to. The subdomain servers verify the token and each set a session cookie, scoped only to their (sub)domain, in the response. The downsides of this approach include making a bunch of probably-unnecessary calls, needing a way to secure the tokens, and requiring that the user's browser accept third-party cookies (most do, but not all).
Another way would be to authenticate at - and receive a session cookie that is set only for - a fully trusted subdomain (auth.companyx.com). Whenever the user tries to visit another (sub)domain (app1.companyx.com), if the user doesn't have a cookie on that domain yet, the site returns a script that makes an authenticated CORS request to auth.companyx.com. The server at auth.companyx.com checks the Origin: app1.companyx.com
header, verifies the user's token at auth.companyx.com is for a user authorized to use app1.companyx.com, and returns a token - possibly a JWT - to the browser. The browser forwards that token to app1.companyx.com (via same-origin XHR), app1.companyx.com verifies the token (either by simply checking the signature and validity locally, or by making a server-to-server call to auth.companyx.com or looking in some shared DB), and returns a session cookie scoped only to app1.companyx.com. If the user tries to visit app4.companyx.com, the server at auth.companyx.com says the user isn't authorized to access that site and doesn't supply a token for it. This is, in effect, a local SSO system.
One problem both of the above scenarios have is that session management ends up being on a per-subdomain basis. If the user logs out of one domain before their session expires, they should probably get logged out of all of them, but this requires additional work to clear out the remaining cookies (or invalidate them server-side, which is more secure but requires notifying all of the servers and getting them to store state about which session tokens are / are not valid).
The usual approach here is simply to distinguish highly-trusted domains from all other domains. If app1, app2, and app3 are trusted but app4 is not trusted, then app4 shouldn't be on the same root domain as the others. This is basically just good security practice in general. An untrusted site that shares a domain root with a trusted site can do things like plant malicious cookies to try and do things like bypass CSRF protection or launch XSS attacks or something against the trusted site(s).
Alternatively, if you trust app4 but just don't want it to see the session token for app3 and think that means the user is automatically also authorized to access app4, well, that's just a standard access control situation. There are lots of ways to handle that, such as using a JWT (or similar signed authentication token) and having each site handle authorization on their own, including "scope" in the session token (again, it needs to be signed) so that each server can see if the token is both authentic (un-tampered-with) and see if the auth server granted access to a particular app, and so on.