-
Notifications
You must be signed in to change notification settings - Fork 4
internal.authentication
[!INFO] This document deals with the Oauth authentication used to restrict access to the backend api, which is used in the fronend application. The Oauth Authentication that is done with twitch and other services is not discussed here, as we are the client and not the ressource server in that scenario.
Warning
You can disable Authentication on all Endpoints by Setting the ENV disableAllAuth
to true
.
This will however remove all checks and make every endpoint (this includes POST, PUT, PATCH, DELETE) public for everyone without any authentication or authorization.
Only use this for testing purposes!
Authentication of all endpoints is done with an Twitch access token obtained via Twitch OAuth Implicit Grant flow as specified in RFC OAuth 2.0.
In the language of the RFC, the involved roles for our specific use case are:
- Ressource Owner: End-User/Panel-User/Moderator (as the person that owns the shared information, the account information)
- Authorization Server: The Server that issues access tokens to the client, and at which an access token can be validated
- Client: The Frontend (the application that needs to authenticate itself, and the user to the Ressource Server that serves the requests), in our case it is ECMAScript in the browser (user-agent)
- Ressource Server: The Backend (as the server that needs to authenticate (and authorize) requests made to it by other clients)
Important
A separate Client (from the backend oauth used to read the chat) is registered with twitch, because the client app (the frontend application inside the users browser) cannot keep an application secret hidden from the enduser, and other applications, and is therefor needs to be a "public client". RFC - Client Types
sequenceDiagram
participant AS as Authorization Server
participant C as Client
participant RS as Ressource Server
C->>AS: redirect user to twitch Auth/login page
AS->>C: redirect user with access token back to client url
C->>C: extract access token, and store for reuse
C->>RS: request Ressource with access token
RS-->>AS: validate access token
AS-->>RS: userId of token holder
RS->>C: server request resonse
The AccessToken is passed to the backend via the Token
header.
The Backend Server the validates it via the twitch id.twitch.tv/oauth2/validate
endpoint.
The returned username and userId is then used to authorize the request if the user id is supposed to have access to the panel and api resources.
If a validation is not successful, a error will be returned to the requesting party. This will require the sender to get a new accessToken. This does not necessarily mean that user input is required. Some times it is enough to trigger a new implicit grant flow, without the user needing to reauthorize the application. What conditions trigger reauthorization is entirely up to twitch and we have no Control over that.
List of response codes related to authentication and reasons for returning theses codes:
-
500
- If thetoken
Header is missing -
401
- If the access token could not be validated with twitch (token is invalid) -
403
- The access token was valid, but the user is not allowed to access the API Ressource
The access token is not validated with Twitch on every request. Because of the increased latency that every request would experience, if we would always make a HTTP request to twitch, until we start responding to the request. Instead after the first successful validation a session is created and saved for this specific accessToken and userAgent String (from the browser). If the same accessToken is used with the same userAgent again, the request is accepted.
A Session automatically times out after 15min. On the next request a validation of the accessToken with the twitch Servers will be performed.
Session Management is completely transparent to the client application. Besides increased latency the client does not notice a timed out session at all, and does not need to do anything.
After Authentication, the userId is checked for if the twitch user is allowed to have panel access. This is done via the paneluser database table. This table does not hold any authentication or authorization related data, and is only for general account information and preferences.
The list of users allowed to access the panel can be modified by any user granted access to the panel. All of the following endpoints are also Authenticated.
Get list of all users with panel access
GET /panelAccounts
Reponse: Json, list of Json Object with username, userId, and accountCreationTime as unix epoch millis.
[
{
"username": "TEST_USER",
"userId": "923928934893489",
"accountCreationTime": 1730760781627
}
]
The Twitch User ID consists of only numbers, but is to be treated as an opaque string id without any pattern.
A new Panel User can be added via the username.
The username is then converted into the twitch userId and then a new panel user (with defaults) is created and saved
POST /panelAccounts
Body: the (single) twitch username plain as a string without any special encoding
Delete a (Twitch) user from the list of users with panel access.
POST /deleteAllSessions
Body: the (single) twitch userId plain as a string without any special encoding
Close all sessions of the User associated with this accessToken, all access tokens need to be revalidated
POST /deleteUserSessions
Close all sessions of any user, all access tokens need to be revalidated
POST /deleteAllSessions
The list of allowed users can be overridden via the application.properties config. This is useful for setup or as a fallback mechanism.
The new list can be supplied via the allowedPanelUsers
key. The value needs to be a ', '
separated list of twitch usernames.
This list is then converted into new or existing panel user accounts with twitch userIds.
This new list becomes the active list when the key overwritePanelUsers
is set to true.
If this is set, all users not in the allowedPanelUsers list lose their panel access, even if they have an account saved in the db.
Example:
allowedPanelUsers=userName1, testUser3
overwritePanelUsers=true
- Create a new Client with twitch on dev.twitch.tv
- Select "public client" because the browser cannot keep an client secret
- add all your possible redirect urls
- (if we would use the same client app, the access token used in the backend chat, and the login, would likely collide and invalidate each other, for the broadcaster, and the chatbot account)
- Use Twitch Implicit Grant Flow (not OIDC version) for authentication
- create the authorisation url to authorize the app with the users twitch account
- set response_type to
token
- use the twitch client id
- set scopes to
scopes=
(empty, no scopes needed for token validation) - set redirect_uri to the url of the panels oauth result page
- set a random string as state, and save this state for later
- set response_type to
- redirect the user to this twitch authorization page
- on redirect from twitch, remove access token from url, check if the state parameter is the same, and display error page on failure
- redirect back to the home page of the bot, and send the access token on every request to the backend bot
- create the authorisation url to authorize the app with the users twitch account
- If the backend returns a 401, the access token is no longer valid, a new access token needs to be requested by redirecting the user to the authorization page again
To access an authenticated endpoint, a valid twitch access token needs to be set as the value of the token
header.
- Create a new Client with twitch on dev.twitch.tv
- Select "public client" because the browser cannot keep an client secret
- add all your possible redirect urls
- (if we would use the same client app, the access token used in the backend chat, and the login, would likely collide and invalidate each other, for the broadcaster, and the chatbot account)
- Set the
TWICH_CLIENT_ID
in the frontend .env config - Set your Twitch username in the panel user overwrite list of the backend, and enable overwrite. This ensures access for the first initial setup. The Overwrite mode needs to be disabled after initial setup.