Skip to content

Commit 891633b

Browse files
committed
client: extend authentication to support API token
API token is actually a tenant token, so there is no need to specify the tenant to be used, neither to request a new tenant token as API tokens have a longer validity period. (Also it is not possible to renew an API token.) When accessing the API using a tenant token, we can query the tenant name using a GraphQL query, this way can test the access and acquire the tenant name as well.
1 parent d1662b5 commit 891633b

3 files changed

Lines changed: 35 additions & 4 deletions

File tree

onekey_client/client.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
def _login_required(func):
2929
@functools.wraps(func)
3030
def wrapper(self, *args, **kwargs):
31-
if self._state.raw_id_token is None:
31+
if not self._state.tenants:
3232
raise errors.NotLoggedIn
3333

3434
return func(self, *args, **kwargs)
@@ -116,6 +116,21 @@ def login(self, email: str, password: str):
116116
self._state.email = email
117117
self._state.raw_id_token = json_res["id_token"]
118118

119+
def use_token(self, token: str):
120+
try:
121+
tenant_id, _ = token.split("/", 1)
122+
except ValueError:
123+
raise errors.InvalidAPIToken()
124+
125+
self._state.raw_tenant_token = token
126+
127+
self_query = load_query("get_self.graphql")
128+
response = self.query(self_query)
129+
self._state.email = response["user"]["email"]
130+
tenant = m.Tenant(id=tenant_id, name=response["tenant"]["name"])
131+
self._state.tenants = {tenant.name: tenant}
132+
self._state.tenant = tenant
133+
119134
def _post(self, path: str, headers: Optional[Dict] = None, **kwargs):
120135
response = self._client.post(path, headers=headers, **kwargs)
121136
response.raise_for_status()
@@ -163,7 +178,8 @@ def use_tenant(self, tenant: m.Tenant):
163178

164179
@_tenant_required
165180
def refresh_tenant_token(self):
166-
self.use_tenant(self._state.tenant)
181+
if self._state.raw_id_token is not None:
182+
self.use_tenant(self._state.tenant)
167183

168184
@_tenant_required
169185
def query(self, query: str, variables: Optional[Dict] = None, timeout=60):

onekey_client/errors.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,25 @@ def __init__(self, message: Optional[str] = None):
1212
class NotLoggedIn(ClientError):
1313
MESSAGE = (
1414
"You are not logged in yet. \n"
15-
"You have to call client.login(email, password) first."
15+
"You have to call client.login(email, password) first or provide API token with use_token()."
1616
)
1717

1818

1919
class TenantNotSelected(ClientError):
20-
MESSAGE = "You have to select a Tenant (Environment) with client.use_tenant(tenant) first."
20+
MESSAGE = (
21+
"You have to select a Tenant (Environment) with client.use_tenant(tenant) first"
22+
"or provide API token with use_token()."
23+
)
2124

2225

2326
class InvalidCABundle(ClientError):
2427
MESSAGE = "The CA bundle is invalid or doesn't exist."
2528

2629

30+
class InvalidAPIToken(ClientError):
31+
MESSAGE = "The API Token is invalid."
32+
33+
2734
class QueryError(ClientError):
2835
"""raised when a GraphQL query returns errors."""
2936

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
query {
2+
tenant {
3+
name
4+
}
5+
user {
6+
email
7+
}
8+
}

0 commit comments

Comments
 (0)