Skip to content

Commit

Permalink
Added mandatory validation check of twitch. Adjusted the login proces…
Browse files Browse the repository at this point in the history
…s so that it doesn't spam Twitch alot and helps the user to find the problem with their authroization
  • Loading branch information
kanimaru committed Aug 9, 2024
1 parent 4f75e9f commit aa2a2da
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 25 deletions.
46 changes: 26 additions & 20 deletions addons/twitcher/lib/oOuch/oauth.gd
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ signal device_code_requested(device_code: OAuthDeviceCodeResponse);
signal token_changed(access_token: String);

var auth_http_server: OAuthHTTPServer;
var token_handler: OAuthTokenHandler;
var _token_handler: OAuthTokenHandler;
var login_in_process: bool;
var _setting: OAuthSetting
var _setting: OAuthSetting;
var _last_login_attempt: int;

enum AuthorizationFlow {
AUTHORIZATION_CODE_FLOW,
Expand All @@ -34,45 +35,50 @@ enum AuthorizationFlow {
PASSWORD_FLOW
}

func _init(setting: OAuthSetting) -> void:
func _init(setting: OAuthSetting, token_handler: OAuthTokenHandler) -> void:
_setting = setting;
auth_http_server = OAuthHTTPServer.new(setting.get_redirect_port());
token_handler = OAuthTokenHandler.new(setting);
token_handler.unauthenticated.connect(login);
token_handler.token_resolved.connect(_on_token_resolved);
_token_handler = token_handler
_token_handler.unauthenticated.connect(login);
_token_handler.token_resolved.connect(_on_token_resolved);

func _on_token_resolved(token: OAuthTokenHandler.OAuthToken) -> void:
token_changed.emit(token.get_access_token());

## Checks if the authentication is valid.
func is_authenticated() -> bool:
return token_handler.is_token_valid();
return _token_handler.is_token_valid();

## Starts the token refresh process to rotate the tokens
func refresh_token() -> void:
await token_handler.refresh_tokens();
await _token_handler.refresh_tokens();

## Checks if the authentication is done or requests a new authentication.
## Use this to ensure that the OAuth is initialized
func ensure_authentication() -> void:
if not token_handler.is_token_valid():
if not _token_handler.is_token_valid():
logInfo("Token is invalid.")
await login();
logDebug("Login is done.")

## Gets the current token as soon as it is available
func get_token() -> String:
if token_handler.get_access_token() == "":
await token_handler.token_resolved;
return token_handler.get_access_token();
if _token_handler.get_access_token() == "":
await _token_handler.token_resolved;
return _token_handler.get_access_token();

## Depending on the authorization_flow it gets resolves the token via the different
## Flow types. Only one login process at the time. All other tries wait until the first process
## was succesful.
func login() -> void:
if Time.get_ticks_msec() - 60 * 1000 < _last_login_attempt:
print("[Twitcher OAuth] Last Login attempt was within 1 minute wait 1 minute before trying again. Please enable and consult logs, cause there is an issue with your authentication!")
await Engine.get_main_loop().create_timer(60).timeout

_last_login_attempt = Time.get_ticks_msec()
if login_in_process:
logDebug("Another process tries already to login. Abort");
await token_handler.token_resolved;
await _token_handler.token_resolved;
return;

login_in_process = true;
Expand All @@ -81,7 +87,7 @@ func login() -> void:
AuthorizationFlow.AUTHORIZATION_CODE_FLOW:
await _start_login_process("code");
AuthorizationFlow.CLIENT_CREDENTIALS:
await token_handler.request_token("client_credentials");
await _token_handler.request_token("client_credentials");
AuthorizationFlow.IMPLICIT_FLOW:
await _start_login_process("token");
AuthorizationFlow.DEVICE_CODE_FLOW:
Expand Down Expand Up @@ -110,11 +116,11 @@ func _start_login_process(response_type: String):
logDebug("Waiting for user to login.")
if response_type == "code":
var auth_code = await _auth_succeed;
token_handler.request_token("authorization_code", auth_code);
await token_handler.token_resolved;
_token_handler.request_token("authorization_code", auth_code);
await _token_handler.token_resolved;
auth_http_server.request_received.disconnect(_process_code_request.bind(auth_http_server));
elif response_type == "token":
await token_handler.token_resolved;
await _token_handler.token_resolved;
auth_http_server.request_received.disconnect(_process_implicit_request.bind(auth_http_server));
logInfo("Request is done stop")
auth_http_server.stop();
Expand All @@ -131,7 +137,7 @@ func _start_device_login_process():
# he want to open the browser manually. Also use print not the logger so that the information
# is sent always.
print("Visit %s and enter the code %s for authorization." % [device_code_response.verification_uri, device_code_response.user_code])
await token_handler.request_device_token(device_code_response, scopes);
await _token_handler.request_device_token(device_code_response, scopes);

func _fetch_device_code_response(scopes: String) -> OAuthDeviceCodeResponse:
logInfo("Start device code flow")
Expand Down Expand Up @@ -187,7 +193,7 @@ func _process_implicit_request(client: OAuthHTTPServer.Client, server: OAuthHTTP
return # Not a valid request
var json_body = parts[1];
var token_request = JSON.parse_string(json_body);
token_handler.update_tokens(token_request["access_token"]);
_token_handler.update_tokens(token_request["access_token"]);
logInfo("Received Access Token update it")
server.send_response(client, "200 OK", "<html><head><title>Login</title><script>window.close()</script></head><body>Success!</body></html>".to_utf8_buffer());

Expand Down Expand Up @@ -236,7 +242,7 @@ func _handle_error(server: OAuthHTTPServer, client: OAuthHTTPServer.Client, quer
#endregion

func get_all_token() -> OAuthTokenHandler.OAuthToken:
return token_handler._tokens;
return _token_handler._tokens;

## Parses a query string and returns a dictionary with the parameters.
static func parse_query(query: String) -> Dictionary:
Expand Down
12 changes: 8 additions & 4 deletions addons/twitcher/lib/oOuch/token_handler.gd
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func _init(setting: OAuthSetting) -> void:
## Checks if tokens runs up and starts refreshing it. (called often hold footprint small)
func _check_token_refresh() -> void:
if _requesting_token: return;

if !_tokens.is_token_valid() && _tokens.has_refresh_token():
logInfo("Token needs refresh");
refresh_tokens();
Expand Down Expand Up @@ -108,10 +109,13 @@ func refresh_tokens() -> void:
if _requesting_token: return;
_requesting_token = true;
logInfo("Refresh token")
var request_body = "client_id=%s&client_secret=%s&refresh_token=%s&grant_type=refresh_token" % [_setting.client_id, _setting.client_secret, _tokens.get_refresh_token()];
var request = _http_client.request(_setting.get_token_path(), HTTPClient.METHOD_POST, HEADERS, request_body);
if await _handle_token_request(request):
logInfo("Token got refreshed")
if _tokens.has_refresh_token():
var request_body = "client_id=%s&client_secret=%s&refresh_token=%s&grant_type=refresh_token" % [_setting.client_id, _setting.client_secret, _tokens.get_refresh_token()];
var request = _http_client.request(_setting.get_token_path(), HTTPClient.METHOD_POST, HEADERS, request_body);
if await _handle_token_request(request):
logInfo("Token got refreshed")
else:
unauthenticated.emit();
else:
unauthenticated.emit();
_requesting_token = false;
Expand Down
3 changes: 2 additions & 1 deletion addons/twitcher/twitch_auth.gd
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ var is_initialzied: bool;

func _init() -> void:
OAuth.set_logger(log.e, log.i, log.d);
auth = OAuth.new(await _get_setting());
var settings = await _get_setting()
auth = OAuth.new(settings, TwitchTokenHandler.new(settings));
is_initialzied = true;
initialized.emit();

Expand Down
29 changes: 29 additions & 0 deletions addons/twitcher/twitch_oauth_token_handler.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
extends "res://addons/twitcher/lib/oOuch/token_handler.gd"

class_name TwitchTokenHandler

## Time between checking the validation of the tokens
var _last_validation_check: int = 0

func _check_token_refresh() -> void:
super._check_token_refresh()

if _last_validation_check < Time.get_ticks_msec():
_last_validation_check = Time.get_ticks_msec() + 60 * 60 * 1000;
_validate_token()

## Calles the validation endpoint of Twtich to make sure
func _validate_token() -> void:
var validation_request = _http_client.request("/oauth2/validate", HTTPClient.METHOD_GET, {
"Authorization": "OAuth %s" % _tokens.get_access_token()
}, "")
var response = await _http_client.wait_for_request(validation_request)
if response.response_code != 200:
refresh_tokens()
return

var response_string: String = response.response_data.get_string_from_utf8();
var response_data = JSON.parse_string(response_string);
if response_data["expires_in"] <= 0:
refresh_tokens()
return

0 comments on commit aa2a2da

Please sign in to comment.