{"action":"created","issue":{"url":"https://api.github.com/repos/TACC/agavepy/issues/4","labels_url":"https://api.github.com/repos/TACC/agavepy/issues/4/labels{/name}","comments_url":"https://api.github.com/repos/TACC/agavepy/issues/4/comments","events_url":"https://api.github.com/repos/TACC/agavepy/issues/4/events","html_url":"https://github.com/TACC/agavepy/issues/4","id":123342244,"number":4,"title":"Using AgavePy to refresh a token maintained outside of AgavePy","user":{"login":"mrhanlon","id":630606,"avatar_url":"https://avatars.githubusercontent.com/u/630606?v=3","gravatar_id":"","url":"https://api.github.com/users/mrhanlon","html_url":"https://github.com/mrhanlon","followers_url":"https://api.github.com/users/mrhanlon/followers","following_url":"https://api.github.com/users/mrhanlon/following{/other_user}","gists_url":"https://api.github.com/users/mrhanlon/gists{/gist_id}","starred_url":"https://api.github.com/users/mrhanlon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrhanlon/subscriptions","organizations_url":"https://api.github.com/users/mrhanlon/orgs","repos_url":"https://api.github.com/users/mrhanlon/repos","events_url":"https://api.github.com/users/mrhanlon/events{/privacy}","received_events_url":"https://api.github.com/users/mrhanlon/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"milestone":null,"comments":3,"created_at":"2015-12-21T19:27:12Z","updated_at":"2016-01-04T15:19:12Z","closed_at":"2016-01-04T15:19:12Z","body":"This issue is migrated from [Bitbucket](https://bitbucket.org/taccaci/agavepy/issues/14).\r\n\r\nWhen using AgavePy as a library within a larger application (e.g., Django), we persist the user's OAuth token outside of the Agave object, e.g., in the session or in the database. It is useful, then, to be able to test and refresh the token directly without needing to make an additional API call. AgavePy will refresh the token automatically when an API call fails due to expired token, but this then results in two API calls, the response of which we don't need at all.\r\n\r\nIn Django we have an application/authentication backend that walks the authorization code flow to get a token and then calls the /profiles API to validate the token and get the user profile to determine the user's identity. Upon success we persist the token in the session. Later, we need to refresh the token when it expires. This is relatively easy to do using requests, but the preference would be to be able to instantiate an instance of `agavepy.agave.Token` and call `token.refresh()` directly to retrieve a new token.\r\n\r\nHere is what we are doing now. When we get a token (at login) we record with it the time of retrieval. Later, in a Django middleware class, we hook `process_request` to 1) to see if the token is still valid and if not, 2) refresh the token:\r\n\r\n```python\r\nclass _AgaveStub(object):\r\n    \"\"\"\r\n    A stub class to mimic the Agave object that is typically the \"parent\" of\r\n    agavepy.agave.Token\r\n    \"\"\"\r\n\r\n    def refresh_aris(self):\r\n        pass\r\n\r\nclass AgaveTokenRefreshMiddleware(object):\r\n\r\n    def process_request(self, request):\r\n        if request.path != '/logout/':\r\n            token_key = getattr(settings, 'AGAVE_TOKEN_SESSION_ID')\r\n            if token_key in request.session:\r\n                current_time = time.time()\r\n                token = request.session[token_key]\r\n                valid_seconds = token['created'] + token['expires_in'] - current_time\r\n                if valid_seconds <= 0:\r\n                    logger.debug('refreshing current token: %s' % token)\r\n                    agave_token = Token(None, None,\r\n                        getattr(settings, 'AGAVE_TENANT_BASEURL'),\r\n                        getattr(settings, 'AGAVE_CLIENT_KEY'),\r\n                        getattr(settings, 'AGAVE_CLIENT_SECRET'), True, _AgaveStub())\r\n                    agave_token.token_info = {'refresh_token': token['refresh_token']}\r\n                    agave_token.refresh()\r\n                    agave_token.token_info['created'] = current_time\r\n                    logger.info('refreshed token: %s' % agave_token.token_info)\r\n                    request.session[token_key] = agave_token.token_info\r\n                    request.session.save()\r\n                else:\r\n                    logger.debug('session.%s valid for %s additional seconds: %s' %\r\n                        (token_key, valid_seconds, token))\r\n\r\n\r\n```\r\n\r\nWe end up having to mock the `agavepy.agave.Agave` class, which `Token` expects to be it's `parent` attribute. We also need to manually set the `token_info` after instantiating the Token because there isn't a way to pass the `refresh_token` in the constructor. This works fine, but it is certainly not ideal.\r\n\r\nIt would be great if we could instantiate a Token efficiently and independently of the rest of AgavePy."},"comment":{"url":"https://api.github.com/repos/TACC/agavepy/issues/comments/168703831","html_url":"https://github.com/TACC/agavepy/issues/4#issuecomment-168703831","issue_url":"https://api.github.com/repos/TACC/agavepy/issues/4","id":168703831,"user":{"login":"mrhanlon","id":630606,"avatar_url":"https://avatars.githubusercontent.com/u/630606?v=3","gravatar_id":"","url":"https://api.github.com/users/mrhanlon","html_url":"https://github.com/mrhanlon","followers_url":"https://api.github.com/users/mrhanlon/followers","following_url":"https://api.github.com/users/mrhanlon/following{/other_user}","gists_url":"https://api.github.com/users/mrhanlon/gists{/gist_id}","starred_url":"https://api.github.com/users/mrhanlon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrhanlon/subscriptions","organizations_url":"https://api.github.com/users/mrhanlon/orgs","repos_url":"https://api.github.com/users/mrhanlon/repos","events_url":"https://api.github.com/users/mrhanlon/events{/privacy}","received_events_url":"https://api.github.com/users/mrhanlon/received_events","type":"User","site_admin":false},"created_at":"2016-01-04T15:19:12Z","updated_at":"2016-01-04T15:19:12Z","body":"Yes, this works fine. I don't know why I missed that before. Thanks."}}