Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions homeassistant/components/auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
"token_type": "Bearer"
}
"""
from datetime import timedelta
import logging
import uuid

Expand All @@ -114,6 +115,7 @@
FlowManagerIndexView, FlowManagerResourceView)
from homeassistant.components.http.view import HomeAssistantView
from homeassistant.components.http.data_validator import RequestDataValidator
from homeassistant.util import dt as dt_util

from . import indieauth

Expand Down Expand Up @@ -349,12 +351,26 @@ def _create_cred_store():
def store_credentials(client_id, credentials):
"""Store credentials and return a code to retrieve it."""
code = uuid.uuid4().hex
temp_credentials[(client_id, code)] = credentials
temp_credentials[(client_id, code)] = (dt_util.utcnow(), credentials)
return code

@callback
def retrieve_credentials(client_id, code):
"""Retrieve credentials."""
return temp_credentials.pop((client_id, code), None)
key = (client_id, code)

if key not in temp_credentials:
return None

created, credentials = temp_credentials.pop(key)

# OAuth 4.2.1
# The authorization code MUST expire shortly after it is issued to
# mitigate the risk of leaks. A maximum authorization code lifetime of
# 10 minutes is RECOMMENDED.
if dt_util.utcnow() - created < timedelta(minutes=10):
return credentials

return None

return store_credentials, retrieve_credentials
28 changes: 28 additions & 0 deletions tests/components/auth/test_init.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
"""Integration tests for the auth component."""
from datetime import timedelta
from unittest.mock import patch

from homeassistant.util.dt import utcnow
from homeassistant.components import auth

from . import async_setup_auth

from tests.common import CLIENT_ID, CLIENT_REDIRECT_URI
Expand Down Expand Up @@ -58,3 +64,25 @@ async def test_login_new_user_and_refresh_token(hass, aiohttp_client):
'authorization': 'Bearer {}'.format(tokens['access_token'])
})
assert resp.status == 200


def test_credential_store_expiration():
"""Test that the credential store will not return expired tokens."""
store, retrieve = auth._create_cred_store()
client_id = 'bla'
credentials = 'creds'
now = utcnow()

with patch('homeassistant.util.dt.utcnow', return_value=now):
code = store(client_id, credentials)

with patch('homeassistant.util.dt.utcnow',
return_value=now + timedelta(minutes=10)):
assert retrieve(client_id, code) is None

with patch('homeassistant.util.dt.utcnow', return_value=now):
code = store(client_id, credentials)

with patch('homeassistant.util.dt.utcnow',
return_value=now + timedelta(minutes=9, seconds=59)):
assert retrieve(client_id, code) == credentials