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://oqtant.infleqtion.com/oqtant',
signin_local_callback_url='http://localhost:8080',
base_url='https://oqtant.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:
verifier =
'WZtKSU9nBiFJQQEIk0YyiNJ7gyisLy8GGZGbadM0YWzaHgV0BeM8Jc5JuKkFOmdP5EHXLWMHV0xWPnPRZp-ezfRL_gfvH3aBWzbeAqiIa0c'
def
generate_challenge(verifier:
str) -> str:
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):
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