diff --git a/funkwhale_cli/api.py b/funkwhale_cli/api.py index c29b51ba3b29e2641014ddb1c44a710a362f5fb5..a594744d7a2643302de3d92807fd5120e03ae2e2 100644 --- a/funkwhale_cli/api.py +++ b/funkwhale_cli/api.py @@ -1,3 +1,6 @@ +import urllib.parse +import webbrowser + import aiohttp from . import exceptions @@ -56,6 +59,40 @@ async def get_jwt_token(session, url, username, password): return (await response.json())["token"] +async def get_oauth_token(session, url, client_id, client_secret): + args = {"response_type": "code", "redirect_uri": "urn:ietf:wg:oauth:2.0:oob", "client_id": client_id, + "client_secret": client_secret, "scope": "read write"} + browser_url = f"{url}authorize?{urllib.parse.urlencode(args)}" + webbrowser.open(browser_url) + code = input("Enter the code from funkwhale") + api_url = f"{url}api/v1/oauth/token/" + response = await session.post( + api_url, data={"client_id": client_id, "client_secret": client_secret, + "grant_type": "authorization_code", "code": code} + ) + return await extract_tokens(response) + + +async def refresh_oauth_token(session, url, client_id, client_secret, refresh_token): + api_url = f"{url}api/v1/oauth/token/" + response = await session.post( + api_url, data={"client_id": client_id, "client_secret": client_secret, + "grant_type": "refresh_token", "refresh_token": refresh_token} + ) + return await extract_tokens(response) + + +async def extract_tokens(response): + if response.status == 400: + raise exceptions.AuthenticationError( + "Unable to log in with provided credentials" + ) + response_json = await response.json() + access_token = response_json["access_token"] + refresh_token = response_json["refresh_token"] + return access_token, refresh_token + + class API(object): def __init__(self, base_url, token): self.base_url = base_url diff --git a/funkwhale_cli/cli/auth.py b/funkwhale_cli/cli/auth.py index befc482585c20b9ce33e0c37f5a9ba08bcacae76..64a0c22b31c9fe8d7b34a87f33c92889f91485a8 100644 --- a/funkwhale_cli/cli/auth.py +++ b/funkwhale_cli/cli/auth.py @@ -95,9 +95,62 @@ async def login(ctx, username, password): click.echo("Login successfull!") +@base.cli.command() +@click.option("-i", "--client-id", envvar="FUNKWHALE_CLIENT_ID", prompt=True) +@click.option( + "-s", "--client-secret", envvar="FUNKWHALE_CLIENT_SECRET", prompt=True, hide_input=True +) +@click.pass_context +@base.async_command +async def login_oauth(ctx, client_id, client_secret): + async with api.get_session() as session: + access_token, refresh_token = await api.get_oauth_token( + session, ctx.obj["SERVER_URL"], client_id=client_id, client_secret=client_secret + ) + + process_access_tokens(ctx, access_token, refresh_token) + click.echo("Login successful!") + + +@base.cli.command() +@click.option("-i", "--client-id", envvar="FUNKWHALE_CLIENT_ID", prompt=True) +@click.option( + "-s", "--client-secret", envvar="FUNKWHALE_CLIENT_SECRET", prompt=True, hide_input=True +) +@click.pass_context +@base.async_command +async def refresh_auth(ctx, client_id, client_secret): + refresh_token = keyring.get_password(ctx.obj["SERVER_URL"], "refresh") + async with api.get_session() as session: + access_token, refresh_token = await api.refresh_oauth_token( + session, ctx.obj["SERVER_URL"], client_id=client_id, client_secret=client_secret, + refresh_token=refresh_token + ) + + process_access_tokens(ctx, access_token, refresh_token) + click.echo("Login successful, tokens are refreshed!") + + +def process_access_tokens(ctx, access_token, refresh_token): + try: + keyring.set_password(ctx.obj["SERVER_URL"], "_", access_token) + keyring.set_password(ctx.obj["SERVER_URL"], "refresh", refresh_token) + except ValueError as e: + raise click.ClickException( + "Error while saving password to keyring: {}.".format( + e.args[0] + ) + ) + except Exception as e: + raise click.ClickException( + "Error while saving password to keyring: {}".format(e.args[0]) + ) + + @base.cli.command() @click.pass_context @base.async_command async def logout(ctx): keyring.delete_password(ctx.obj["SERVER_URL"], "_") - click.echo("Logout successfull!") + keyring.delete_password(ctx.obj["SERVER_URL"], "refresh") + click.echo("Logout successful!")