Skip to content

Commit

Permalink
Validate expiration of Token at UserInfo endpoint
Browse files Browse the repository at this point in the history
Close #424
  • Loading branch information
tpazderka committed Feb 6, 2018
1 parent 9580ce8 commit e916933
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ The format is based on the [KeepAChangeLog] project.
- [#145] Successful token endpoint responses have correct no-cache headers
- [#352] Fixed broken windows test for ``test_provider_key_setup``.
- [#475] ``get_verify_key`` returns inactive ``sig`` keys for verification
- [#429] An expired token is not possible to use.

[#430]: https://github.com/OpenIDC/pyoidc/pull/430
[#427]: https://github.com/OpenIDC/pyoidc/pull/427
Expand All @@ -50,6 +51,7 @@ The format is based on the [KeepAChangeLog] project.
[#475]: https://github.com/OpenIDC/pyoidc/issues/475
[#478]: https://github.com/OpenIDC/pyoidc/issues/478
[#483]: https://github.com/OpenIDC/pyoidc/pull/483
[#429]: https://github.com/OpenIDC/pyoidc/issues/424

## 0.12.0 [2017-09-25]

Expand Down
3 changes: 3 additions & 0 deletions src/oic/oic/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -1221,6 +1221,9 @@ def _do_user_info(self, token, **kwargs):
logger.error('Wrong token type: {}'.format(typ))
raise FailedAuthentication("Wrong type of token")

if _sdb.access_token.is_expired():
return error(error='invalid_token', descr='Token is expired', status_code=401)

if _sdb.is_revoked(key):
return error(error="invalid_token", descr="Token is revoked",
status_code=401)
Expand Down
15 changes: 14 additions & 1 deletion src/oic/utils/sdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ def __init__(self, typ, lifetime=0, **kwargs):
self.type = typ
self.lifetime = lifetime
self.args = kwargs
self.issued_at = utc_time_sans_frac()

def __call__(self, sid, *args, **kwargs):
"""
Expand Down Expand Up @@ -128,7 +129,19 @@ def get_type(self, token):
raise NotImplementedError()

def expires_at(self):
return utc_time_sans_frac() + self.lifetime
return self.issued_at + self.lifetime

def is_expired(self, when=None):
"""Return if token is still valid."""
if when is None:
now = utc_time_sans_frac()
else:
now = when
eat = self.expires_at()
if now > eat:
return True
else:
return False

def invalidate(self, token):
pass
Expand Down
23 changes: 23 additions & 0 deletions tests/test_oic_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import pytest
import responses
from freezegun import freeze_time
from mock import Mock
from mock import patch
from requests import ConnectionError
Expand Down Expand Up @@ -705,6 +706,28 @@ def test_userinfo_endpoint(self):
ident = OpenIDSchema().deserialize(resp.message, "json")
assert _eq(ident.keys(), ['nickname', 'sub', 'name', 'email'])

def test_userinfo_endpoint_expired(self):
self.cons.client_secret = "drickyoughurt"
self.cons.config["response_type"] = ["token"]
self.cons.config["request_method"] = "parameter"
state, location = self.cons.begin("openid", "token", path="http://localhost:8087")

initial_datetime = datetime.datetime(2018, 2, 5, 10, 0, 0, 0)
final_datetime = datetime.datetime(2018, 2, 9, 10, 0, 0, 0)
with freeze_time(initial_datetime) as frozen:
resp = self.provider.authorization_endpoint(request=urlparse(location).query)

# redirect
atr = AuthorizationResponse().deserialize(urlparse(resp.message).fragment, "urlencoded")
frozen.move_to(final_datetime)

uir = UserInfoRequest(access_token=atr["access_token"], schema="openid")
resp = self.provider.userinfo_endpoint(request=uir.to_urlencoded())

message = json.loads(resp.message)
assert message['error'] == 'invalid_token'
assert message['error_description'] == 'Token is expired'

def test_userinfo_endpoint_extra_claim(self):
# We have to recreate the cache again
self.provider.extra_claims = ['extra_claim']
Expand Down
21 changes: 20 additions & 1 deletion tests/test_sdb.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import base64
import datetime
import hashlib
import hmac
import random
import time

import pytest
from freezegun import freeze_time

from oic.oic.message import AuthorizationRequest
from oic.oic.message import OpenIDRequest
Expand Down Expand Up @@ -71,7 +73,7 @@ def test_get_token(self):
class TestToken(object):
@pytest.fixture(autouse=True)
def create_token(self):
self.token = DefaultToken("secret", "password", lifetime={'': 60})
self.token = DefaultToken("secret", "password", lifetime=60)

def test_token(self):
sid = self.token.key(areq=AREQ)
Expand All @@ -95,6 +97,23 @@ def test_type_and_key(self):
assert part[0] == "A"
assert part[1] == sid

def test_expired_fresh(self):
_token = DefaultToken('secret', 'password', lifetime=60)
assert _token.is_expired() is False

def test_expired_stale(self):
initial_datetime = datetime.datetime(2018, 2, 5, 10, 0, 0, 0)
final_datetime = datetime.datetime(2018, 2, 5, 10, 1, 0, 0)
with freeze_time(initial_datetime) as frozen:
_token = DefaultToken('secret', 'password', lifetime=2)
frozen.move_to(final_datetime)
assert _token.is_expired() is True

def test_expired_when(self):
_token = DefaultToken('secret', 'password', lifetime=2)
when = time.time() + 5 # 5 seconds from now
assert _token.is_expired(when=when) is True


class TestSessionDB(object):
@pytest.fixture(autouse=True)
Expand Down

0 comments on commit e916933

Please sign in to comment.