pokepurple/addons/twitcher/lib/oOuch/oauth_token.gd

142 lines
4.7 KiB
GDScript3
Raw Normal View History

@icon("./security-icon.svg")
@tool
extends Resource
## Used to store and load token's and to exchange them through the code.
## Try to avoid debugging this object cause it leaks your access and refresh tokens
## Hint never store the token value as string in your code to reduce the chance
## to leak the tokens always use the getter.
class_name OAuthToken
static var CRYPTO: Crypto = Crypto.new()
## Key for encryption purpose to save the tokens
@export var _crypto_key_provider: CryptoKeyProvider = preload("res://addons/twitcher/lib/oOuch/default_key_provider.tres")
## Unique identifier to store multiple tokens within one config file
@export var _identifier: String = "Auth-%s" % randi_range(0, 10000)
## Storage where the tokens should be saved encrypted (multiple secrets can be put in the same file see _identifier)
@export var _cache_path: String = "user://auth.conf":
set(val):
_cache_path = val
_load_tokens()
var _scopes: PackedStringArray = []
var _expire_date: int
var _config_file: ConfigFile = ConfigFile.new()
var _access_token: String = "":
set(val):
_access_token = val
if val != "": authorized.emit()
var _refresh_token: String = ""
## Called when the token was resolved / accesstoken got refreshed
signal authorized
func update_values(access_token: String, refresh_token: String, expire_in: int, scopes: Array[String]):
_expire_date = roundi(Time.get_unix_time_from_system() + expire_in)
_access_token = access_token
_refresh_token = refresh_token
_scopes = scopes
_persist_tokens()
emit_changed()
## Persists the tokesn with the expire date
func _persist_tokens():
var encrypted_access_token = _crypto_key_provider.encrypt(_access_token.to_utf8_buffer())
var encrypted_refresh_token = _crypto_key_provider.encrypt(_refresh_token.to_utf8_buffer())
_config_file.load(_cache_path)
_config_file.set_value(_identifier, "expire_date", _expire_date)
_config_file.set_value(_identifier, "access_token", Marshalls.raw_to_base64(encrypted_access_token))
_config_file.set_value(_identifier, "refresh_token", Marshalls.raw_to_base64(encrypted_refresh_token))
_config_file.set_value(_identifier, "scopes", ",".join(_scopes))
var err = _config_file.save(_cache_path)
if err != OK: push_error("Couldn't save tokens cause of ", error_string(err))
## Loads the tokens and returns the information if the file got created
func _load_tokens() -> bool:
var status = _config_file.load(_cache_path)
if status == OK && _config_file.has_section(_identifier):
_expire_date = _config_file.get_value(_identifier, "expire_date", 0)
var encrypted_access_token: PackedByteArray = Marshalls.base64_to_raw(_config_file.get_value(_identifier, "access_token"))
var encrypted_refresh_token: PackedByteArray = Marshalls.base64_to_raw(_config_file.get_value(_identifier, "refresh_token"))
_access_token = _crypto_key_provider.decrypt(encrypted_access_token).get_string_from_utf8()
_refresh_token = _crypto_key_provider.decrypt(encrypted_refresh_token).get_string_from_utf8()
_scopes = _config_file.get_value(_identifier, "scopes", "").split(",", false)
emit_changed()
return true
return false
func remove_tokens() -> void:
var status = _config_file.load(_cache_path)
if status == OK && _config_file.has_section(_identifier):
_access_token = ""
_refresh_token = ""
_expire_date = 0
_scopes.clear()
_config_file.erase_section(_identifier)
var err = _config_file.save(_cache_path)
if err != OK: push_error("Couldn't save tokens cause of ", error_string(err))
emit_changed()
print("%s got revoked" % _identifier)
else:
print("%s not found" % _identifier)
func get_refresh_token() -> String:
return _refresh_token
func get_access_token() -> String:
if not is_token_valid(): await authorized
return _access_token
func get_scopes() -> PackedStringArray:
return _scopes
## The unix timestamp when the token is expiring
func get_expiration() -> int:
return _expire_date
func get_expiration_readable() -> String:
if _expire_date == 0:
return "Not available"
return Time.get_datetime_string_from_unix_time(_expire_date, true)
func invalidate() -> void:
_expire_date = 0
_refresh_token = ""
_access_token = ""
_scopes = []
emit_changed()
## Does this accesstoken has a refresh token
func has_refresh_token() -> bool:
return _refresh_token != "" && _refresh_token != null
## Checks if the access token is still valid
func is_token_valid() -> bool:
var current_time = Time.get_unix_time_from_system()
return current_time < _expire_date
## Get all token names within a config file
static func get_identifiers(cache_file: String) -> PackedStringArray:
var _config_file: ConfigFile = ConfigFile.new()
var status = _config_file.load(cache_file)
if status != OK: return []
return _config_file.get_sections()