Skip to content

Commit

Permalink
fix: Fix SSO (#25)
Browse files Browse the repository at this point in the history
* feat: OIDC device code grant implementation

* fix: Remove hardcoded auth path
  • Loading branch information
gianklug authored Jan 15, 2024
1 parent df5db3c commit 295a406
Showing 1 changed file with 36 additions and 2 deletions.
38 changes: 36 additions & 2 deletions src/libtimed/oidc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import datetime
import http.server
import json
import time
import webbrowser
from urllib.parse import urlparse

Expand Down Expand Up @@ -41,18 +42,20 @@ def log_message(self, *args, **kwargs):


class OIDCClient:
def __init__(self, client_id, sso_url, sso_realm, auth_path):
def __init__(self, client_id, sso_url, sso_realm, auth_path, use_device_flow=False):
self.client_id = client_id
self.sso_url = sso_url
self.sso_realm = sso_realm
self.auth_path = auth_path
self.use_device_flow = use_device_flow

def autoconfig(self):
data = requests.get(
f"{self.sso_url}/auth/realms/{self.sso_realm}/.well-known/openid-configuration"
f"{self.sso_url}/realms/{self.sso_realm}/.well-known/openid-configuration"
).json()
self.authorization_endpoint = data["authorization_endpoint"]
self.token_endpoint = data["token_endpoint"]
self.device_endpoint = data["device_authorization_endpoint"]

def start_browser_flow(self):
# construct the authorization request
Expand All @@ -74,6 +77,29 @@ def start_browser_flow(self):
self.code = code
return True

def start_device_flow(self):
# construct the authorization request
auth_data = requests.post(self.device_endpoint, data={"client_id": self.client_id}).json()
verification_uri_complete = auth_data["verification_uri_complete"]
verification_uri = auth_data["verification_uri"]
device_code = auth_data["device_code"]
user_code = auth_data["user_code"]

# open the browser to the authorization URL
webbrowser.open_new(verification_uri_complete)
# print manual instructions
print(f"Please visit {verification_uri} and enter the code {user_code}")
time.sleep(5)

resp = {}
while "access_token" not in resp:
resp = requests.post(self.token_endpoint, data={"client_id": self.client_id, "grant_type": "urn:ietf:params:oauth:grant-type:device_code", "device_code": device_code}).json()
print(resp)
time.sleep(5)
access_token = resp["access_token"]
return access_token


def get_token(self):
# construct the token request
token_request = {
Expand Down Expand Up @@ -113,9 +139,11 @@ def check_expired(self, token):
return now.timestamp() < expires_at

def keyring_get(self):
return []
return keyring.get_password("system", "libtimed_token_" + self.client_id)

def keyring_set(self, token):
return True
keyring.set_password("system", "libtimed_token_" + self.client_id, token)

def authorize(self):
Expand All @@ -124,6 +152,12 @@ def authorize(self):
return cached_token

self.autoconfig()
if self.use_device_flow:
if token:= self.start_device_flow():
self.keyring_set(token)
return token
return False

if self.start_browser_flow():
token = self.get_token()
if not token:
Expand Down

0 comments on commit 295a406

Please sign in to comment.