Skip to content

Commit

Permalink
Add refresh token endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
roshii committed May 19, 2023
1 parent 2b29e67 commit ff00830
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 10 deletions.
27 changes: 23 additions & 4 deletions jmclient/jmclient/wallet_rpc.py
Expand Up @@ -358,12 +358,11 @@ def yieldgenerator_report_unavailable(self, request, failure):
request.setResponseCode(404)
return self.err(request, "Yield generator report not available.")

def check_cookie(self, request):
#part after bearer is what we need
def check_cookie(self, request, *, verify_exp: bool = True):
try:
auth_header = request.getHeader("Authorization")
# Token itself is stated after `Bearer ` prefix, it must be removed
self.token.verify(auth_header[7:])
access_token = request.getHeader("Authorization")[7:]
self.token.verify(access_token, verify_exp=verify_exp)
except Exception as e:
jlog.debug(e)
raise NotAuthorized()
Expand Down Expand Up @@ -561,6 +560,26 @@ def preflight(self, request):
request.setHeader("Access-Control-Allow-Methods", "POST")

with app.subroute(api_version_string) as app:
@app.route('/token/refresh', methods=['POST'])
def refresh(self, request):
try:
self.check_cookie(request, verify_exp=False)

assert isinstance(request.content, BytesIO)
refresh_token = self.get_POST_body(request, ["refresh_token"])["refresh_token"]
self.token.verify(refresh_token, is_refresh=True)

return make_jmwalletd_response(
request,
walletname=self.wallet_name,
**self.token.issue(),
)
except:
return make_jmwalletd_response(
request,
status=401,
)

@app.route('/wallet/<string:walletname>/display', methods=['GET'])
def displaywallet(self, request, walletname):
print_req(request)
Expand Down
96 changes: 90 additions & 6 deletions jmclient/test/test_wallet_rpc.py
@@ -1,11 +1,12 @@
import os, json
import datetime
import json
import os

import jwt
import pytest
from twisted.internet import reactor, defer, task

from twisted.web.client import readBody, Headers
from twisted.trial import unittest

from autobahn.twisted.websocket import WebSocketClientFactory, \
connectWS

Expand Down Expand Up @@ -35,10 +36,10 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.token = test_token_authority

def check_cookie(self, request):
def check_cookie(self, request, *args, **kwargs):
if self.auth_disabled:
return True
return super().check_cookie(request)
return super().check_cookie(request, *args, **kwargs)

class WalletRPCTestBase(object):
""" Base class for set up of tests of the
Expand Down Expand Up @@ -84,6 +85,7 @@ def setUp(self):
# (and don't use wallet files yet), we won't have set a wallet name,
# so we set it here:
self.daemon.wallet_name = self.get_wallet_file_name(1)
self.daemon.token.wallet_name = self.daemon.wallet_name
r, s = self.daemon.startService()
self.listener_rpc = r
self.listener_ws = s
Expand Down Expand Up @@ -119,7 +121,7 @@ def get_wallet_file_name(self, i, fullpath=False):
@defer.inlineCallbacks
def do_request(self, agent, method, addr, body, handler, token=None):
if token:
headers = Headers({"Authorization": ["Bearer " + self.jwt_token]})
headers = Headers({"Authorization": ["Bearer " + token]})
else:
headers = None
response = yield agent.request(method, addr, headers, bodyProducer=body)
Expand Down Expand Up @@ -669,6 +671,88 @@ def process_get_seed_response(self, response, code):
json_body = json.loads(response.decode('utf-8'))
assert json_body["seedphrase"]

class TrialTestWRPC_JWT(WalletRPCTestBase, unittest.TestCase):
@defer.inlineCallbacks
def test_refresh_token(self):
"""Test refresh token endpoint
1. Valid access, valid refresh -> Success
2. Valid access, invalid refresh -> Failed
3. Valid access, expired refresh -> Failed
4. Expired access, valid refresh -> Success
5. Expired access, invalid refresh -> Failed
6. Expired access, expired refresh -> Failed
7. Invalid access, valid refresh -> Failed
"""

agent = get_nontor_agent()
addr = self.get_route_root()
addr += "/token/refresh"
addr = addr.encode()

for access_token, refresh_token, is_successful_response in [
("valid", "valid", True),
("valid", "invalid", False),
("valid", "expired", False),
("expired", "valid", True),
("expired", "invalid", False),
("expired", "expired", False),
("invalid", "valid", False),
]:
access_token, refresh_token, is_successful_response = "valid", "valid", True
body = BytesProducer(
json.dumps(
{
"refresh_token": self.get_token(
expired=refresh_token == "expired",
invalid=refresh_token == "invalid",
refresh=True,
)
}
).encode()
)
response_handler = (
self.successful_refresh_response_handler
if is_successful_response
else self.failed_refresh_response_handler
)
yield self.do_request(
agent,
b"POST",
addr,
body,
response_handler,
self.get_token(
expired=access_token == "expired",
invalid=access_token == "invalid",
),
)

def failed_refresh_response_handler(self, response, code):
assert code == 401

def successful_refresh_response_handler(self, response, code):
assert code == 200
json_body = json.loads(response.decode("utf-8"))
assert json_body.get("token")
assert json_body.get("refresh_token")

def get_token(self, *, expired=False, invalid=False, refresh=False):
if invalid:
return jwt.encode({"scope": "invalid"}, "12345678")

if expired:
exp = datetime.datetime.utcnow() - datetime.timedelta(hours=1)
else:
exp = datetime.datetime.utcnow() + datetime.timedelta(hours=1)

t = jwt.encode(
{"exp": exp, "scope": f"walletrpc {self.daemon.wallet_name}"},
getattr(test_token_authority.signature, "refresh" if refresh else "access"),
algorithm=test_token_authority.signature.algorithm,
)
return t

"""
Sample listutxos response for reference:
Expand Down

0 comments on commit ff00830

Please sign in to comment.