2018-02-05 15:29:08 +00:00
|
|
|
import logging
|
|
|
|
|
|
|
|
from django.http import JsonResponse
|
|
|
|
|
|
|
|
from oidc_provider.lib.errors import TokenIntrospectionError
|
2018-04-23 13:59:56 +00:00
|
|
|
from oidc_provider.lib.utils.common import run_processing_hook
|
|
|
|
from oidc_provider.lib.utils.oauth2 import extract_client_auth
|
|
|
|
from oidc_provider.models import Token, Client
|
|
|
|
from oidc_provider import settings
|
2018-02-05 15:29:08 +00:00
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2018-04-23 13:59:56 +00:00
|
|
|
INTROSPECTION_SCOPE = 'token_introspection'
|
|
|
|
|
2018-02-05 15:29:08 +00:00
|
|
|
|
|
|
|
class TokenIntrospectionEndpoint(object):
|
|
|
|
|
|
|
|
def __init__(self, request):
|
|
|
|
self.request = request
|
|
|
|
self.params = {}
|
2018-04-23 13:59:56 +00:00
|
|
|
self.id_token = None
|
|
|
|
self.client = None
|
2018-02-05 15:29:08 +00:00
|
|
|
self._extract_params()
|
|
|
|
|
|
|
|
def _extract_params(self):
|
|
|
|
# Introspection only supports POST requests
|
|
|
|
self.params['token'] = self.request.POST.get('token')
|
2018-04-23 13:59:56 +00:00
|
|
|
client_id, client_secret = extract_client_auth(self.request)
|
|
|
|
self.params['client_id'] = client_id
|
|
|
|
self.params['client_secret'] = client_secret
|
2018-02-05 15:29:08 +00:00
|
|
|
|
|
|
|
def validate_params(self):
|
2018-04-23 13:59:56 +00:00
|
|
|
if not (self.params['client_id'] and self.params['client_secret']):
|
|
|
|
logger.debug('[Introspection] No client credentials provided')
|
2018-02-05 15:29:08 +00:00
|
|
|
raise TokenIntrospectionError()
|
|
|
|
if not self.params['token']:
|
|
|
|
logger.debug('[Introspection] No token provided')
|
|
|
|
raise TokenIntrospectionError()
|
|
|
|
try:
|
|
|
|
token = Token.objects.get(access_token=self.params['token'])
|
|
|
|
except Token.DoesNotExist:
|
|
|
|
logger.debug('[Introspection] Token does not exist: %s', self.params['token'])
|
|
|
|
raise TokenIntrospectionError()
|
|
|
|
if token.has_expired():
|
|
|
|
logger.debug('[Introspection] Token is not valid: %s', self.params['token'])
|
|
|
|
raise TokenIntrospectionError()
|
|
|
|
if not token.id_token:
|
2018-04-23 13:59:56 +00:00
|
|
|
logger.debug('[Introspection] Token not an authentication token: %s',
|
|
|
|
self.params['token'])
|
2018-02-05 15:29:08 +00:00
|
|
|
raise TokenIntrospectionError()
|
|
|
|
|
|
|
|
self.id_token = token.id_token
|
|
|
|
audience = self.id_token.get('aud')
|
|
|
|
if not audience:
|
|
|
|
logger.debug('[Introspection] No audience found for token: %s', self.params['token'])
|
|
|
|
raise TokenIntrospectionError()
|
|
|
|
|
|
|
|
try:
|
2018-04-23 13:59:56 +00:00
|
|
|
self.client = Client.objects.get(
|
|
|
|
client_id=self.params['client_id'],
|
|
|
|
client_secret=self.params['client_secret'])
|
|
|
|
except Client.DoesNotExist:
|
|
|
|
logger.debug('[Introspection] No valid client for id: %s',
|
|
|
|
self.params['client_id'])
|
|
|
|
raise TokenIntrospectionError()
|
|
|
|
if INTROSPECTION_SCOPE not in self.client.scope:
|
|
|
|
logger.debug('[Introspection] Client %s does not have introspection scope',
|
|
|
|
self.params['client_id'])
|
|
|
|
raise TokenIntrospectionError()
|
|
|
|
if settings.get('OIDC_INTROSPECTION_VALIDATE_AUDIENCE_SCOPE') \
|
|
|
|
and audience not in self.client.scope:
|
|
|
|
logger.debug('[Introspection] Client %s does not audience scope %s',
|
|
|
|
self.params['client_id'], audience)
|
2018-02-05 15:29:08 +00:00
|
|
|
raise TokenIntrospectionError()
|
|
|
|
|
|
|
|
def create_response_dic(self):
|
|
|
|
response_dic = dict((k, self.id_token[k]) for k in ('sub', 'exp', 'iat', 'iss'))
|
|
|
|
response_dic['active'] = True
|
|
|
|
response_dic['client_id'] = self.id_token.get('aud')
|
2018-04-23 13:59:56 +00:00
|
|
|
response_dic['aud'] = self.client.client_id
|
2018-02-05 15:29:08 +00:00
|
|
|
|
2018-04-23 13:59:56 +00:00
|
|
|
response_dic = run_processing_hook(response_dic,
|
|
|
|
'OIDC_INTROSPECTION_PROCESSING_HOOK',
|
|
|
|
client=self.client,
|
2018-02-05 15:29:08 +00:00
|
|
|
id_token=self.id_token)
|
|
|
|
|
|
|
|
return response_dic
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def response(cls, dic, status=200):
|
|
|
|
"""
|
|
|
|
Create and return a response object.
|
|
|
|
"""
|
|
|
|
response = JsonResponse(dic, status=status)
|
|
|
|
response['Cache-Control'] = 'no-store'
|
|
|
|
response['Pragma'] = 'no-cache'
|
|
|
|
|
|
|
|
return response
|