Cross-service auth without Discord OAuth
The Discord-facing service in YouTeacher does not run its own login. Despite sitting behind a Discord surface, it never speaks Discord OAuth. Instead it borrows the identity the rest of the platform already established: a browser that is signed in to the main product carries a session, and this service trusts the platform's central auth service to say who that session belongs to.
The shape of a request
When a request arrives, the service first checks whether the browser carried a platform session at all. No session → the caller is treated as anonymous, and nothing is forwarded. This is a cheap local check, not a network call.
If a session is present, the service forwards the browser's session cookie to the auth service's "current user" endpoint and reads back the answer: a user id, email, a userType (talent / employer / recruiter / admin), and an optional name. If the auth service declines, or the account has no userType yet (incomplete registration), the caller is resolved as null — again, anonymous. The session cookie is the thing that proves which human; this service holds no password, no OAuth token, no session store of its own.
Two credentials, two jobs
A forwarded request actually carries two things, and they answer different questions:
- The session cookie answers which user — it is the human's identity, minted by the central login flow and merely passed along.
- A short-lived service token (a signed bearer, valid on the order of a minute, tagged with the calling service's name) answers which service is calling. It is the internal caller's badge, letting the auth and profile services distinguish a trusted sibling service from arbitrary internet traffic.
These are independent. The service token never stands in for a user; it only vouches for the service. A call that needs to act as a user must still forward that user's session.
Display identity comes from a different service
User id and email are enough to authorize an action, but not to show a friendly name and avatar. Those come from a separate profile service. The Discord service again forwards the session cookie there, because the profile lookup keys off a profile id rather than the auth user id — so the service token alone would not identify the right record. The lookup tries a cascade of possible display fields (display name, username, an organization or school name), and if the profile service is unreachable or returns nothing, it falls back to the user's name, or failing that the local part of their email. Avatar is optional and simply omitted when absent. The rule of thumb: identity is authoritative from auth; presentation is best-effort from profile.
How routes gate reads vs. writes
The same identity helper exposes two postures, and route handlers pick one:
- Extract — resolve the user if there is one, otherwise return nothing. Public reads use this: an anonymous visitor still gets a response, just an un-personalized one.
- Require — resolve the user or reject the request as unauthorized. Authenticated writes use this: no valid session, no action.
So "public read, authenticated write" is not enforced per-route by scattered checks; it is one decision — which posture does this handler ask for — made against a single identity resolver. Route paths themselves are not hardcoded; they are injected from configuration, so the service can be pointed at different auth/profile deployments without code changes.
Why this design
A single sign-on lives in one place, and every satellite service leans on it rather than re-implementing OAuth, token refresh, and session storage per surface. The cost is a trust boundary: this service must be a place the session cookie and the internal token can safely flow through. That is exactly why the mechanism details — the secret, the exact token and cookie shapes — stay out of a public note.
Related
- youteacher — the platform this service belongs to