|
|
@ -1,20 +1,47 @@ |
|
|
|
#!/usr/bin/env python3 |
|
|
|
#!/usr/bin/env python3 |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
Usage:: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
usage: auth.py [-h] [{google,apple,github,jwt}] [jwt] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Login to your comma account |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
positional arguments: |
|
|
|
|
|
|
|
{google,apple,github,jwt} |
|
|
|
|
|
|
|
jwt |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
optional arguments: |
|
|
|
|
|
|
|
-h, --help show this help message and exit |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Examples:: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
./auth.py # Log in with google account |
|
|
|
|
|
|
|
./auth.py github # Log in with GitHub Account |
|
|
|
|
|
|
|
./auth.py jwt ey......hw # Log in with a JWT from https://jwt.comma.ai, for use in CI |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import argparse |
|
|
|
import sys |
|
|
|
import sys |
|
|
|
|
|
|
|
import pprint |
|
|
|
import webbrowser |
|
|
|
import webbrowser |
|
|
|
from http.server import HTTPServer, BaseHTTPRequestHandler |
|
|
|
from http.server import BaseHTTPRequestHandler, HTTPServer |
|
|
|
from urllib.parse import urlencode, parse_qs |
|
|
|
from typing import Any, Dict |
|
|
|
from tools.lib.api import CommaApi, APIError |
|
|
|
from urllib.parse import parse_qs, urlencode |
|
|
|
from tools.lib.auth_config import set_token |
|
|
|
|
|
|
|
from typing import Dict, Any |
|
|
|
from tools.lib.api import APIError, CommaApi, UnauthorizedError |
|
|
|
|
|
|
|
from tools.lib.auth_config import set_token, get_token |
|
|
|
|
|
|
|
|
|
|
|
PORT = 3000 |
|
|
|
PORT = 3000 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ClientRedirectServer(HTTPServer): |
|
|
|
class ClientRedirectServer(HTTPServer): |
|
|
|
query_params: Dict[str, Any] = {} |
|
|
|
query_params: Dict[str, Any] = {} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ClientRedirectHandler(BaseHTTPRequestHandler): |
|
|
|
class ClientRedirectHandler(BaseHTTPRequestHandler): |
|
|
|
def do_GET(self): |
|
|
|
def do_GET(self): |
|
|
|
if not self.path.startswith('/auth/g/redirect'): |
|
|
|
if not self.path.startswith('/auth'): |
|
|
|
self.send_response(204) |
|
|
|
self.send_response(204) |
|
|
|
return |
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
@ -30,21 +57,48 @@ class ClientRedirectHandler(BaseHTTPRequestHandler): |
|
|
|
def log_message(self, format, *args): # pylint: disable=redefined-builtin |
|
|
|
def log_message(self, format, *args): # pylint: disable=redefined-builtin |
|
|
|
pass # this prevent http server from dumping messages to stdout |
|
|
|
pass # this prevent http server from dumping messages to stdout |
|
|
|
|
|
|
|
|
|
|
|
def auth_redirect_link(): |
|
|
|
|
|
|
|
redirect_uri = f'http://localhost:{PORT}/auth/g/redirect' |
|
|
|
def auth_redirect_link(method): |
|
|
|
|
|
|
|
provider_id = { |
|
|
|
|
|
|
|
'google': 'g', |
|
|
|
|
|
|
|
'apple': 'a', |
|
|
|
|
|
|
|
'github': 'h', |
|
|
|
|
|
|
|
}[method] |
|
|
|
|
|
|
|
|
|
|
|
params = { |
|
|
|
params = { |
|
|
|
'type': 'web_server', |
|
|
|
'redirect_uri': f"https://api.comma.ai/v2/auth/{provider_id}/redirect/", |
|
|
|
'client_id': '45471411055-ornt4svd2miog6dnopve7qtmh5mnu6id.apps.googleusercontent.com', |
|
|
|
'state': f'service,localhost:{PORT}', |
|
|
|
'redirect_uri': redirect_uri, |
|
|
|
|
|
|
|
'response_type': 'code', |
|
|
|
|
|
|
|
'scope': 'https://www.googleapis.com/auth/userinfo.email', |
|
|
|
|
|
|
|
'prompt': 'select_account', |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return (redirect_uri, 'https://accounts.google.com/o/oauth2/auth?' + urlencode(params)) |
|
|
|
if method == 'google': |
|
|
|
|
|
|
|
params.update({ |
|
|
|
|
|
|
|
'type': 'web_server', |
|
|
|
|
|
|
|
'client_id': '45471411055-ornt4svd2miog6dnopve7qtmh5mnu6id.apps.googleusercontent.com', |
|
|
|
|
|
|
|
'response_type': 'code', |
|
|
|
|
|
|
|
'scope': 'https://www.googleapis.com/auth/userinfo.email', |
|
|
|
|
|
|
|
'prompt': 'select_account', |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
return 'https://accounts.google.com/o/oauth2/auth?' + urlencode(params) |
|
|
|
|
|
|
|
elif method == 'github': |
|
|
|
|
|
|
|
params.update({ |
|
|
|
|
|
|
|
'client_id': '28c4ecb54bb7272cb5a4', |
|
|
|
|
|
|
|
'scope': 'read:user', |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
return 'https://github.com/login/oauth/authorize?' + urlencode(params) |
|
|
|
|
|
|
|
elif method == 'apple': |
|
|
|
|
|
|
|
params.update({ |
|
|
|
|
|
|
|
'client_id': 'ai.comma.login', |
|
|
|
|
|
|
|
'response_type': 'code', |
|
|
|
|
|
|
|
'response_mode': 'form_post', |
|
|
|
|
|
|
|
'scope': 'name email', |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
return 'https://appleid.apple.com/auth/authorize?' + urlencode(params) |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
raise NotImplementedError(f"no redirect implemented for method {method}") |
|
|
|
|
|
|
|
|
|
|
|
def login(): |
|
|
|
|
|
|
|
redirect_uri, oauth_uri = auth_redirect_link() |
|
|
|
def login(method): |
|
|
|
|
|
|
|
oauth_uri = auth_redirect_link(method) |
|
|
|
|
|
|
|
|
|
|
|
web_server = ClientRedirectServer(('localhost', PORT), ClientRedirectHandler) |
|
|
|
web_server = ClientRedirectServer(('localhost', PORT), ClientRedirectHandler) |
|
|
|
print(f'To sign in, use your browser and navigate to {oauth_uri}') |
|
|
|
print(f'To sign in, use your browser and navigate to {oauth_uri}') |
|
|
@ -53,7 +107,6 @@ def login(): |
|
|
|
while True: |
|
|
|
while True: |
|
|
|
web_server.handle_request() |
|
|
|
web_server.handle_request() |
|
|
|
if 'code' in web_server.query_params: |
|
|
|
if 'code' in web_server.query_params: |
|
|
|
code = web_server.query_params['code'] |
|
|
|
|
|
|
|
break |
|
|
|
break |
|
|
|
elif 'error' in web_server.query_params: |
|
|
|
elif 'error' in web_server.query_params: |
|
|
|
print('Authentication Error: "%s". Description: "%s" ' % ( |
|
|
|
print('Authentication Error: "%s". Description: "%s" ' % ( |
|
|
@ -62,11 +115,31 @@ def login(): |
|
|
|
break |
|
|
|
break |
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
|
try: |
|
|
|
auth_resp = CommaApi().post('v2/auth/', data={'code': code, 'redirect_uri': redirect_uri}) |
|
|
|
auth_resp = CommaApi().post('v2/auth/', data={'code': web_server.query_params['code'], 'provider': web_server.query_params['provider']}) |
|
|
|
set_token(auth_resp['access_token']) |
|
|
|
set_token(auth_resp['access_token']) |
|
|
|
print('Authenticated') |
|
|
|
|
|
|
|
except APIError as e: |
|
|
|
except APIError as e: |
|
|
|
print(f'Authentication Error: {e}', file=sys.stderr) |
|
|
|
print(f'Authentication Error: {e}', file=sys.stderr) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
|
if __name__ == '__main__': |
|
|
|
login() |
|
|
|
parser = argparse.ArgumentParser(description='Login to your comma account') |
|
|
|
|
|
|
|
parser.add_argument('method', default='google', const='google', nargs='?', choices=['google', 'apple', 'github', 'jwt']) |
|
|
|
|
|
|
|
parser.add_argument('jwt', nargs='?') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
args = parser.parse_args() |
|
|
|
|
|
|
|
if args.method == 'jwt': |
|
|
|
|
|
|
|
if args.jwt is None: |
|
|
|
|
|
|
|
print("method JWT selected, but no JWT was provided") |
|
|
|
|
|
|
|
exit(1) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
set_token(args.jwt) |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
login(args.method) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
|
|
|
|
|
me = CommaApi(token=get_token()).get('/v1/me') |
|
|
|
|
|
|
|
print("Authenticated!") |
|
|
|
|
|
|
|
pprint.pprint(me) |
|
|
|
|
|
|
|
except UnauthorizedError: |
|
|
|
|
|
|
|
print("Got invalid JWT") |
|
|
|
|
|
|
|
exit(1) |
|
|
|