Tirith is an fullstack application which aims to provide a web-interface to access and manage skribbltypo related data.
It is split into a NEST backend which serves as API for the frontend and the skribbltypo extension, and an Angular frontend which replaces the legacy skribbltypo website.
The website features an introduction to the extension, various tools and helpers for Palantir, an user dashboard and a restricted additional admin dashboard.
The API uses rich swagger annotations to generate a meaningful openapi spec and the frontend api client from that.
There is also a swagger interface available for the public api.
Authentication is done by providing a BEARER token in the headers; each user has an individual token for use across typo and can be obtained on the /login page on the website.
The api uses the toobeeh/Valmar service, which is an internal component of the typo ecosystem, for database access and domain logic.
To implement new features, Valmar will need to support them first as well.
The Tirith api adds a "public interface" on top of Valmar, taking care of authentication, rate-limiting and caching if needed.
The frontend angular application is split into modules:
- public for static/information/tool content
- auth which features the authentication page to login with Discord
- admin containing the admin dashboard.
- user - the user dashboard, primary for server management
Additionally, there is the API service module, which is auto-generated via openapi tools based on the backend openapi specs.
The frontend is live on the www subdomain and probably also accessible via redirect from the typo TLD https://typo.rip
TLDR:
- Typo implements minimal OIDC & OAuth2
- Typo OAuth2 login is a wrapper around Discord OAuth
- Typo has openId discovery and a jwks endpoint
- Typo supports OAUth2 only via authorization_code grant type
- The Typo OAuth2 authorization endpoint is located at the website
- Scope parameters are fixed per client and ignored, redirect_uri is supported but must match one of the clients known uris
- OAuth2 clients are public and can be created by anyone through te API
- The Typo OAuth2 token endpoint is located at the API oauth2 controller
Typo went through a lot of (bad practice) auth variants, so there might be remains of that found in the code.
The current auth flow is completely independent of that, but might support a few old endpoints for backwards compatibility.
The core of authenticating with typo is a discord login. Each typo account is associated with a discord account.
To identify a user, the Discord oauth2 flow is performed. The resulting authorization code is used in the backend to receive a discord access token and using that, receive the discord ID of the acting user.
Using the discord ID, the backend can fetch the associated typo user and proceed with the typo auth flow.
Typo implements a minimal version of OAuth2, which only supports Authorization Code flow.
Any user can create up to 5 unverified OAuth2 clients which have fixed parameters:
- expiry
- redirect uri
- scope
To create more clients or to display an approved badge on the login UI, users can request manual verification from the dev.
Although being limited to fixed client parameters and the authorization code flow, Typo is fully OAuth2 compliant and uses JWT as access tokens.
The typo frontend exposes two pages to perform the flow:
This page is the entry for the auth code flow.
To start the flow, the client has to redirect to this page and provide following query parameters:
- client_id: ID of the client, has to be created via the API first
- response_type: Can only be "code", since typo does not implement other OAuth2 flows
- state: Optional state parameter
This page will first redirect to the discord oauth2 flow as described above, and then proceed with the actual OAuth flow on the /oauth2/submit page.
This page posts the received discord auth code along with the typo oauth client id to the typo API, to receive a typo auth code along with user details and information to the token that is about to be created.
The user is prompted to accept the information about the created token, which will next complete the auth code flow by redirecting back to the client's redirect_uri along with following query parameters:
- code: the created OAuth2 Authorization Code which can be used to obtain the Access Token
- state: the state data passed before when starting the flow
Once it has received the authorization code, the client can once retrieve an access token that matches the client's scopes.
This is done using an endpoint in the typo API:
This endpoint receives following url encoded data:
- grant_type: "authorization_code", which is the only supported flow
- code: the authorization code received in the previous steps
- redirect_uri: the uri that had been used when creating the auth code
- client_id: the client id that the auth code had been issued for
Before auth code creation, the API fetches the discord user from the discord auth code.
It checks whether there is a typo user for this id, decrypts the access token and returns the state.
If no user exists, the frontend first creates a new user using the decrypted access token.
After that, the frontend calls the api to create a typo oauth2 authorization code, again using the encrypted discord access token.
The API forwards the client id and user login to the internal domain logic API, Valmar.
There, an auth code is stored along with an expiry, and returned to the API, which passes it to the user.
Auth codes are c# ULIDs.
When the API receives the auth code, it sends it along with redirect uri and client id to Valmar.
Valmar verifies the parameters and generates a JWT based on scopes and expiry associated with the client, which is passed to the API and again returned to the user.