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!")