2015-01-08 20:55:24 +00:00
|
|
|
from datetime import timedelta
|
2016-12-07 11:15:47 +00:00
|
|
|
import time
|
2015-03-02 20:37:54 +00:00
|
|
|
import uuid
|
|
|
|
|
2016-10-03 15:54:54 +00:00
|
|
|
from Cryptodome.PublicKey.RSA import importKey
|
2016-12-07 12:14:37 +00:00
|
|
|
from django.utils import dateformat, timezone
|
2016-01-25 20:52:24 +00:00
|
|
|
from jwkest.jwk import RSAKey as jwk_RSAKey
|
2016-03-22 19:17:56 +00:00
|
|
|
from jwkest.jwk import SYMKey
|
2015-07-27 14:33:28 +00:00
|
|
|
from jwkest.jws import JWS
|
2016-10-31 20:07:06 +00:00
|
|
|
from jwkest.jwt import JWT
|
2015-03-02 20:37:54 +00:00
|
|
|
|
2018-02-05 15:29:08 +00:00
|
|
|
from oidc_provider.lib.utils.common import get_issuer, run_processing_hook
|
2017-12-14 17:03:15 +00:00
|
|
|
from oidc_provider.lib.claims import StandardScopeClaims
|
2016-08-11 22:05:13 +00:00
|
|
|
from oidc_provider.models import (
|
|
|
|
Code,
|
|
|
|
RSAKey,
|
|
|
|
Token,
|
|
|
|
)
|
2015-02-18 18:07:22 +00:00
|
|
|
from oidc_provider import settings
|
2015-01-08 20:55:24 +00:00
|
|
|
|
|
|
|
|
2018-04-10 21:41:38 +00:00
|
|
|
def create_id_token(token, user, aud, nonce='', at_hash='', request=None, scope=None):
|
2015-01-28 18:19:36 +00:00
|
|
|
"""
|
2016-09-09 14:43:28 +00:00
|
|
|
Creates the id_token dictionary.
|
2015-01-08 20:55:24 +00:00
|
|
|
See: http://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
|
|
|
Return a dic.
|
2015-01-28 18:19:36 +00:00
|
|
|
"""
|
2017-08-08 22:41:42 +00:00
|
|
|
if scope is None:
|
|
|
|
scope = []
|
2016-01-12 18:17:22 +00:00
|
|
|
sub = settings.get('OIDC_IDTOKEN_SUB_GENERATOR', import_str=True)(user=user)
|
2015-04-29 21:55:48 +00:00
|
|
|
|
2015-02-26 19:14:36 +00:00
|
|
|
expires_in = settings.get('OIDC_IDTOKEN_EXPIRE')
|
2015-01-08 20:55:24 +00:00
|
|
|
|
|
|
|
# Convert datetimes into timestamps.
|
2016-12-07 12:14:37 +00:00
|
|
|
now = int(time.time())
|
|
|
|
iat_time = now
|
|
|
|
exp_time = int(now + expires_in)
|
2015-04-29 21:55:48 +00:00
|
|
|
user_auth_time = user.last_login or user.date_joined
|
2016-12-07 12:14:37 +00:00
|
|
|
auth_time = int(dateformat.format(user_auth_time, 'U'))
|
2015-01-08 20:55:24 +00:00
|
|
|
|
|
|
|
dic = {
|
2016-05-25 21:58:58 +00:00
|
|
|
'iss': get_issuer(request=request),
|
2015-03-02 20:37:54 +00:00
|
|
|
'sub': sub,
|
2015-07-27 18:50:02 +00:00
|
|
|
'aud': str(aud),
|
2015-01-08 20:55:24 +00:00
|
|
|
'exp': exp_time,
|
|
|
|
'iat': iat_time,
|
2015-04-29 21:55:48 +00:00
|
|
|
'auth_time': auth_time,
|
2015-01-08 20:55:24 +00:00
|
|
|
}
|
|
|
|
|
2015-07-10 12:44:26 +00:00
|
|
|
if nonce:
|
2015-07-27 18:50:02 +00:00
|
|
|
dic['nonce'] = str(nonce)
|
2015-07-10 12:44:26 +00:00
|
|
|
|
2016-08-05 19:11:01 +00:00
|
|
|
if at_hash:
|
|
|
|
dic['at_hash'] = at_hash
|
|
|
|
|
2018-04-10 21:41:38 +00:00
|
|
|
# Inlude (or not) user standard claims in the id_token.
|
|
|
|
if settings.get('OIDC_IDTOKEN_INCLUDE_CLAIMS'):
|
|
|
|
if settings.get('OIDC_EXTRA_SCOPE_CLAIMS'):
|
|
|
|
custom_claims = settings.get('OIDC_EXTRA_SCOPE_CLAIMS', import_str=True)(token)
|
2018-09-29 16:27:07 +00:00
|
|
|
claims = custom_claims.create_response_dic()
|
|
|
|
else:
|
|
|
|
claims = StandardScopeClaims(token).create_response_dic()
|
|
|
|
dic.update(claims)
|
2016-09-09 14:43:28 +00:00
|
|
|
|
2018-05-23 21:16:26 +00:00
|
|
|
dic = run_processing_hook(
|
|
|
|
dic, 'OIDC_IDTOKEN_PROCESSING_HOOK',
|
2018-05-31 07:23:58 +00:00
|
|
|
user=user, token=token, request=request)
|
2016-02-12 16:02:35 +00:00
|
|
|
|
2015-01-08 20:55:24 +00:00
|
|
|
return dic
|
|
|
|
|
2017-08-08 22:41:42 +00:00
|
|
|
|
2016-03-22 19:17:56 +00:00
|
|
|
def encode_id_token(payload, client):
|
2015-01-28 18:19:36 +00:00
|
|
|
"""
|
2015-01-08 20:55:24 +00:00
|
|
|
Represent the ID Token as a JSON Web Token (JWT).
|
|
|
|
Return a hash.
|
2015-01-28 18:19:36 +00:00
|
|
|
"""
|
2016-10-31 20:07:06 +00:00
|
|
|
keys = get_client_alg_keys(client)
|
|
|
|
_jws = JWS(payload, alg=client.jwt_alg)
|
2015-09-30 15:31:49 +00:00
|
|
|
return _jws.sign_compact(keys)
|
2015-01-08 20:55:24 +00:00
|
|
|
|
2017-08-08 22:41:42 +00:00
|
|
|
|
2016-10-31 20:07:06 +00:00
|
|
|
def decode_id_token(token, client):
|
|
|
|
"""
|
|
|
|
Represent the ID Token as a JSON Web Token (JWT).
|
|
|
|
Return a hash.
|
|
|
|
"""
|
|
|
|
keys = get_client_alg_keys(client)
|
|
|
|
return JWS().verify_compact(token, keys=keys)
|
|
|
|
|
2017-08-08 22:41:42 +00:00
|
|
|
|
2016-10-31 20:07:06 +00:00
|
|
|
def client_id_from_id_token(id_token):
|
|
|
|
"""
|
|
|
|
Extracts the client id from a JSON Web Token (JWT).
|
|
|
|
Returns a string or None.
|
|
|
|
"""
|
|
|
|
payload = JWT().unpack(id_token).payload()
|
2017-11-09 11:05:20 +00:00
|
|
|
aud = payload.get('aud', None)
|
|
|
|
if aud is None:
|
|
|
|
return None
|
|
|
|
if isinstance(aud, list):
|
|
|
|
return aud[0]
|
|
|
|
return aud
|
2015-01-28 18:19:36 +00:00
|
|
|
|
|
|
|
|
2016-08-05 19:11:01 +00:00
|
|
|
def create_token(user, client, scope, id_token_dic=None):
|
2015-01-28 18:19:36 +00:00
|
|
|
"""
|
2015-01-08 20:55:24 +00:00
|
|
|
Create and populate a Token object.
|
|
|
|
Return a Token object.
|
2015-01-28 18:19:36 +00:00
|
|
|
"""
|
2015-01-08 20:55:24 +00:00
|
|
|
token = Token()
|
|
|
|
token.user = user
|
|
|
|
token.client = client
|
|
|
|
token.access_token = uuid.uuid4().hex
|
|
|
|
|
2016-08-05 19:11:01 +00:00
|
|
|
if id_token_dic is not None:
|
|
|
|
token.id_token = id_token_dic
|
2015-01-08 20:55:24 +00:00
|
|
|
|
|
|
|
token.refresh_token = uuid.uuid4().hex
|
2015-01-28 20:00:04 +00:00
|
|
|
token.expires_at = timezone.now() + timedelta(
|
2015-02-26 19:14:36 +00:00
|
|
|
seconds=settings.get('OIDC_TOKEN_EXPIRE'))
|
2015-01-08 20:55:24 +00:00
|
|
|
token.scope = scope
|
|
|
|
|
2015-03-12 15:40:36 +00:00
|
|
|
return token
|
|
|
|
|
2017-08-08 22:41:42 +00:00
|
|
|
|
2016-04-06 21:03:30 +00:00
|
|
|
def create_code(user, client, scope, nonce, is_authentication,
|
|
|
|
code_challenge=None, code_challenge_method=None):
|
2015-03-12 15:40:36 +00:00
|
|
|
"""
|
|
|
|
Create and populate a Code object.
|
|
|
|
Return a Code object.
|
|
|
|
"""
|
|
|
|
code = Code()
|
|
|
|
code.user = user
|
|
|
|
code.client = client
|
2016-04-06 21:03:30 +00:00
|
|
|
|
2015-03-12 15:40:36 +00:00
|
|
|
code.code = uuid.uuid4().hex
|
2016-08-05 19:11:01 +00:00
|
|
|
|
2016-04-07 19:18:47 +00:00
|
|
|
if code_challenge and code_challenge_method:
|
|
|
|
code.code_challenge = code_challenge
|
|
|
|
code.code_challenge_method = code_challenge_method
|
2016-04-06 21:03:30 +00:00
|
|
|
|
2015-03-12 15:40:36 +00:00
|
|
|
code.expires_at = timezone.now() + timedelta(
|
|
|
|
seconds=settings.get('OIDC_CODE_EXPIRE'))
|
|
|
|
code.scope = scope
|
2015-07-15 19:23:36 +00:00
|
|
|
code.nonce = nonce
|
2016-02-16 20:33:12 +00:00
|
|
|
code.is_authentication = is_authentication
|
2015-03-12 15:40:36 +00:00
|
|
|
|
2015-07-10 12:44:26 +00:00
|
|
|
return code
|
2016-10-31 20:07:06 +00:00
|
|
|
|
2017-08-08 22:41:42 +00:00
|
|
|
|
2016-10-31 20:07:06 +00:00
|
|
|
def get_client_alg_keys(client):
|
|
|
|
"""
|
|
|
|
Takes a client and returns the set of keys associated with it.
|
|
|
|
Returns a list of keys.
|
|
|
|
"""
|
|
|
|
if client.jwt_alg == 'RS256':
|
|
|
|
keys = []
|
|
|
|
for rsakey in RSAKey.objects.all():
|
|
|
|
keys.append(jwk_RSAKey(key=importKey(rsakey.key), kid=rsakey.kid))
|
|
|
|
if not keys:
|
|
|
|
raise Exception('You must add at least one RSA Key.')
|
|
|
|
elif client.jwt_alg == 'HS256':
|
|
|
|
keys = [SYMKey(key=client.client_secret, alg=client.jwt_alg)]
|
|
|
|
else:
|
|
|
|
raise Exception('Unsupported key algorithm.')
|
|
|
|
|
|
|
|
return keys
|