oqtant.util.auth

  1# Copyright 2023 Infleqtion
  2#
  3# Licensed under the Apache License, Version 2.0 (the "License");
  4# you may not use this file except in compliance with the License.
  5# You may obtain a copy of the License at
  6#
  7#     https://www.apache.org/licenses/LICENSE-2.0
  8#
  9# Unless required by applicable law or agreed to in writing, software
 10# distributed under the License is distributed on an "AS IS" BASIS,
 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12# See the License for the specific language governing permissions and
 13# limitations under the License.
 14
 15import base64
 16import hashlib
 17import os
 18import webbrowser
 19from multiprocessing import Queue
 20
 21import requests
 22import uvicorn
 23from fastapi import APIRouter, FastAPI, Request
 24from fastapi.responses import RedirectResponse
 25
 26from oqtant.settings import Settings
 27from oqtant.util.server import ThreadServer
 28
 29settings = Settings()
 30
 31app = FastAPI(title="Login API", openapi_url="/openapi.json")
 32router = APIRouter()
 33
 34
 35def generate_random(length: int) -> str:
 36    return base64.urlsafe_b64encode(os.urandom(length)).decode("utf-8").replace("=", "")
 37
 38
 39verifier = generate_random(80)
 40
 41
 42def generate_challenge(verifier: str) -> str:
 43    hashed = hashlib.sha256(verifier.encode("utf-8")).digest()
 44    return base64.urlsafe_b64encode(hashed).decode("utf-8").replace("=", "")
 45
 46
 47def get_authentication_url(auth_server_port: int):
 48    code_challenge = generate_challenge(verifier)
 49    auth_url = "".join(
 50        [
 51            f"{settings.auth0_base_url}/authorize",
 52            "?response_type=code",
 53            f"&scope={settings.auth0_scope}",
 54            f"&audience={settings.auth0_audience}",
 55            f"&code_challenge={code_challenge}",
 56            "&code_challenge_method=S256",
 57            f"&client_id={settings.auth0_client_id}",
 58            f"&redirect_uri=http://localhost:{auth_server_port}",
 59        ]
 60    )
 61    return auth_url
 62
 63
 64queue = Queue()
 65
 66
 67@app.get("/")
 68async def main(request: Request, code):
 69    resp = await get_token(verifier, code, request.url.port)
 70    token = resp["access_token"]
 71    queue.put({"token": token})
 72    if token:
 73        return "Successfully authenticated, you may close this tab now"
 74    else:
 75        return "Failed to authenticate, please close this tab and try again"
 76
 77
 78@app.get("/login")
 79def login(request: Request):
 80    return RedirectResponse(
 81        url=get_authentication_url(auth_server_port=request.url.port)
 82    )
 83
 84
 85async def get_token(verifier: str, code: str, auth_server_port: int):
 86    url = f"{settings.auth0_base_url}/oauth/token"
 87    headers = {"content-type": "application/x-www-form-urlencoded"}
 88    data = {
 89        "grant_type": "authorization_code",
 90        "client_id": settings.auth0_client_id,
 91        "code_verifier": verifier,
 92        "code": code,
 93        "redirect_uri": f"http://localhost:{auth_server_port}",
 94    }
 95    resp = requests.post(
 96        url, headers=headers, data=data, allow_redirects=False, timeout=(5, 30)
 97    )
 98    return resp.json()
 99
100
101def get_user_token(auth_server_port: int = 8080) -> str:
102    """A utility function required for getting Oqtant authenticated with your Oqtant account.
103       Starts up a server to handle the Auth0 authentication process and acquire a token.
104    Args:
105        auth_server_port (int): optional port to run the authentication server on
106    Returns:
107        str: Auth0 user token
108    """
109    allowed_ports = [8080, 8081, 8082, 8083, 8084, 8085]
110    if auth_server_port not in allowed_ports:
111        raise ValueError(f"{auth_server_port} not in allowed ports: {allowed_ports}")
112    server_config = uvicorn.Config(
113        app=app, host="localhost", port=auth_server_port, log_level="error"
114    )
115    server = ThreadServer(config=server_config)
116    with server.run_in_thread():
117        webbrowser.open(f"http://localhost:{auth_server_port}/login")
118        token = queue.get(block=True)
119    return token["token"]
settings = Settings(auth0_base_url='https://coldquanta-dev.us.auth0.com', auth0_client_id='ZzQdn5ZZq1dmpP5N55KINr33u47RBRiu', auth0_scope='offline_access bec_dev_service:client', auth0_audience='https://oraqle-dev.infleqtion.com/oqtant', signin_local_callback_url='http://localhost:8080', base_url='https://oraqle-dev.infleqtion.com/api/jobs', max_ind_var=2, max_job_batch_size=30)
app = <fastapi.applications.FastAPI object>
router = <fastapi.routing.APIRouter object>
def generate_random(length: int) -> str:
36def generate_random(length: int) -> str:
37    return base64.urlsafe_b64encode(os.urandom(length)).decode("utf-8").replace("=", "")
verifier = 'WZtKSU9nBiFJQQEIk0YyiNJ7gyisLy8GGZGbadM0YWzaHgV0BeM8Jc5JuKkFOmdP5EHXLWMHV0xWPnPRZp-ezfRL_gfvH3aBWzbeAqiIa0c'
def generate_challenge(verifier: str) -> str:
43def generate_challenge(verifier: str) -> str:
44    hashed = hashlib.sha256(verifier.encode("utf-8")).digest()
45    return base64.urlsafe_b64encode(hashed).decode("utf-8").replace("=", "")
def get_authentication_url(auth_server_port: int):
48def get_authentication_url(auth_server_port: int):
49    code_challenge = generate_challenge(verifier)
50    auth_url = "".join(
51        [
52            f"{settings.auth0_base_url}/authorize",
53            "?response_type=code",
54            f"&scope={settings.auth0_scope}",
55            f"&audience={settings.auth0_audience}",
56            f"&code_challenge={code_challenge}",
57            "&code_challenge_method=S256",
58            f"&client_id={settings.auth0_client_id}",
59            f"&redirect_uri=http://localhost:{auth_server_port}",
60        ]
61    )
62    return auth_url
queue = <multiprocessing.queues.Queue object>
@app.get('/')
async def main(request: starlette.requests.Request, code):
68@app.get("/")
69async def main(request: Request, code):
70    resp = await get_token(verifier, code, request.url.port)
71    token = resp["access_token"]
72    queue.put({"token": token})
73    if token:
74        return "Successfully authenticated, you may close this tab now"
75    else:
76        return "Failed to authenticate, please close this tab and try again"
@app.get('/login')
def login(request: starlette.requests.Request):
79@app.get("/login")
80def login(request: Request):
81    return RedirectResponse(
82        url=get_authentication_url(auth_server_port=request.url.port)
83    )
async def get_token(verifier: str, code: str, auth_server_port: int):
86async def get_token(verifier: str, code: str, auth_server_port: int):
87    url = f"{settings.auth0_base_url}/oauth/token"
88    headers = {"content-type": "application/x-www-form-urlencoded"}
89    data = {
90        "grant_type": "authorization_code",
91        "client_id": settings.auth0_client_id,
92        "code_verifier": verifier,
93        "code": code,
94        "redirect_uri": f"http://localhost:{auth_server_port}",
95    }
96    resp = requests.post(
97        url, headers=headers, data=data, allow_redirects=False, timeout=(5, 30)
98    )
99    return resp.json()
def get_user_token(auth_server_port: int = 8080) -> str:
102def get_user_token(auth_server_port: int = 8080) -> str:
103    """A utility function required for getting Oqtant authenticated with your Oqtant account.
104       Starts up a server to handle the Auth0 authentication process and acquire a token.
105    Args:
106        auth_server_port (int): optional port to run the authentication server on
107    Returns:
108        str: Auth0 user token
109    """
110    allowed_ports = [8080, 8081, 8082, 8083, 8084, 8085]
111    if auth_server_port not in allowed_ports:
112        raise ValueError(f"{auth_server_port} not in allowed ports: {allowed_ports}")
113    server_config = uvicorn.Config(
114        app=app, host="localhost", port=auth_server_port, log_level="error"
115    )
116    server = ThreadServer(config=server_config)
117    with server.run_in_thread():
118        webbrowser.open(f"http://localhost:{auth_server_port}/login")
119        token = queue.get(block=True)
120    return token["token"]

A utility function required for getting Oqtant authenticated with your Oqtant account. Starts up a server to handle the Auth0 authentication process and acquire a token. Args: auth_server_port (int): optional port to run the authentication server on Returns: str: Auth0 user token