Expire auth code after 10 minutes (#15381)

This commit is contained in:
Paulus Schoutsen 2018-07-10 11:20:22 +02:00 committed by GitHub
parent df8c59406b
commit dbdd0a1f56
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 2 deletions

View file

@ -102,6 +102,7 @@ a limited expiration.
"token_type": "Bearer" "token_type": "Bearer"
} }
""" """
from datetime import timedelta
import logging import logging
import uuid import uuid
@ -114,6 +115,7 @@ from homeassistant.helpers.data_entry_flow import (
FlowManagerIndexView, FlowManagerResourceView) FlowManagerIndexView, FlowManagerResourceView)
from homeassistant.components.http.view import HomeAssistantView from homeassistant.components.http.view import HomeAssistantView
from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.components.http.data_validator import RequestDataValidator
from homeassistant.util import dt as dt_util
from . import indieauth from . import indieauth
@ -349,12 +351,26 @@ def _create_cred_store():
def store_credentials(client_id, credentials): def store_credentials(client_id, credentials):
"""Store credentials and return a code to retrieve it.""" """Store credentials and return a code to retrieve it."""
code = uuid.uuid4().hex code = uuid.uuid4().hex
temp_credentials[(client_id, code)] = credentials temp_credentials[(client_id, code)] = (dt_util.utcnow(), credentials)
return code return code
@callback @callback
def retrieve_credentials(client_id, code): def retrieve_credentials(client_id, code):
"""Retrieve credentials.""" """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 return store_credentials, retrieve_credentials

View file

@ -1,4 +1,10 @@
"""Integration tests for the auth component.""" """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 . import async_setup_auth
from tests.common import CLIENT_ID, CLIENT_REDIRECT_URI from tests.common import CLIENT_ID, CLIENT_REDIRECT_URI
@ -58,3 +64,25 @@ async def test_login_new_user_and_refresh_token(hass, aiohttp_client):
'authorization': 'Bearer {}'.format(tokens['access_token']) 'authorization': 'Bearer {}'.format(tokens['access_token'])
}) })
assert resp.status == 200 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