Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Commit

Permalink
Stop putting a time caveat on access tokens
Browse files Browse the repository at this point in the history
The 'time' caveat on the access tokens was something of a lie, since we weren't
enforcing it; more pertinently its presence stops us ever adding useful time
caveats.

Let's move in the right direction by not lying in our caveats.
  • Loading branch information
richvdh committed Nov 28, 2016
1 parent b614653 commit d32a300
Show file tree
Hide file tree
Showing 7 changed files with 17 additions and 40 deletions.
4 changes: 4 additions & 0 deletions synapse/api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,10 @@ def validate_macaroon(self, macaroon, type_string, verify_expiry, user_id):
else:
v.satisfy_general(lambda c: c.startswith("time < "))

# access_tokens and refresh_tokens include a nonce for uniqueness: any
# value is acceptable
v.satisfy_general(lambda c: c.startswith("nonce = "))

v.verify(macaroon, self.hs.config.macaroon_secret_key)

def _verify_expiry(self, caveat):
Expand Down
6 changes: 0 additions & 6 deletions synapse/config/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ def read_config(self, config):
)

self.registration_shared_secret = config.get("registration_shared_secret")
self.user_creation_max_duration = int(config["user_creation_max_duration"])

self.bcrypt_rounds = config.get("bcrypt_rounds", 12)
self.trusted_third_party_id_servers = config["trusted_third_party_id_servers"]
Expand All @@ -55,11 +54,6 @@ def default_config(self, **kwargs):
# secret, even if registration is otherwise disabled.
registration_shared_secret: "%(registration_shared_secret)s"
# Sets the expiry for the short term user creation in
# milliseconds. For instance the bellow duration is two weeks
# in milliseconds.
user_creation_max_duration: 1209600000
# Set the number of bcrypt rounds used to generate password hash.
# Larger numbers increase the work factor needed to generate the hash.
# The default number of rounds is 12.
Expand Down
18 changes: 6 additions & 12 deletions synapse/handlers/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,21 +531,15 @@ def issue_access_token(self, user_id, device_id=None):
device_id)
defer.returnValue(access_token)

@defer.inlineCallbacks
def issue_refresh_token(self, user_id, device_id=None):
refresh_token = self.generate_refresh_token(user_id)
yield self.store.add_refresh_token_to_user(user_id, refresh_token,
device_id)
defer.returnValue(refresh_token)

def generate_access_token(self, user_id, extra_caveats=None,
duration_in_ms=(60 * 60 * 1000)):
def generate_access_token(self, user_id, extra_caveats=None):
extra_caveats = extra_caveats or []
macaroon = self._generate_base_macaroon(user_id)
macaroon.add_first_party_caveat("type = access")
now = self.hs.get_clock().time_msec()
expiry = now + duration_in_ms
macaroon.add_first_party_caveat("time < %d" % (expiry,))
# Include a nonce, to make sure that each login gets a different
# access token.
macaroon.add_first_party_caveat("nonce = %s" % (
stringutils.random_string_with_symbols(16),
))
for caveat in extra_caveats:
macaroon.add_first_party_caveat(caveat)
return macaroon.serialize()
Expand Down
5 changes: 2 additions & 3 deletions synapse/handlers/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ def _submit_captcha(self, ip_addr, private_key, challenge, response):
defer.returnValue(data)

@defer.inlineCallbacks
def get_or_create_user(self, requester, localpart, displayname, duration_in_ms,
def get_or_create_user(self, requester, localpart, displayname,
password_hash=None):
"""Creates a new user if the user does not exist,
else revokes all previous access tokens and generates a new one.
Expand Down Expand Up @@ -399,8 +399,7 @@ def get_or_create_user(self, requester, localpart, displayname, duration_in_ms,

user = UserID(localpart, self.hs.hostname)
user_id = user.to_string()
token = self.auth_handler().generate_access_token(
user_id, None, duration_in_ms)
token = self.auth_handler().generate_access_token(user_id)

if need_register:
yield self.store.register(
Expand Down
12 changes: 0 additions & 12 deletions synapse/rest/client/v1/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,6 @@ class CreateUserRestServlet(ClientV1RestServlet):
def __init__(self, hs):
super(CreateUserRestServlet, self).__init__(hs)
self.store = hs.get_datastore()
self.direct_user_creation_max_duration = hs.config.user_creation_max_duration
self.handlers = hs.get_handlers()

@defer.inlineCallbacks
Expand Down Expand Up @@ -418,18 +417,8 @@ def _do_create(self, requester, user_json):
if "displayname" not in user_json:
raise SynapseError(400, "Expected 'displayname' key.")

if "duration_seconds" not in user_json:
raise SynapseError(400, "Expected 'duration_seconds' key.")

localpart = user_json["localpart"].encode("utf-8")
displayname = user_json["displayname"].encode("utf-8")
duration_seconds = 0
try:
duration_seconds = int(user_json["duration_seconds"])
except ValueError:
raise SynapseError(400, "Failed to parse 'duration_seconds'")
if duration_seconds > self.direct_user_creation_max_duration:
duration_seconds = self.direct_user_creation_max_duration
password_hash = user_json["password_hash"].encode("utf-8") \
if user_json.get("password_hash") else None

Expand All @@ -438,7 +427,6 @@ def _do_create(self, requester, user_json):
requester=requester,
localpart=localpart,
displayname=displayname,
duration_in_ms=(duration_seconds * 1000),
password_hash=password_hash
)

Expand Down
6 changes: 3 additions & 3 deletions tests/handlers/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,14 @@ def verify_user(caveat):
def verify_type(caveat):
return caveat == "type = access"

def verify_expiry(caveat):
return caveat == "time < 8600000"
def verify_nonce(caveat):
return caveat.startswith("nonce =")

v = pymacaroons.Verifier()
v.satisfy_general(verify_gen)
v.satisfy_general(verify_user)
v.satisfy_general(verify_type)
v.satisfy_general(verify_expiry)
v.satisfy_general(verify_nonce)
v.verify(macaroon, self.hs.config.macaroon_secret_key)

def test_short_term_login_token_gives_user_id(self):
Expand Down
6 changes: 2 additions & 4 deletions tests/handlers/test_register.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,12 @@ def setUp(self):

@defer.inlineCallbacks
def test_user_is_created_and_logged_in_if_doesnt_exist(self):
duration_ms = 200
local_part = "someone"
display_name = "someone"
user_id = "@someone:test"
requester = create_requester("@as:test")
result_user_id, result_token = yield self.handler.get_or_create_user(
requester, local_part, display_name, duration_ms)
requester, local_part, display_name)
self.assertEquals(result_user_id, user_id)
self.assertEquals(result_token, 'secret')

Expand All @@ -71,12 +70,11 @@ def test_if_user_exists(self):
user_id=frank.to_string(),
token="jkv;g498752-43gj['eamb!-5",
password_hash=None)
duration_ms = 200
local_part = "frank"
display_name = "Frank"
user_id = "@frank:test"
requester = create_requester("@as:test")
result_user_id, result_token = yield self.handler.get_or_create_user(
requester, local_part, display_name, duration_ms)
requester, local_part, display_name)
self.assertEquals(result_user_id, user_id)
self.assertEquals(result_token, 'secret')

0 comments on commit d32a300

Please sign in to comment.