django-oidc-provider/oidc_provider/lib/utils/token.py

168 lines
4.5 KiB
Python
Raw Normal View History

2015-01-08 20:55:24 +00:00
from datetime import timedelta
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
from django.utils import dateformat, timezone
from jwkest.jwk import RSAKey as jwk_RSAKey
2016-03-22 19:17:56 +00:00
from jwkest.jwk import SYMKey
from jwkest.jws import JWS
from jwkest.jwt import JWT
2015-03-02 20:37:54 +00:00
from oidc_provider.lib.utils.common import get_issuer, run_processing_hook
from oidc_provider.lib.claims import StandardScopeClaims
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
def create_id_token(token, user, aud, nonce='', at_hash='', request=None, scope=None):
"""
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.
"""
2017-08-08 22:41:42 +00:00
if scope is None:
scope = []
sub = settings.get('OIDC_IDTOKEN_SUB_GENERATOR', import_str=True)(user=user)
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.
now = int(time.time())
iat_time = now
exp_time = int(now + expires_in)
user_auth_time = user.last_login or user.date_joined
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,
'aud': str(aud),
2015-01-08 20:55:24 +00:00
'exp': exp_time,
'iat': iat_time,
'auth_time': auth_time,
2015-01-08 20:55:24 +00:00
}
if nonce:
dic['nonce'] = str(nonce)
if at_hash:
dic['at_hash'] = at_hash
# 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)
claims = custom_claims.create_response_dic()
else:
claims = StandardScopeClaims(token).create_response_dic()
dic.update(claims)
Merge branch 'develop' of github.com:juanifioren/django-oidc-provider * 'develop' of github.com:juanifioren/django-oidc-provider: Update changelog.rst include request in password grant authenticate call Update setup.py Update changelog.rst Update changelog.rst Adjust import order and method order in introspection tests Replace resource with client in docs. Update settings docs to add extra introspection setting Update README.md Update README.md Remove the Resource model Skip csrf protection on introspection endpoint Add token introspection endpoint to satisfy https://tools.ietf.org/html/rfc7662 Test docs with tox. Remove Django 1.7 for travis. Drop support for Django 1.7. Move extract_client_auth to oauth2 utils. Remove duplicate link in docs. Bump version v0.6.0. Fix BaseCodeTokenModel and user attr. Update README.md Edit README and contribute doc. Edit changelog. Update changelog.rst Add protected_resource_view test using client_credentials. Fix docs. Improve docs. Client credentials implementation. Move changelog into docs. Update README.md Update CHANGELOG.md Fixed infinite callback loop in check-session iframe Fix PEP8. New migration. Update example project. Fix PEP8. Fix PEP8. PEP8 errors and urls. PEP8 models. Fix contribute docs. Fix tox for checking PEP8 all files. Update README.md Update README.md Simplify test suit. Update CHANGELOG.md Bump version 0.5.3. Update installation.rst Update CHANGELOG.md Fixed wrong Object in Template Update project to support Django 2.0 Now passing along the token to create_id_token function. Made token and token_refresh endpoint return requested claims. Sphinx documentation fixes (#219) Use request.user.is_authenticated as a bool with recent Django (#216) Fixed client id retrieval when aud is a list of str. (#210) Add owner field to Client (#211) Update CHANGELOG removed tab char Add pep8 compliance and checker Bump version Update CHANGELOG.md Preparing v0.5.2 (#201) Fix Django 2.0 deprecation warnings (#185) Fix infinite login loop if "prompt=login" (#198) fixed typos Bump version Fix scope handling of token endpoint (#193) Fixes #192 Use stored user consent for public clients too (#189) Redirect URIs must match exactly. (#191) Bug #187 prompt handling (#188) Don't pin exact versions in install_requires.
2018-05-23 21:16:26 +00:00
dic = run_processing_hook(
dic, 'OIDC_IDTOKEN_PROCESSING_HOOK',
user=user, token=token, request=request)
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-08 20:55:24 +00:00
Represent the ID Token as a JSON Web Token (JWT).
Return a hash.
"""
keys = get_client_alg_keys(client)
_jws = JWS(payload, alg=client.jwt_alg)
return _jws.sign_compact(keys)
2015-01-08 20:55:24 +00:00
2017-08-08 22:41:42 +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
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()
aud = payload.get('aud', None)
if aud is None:
return None
if isinstance(aud, list):
return aud[0]
return aud
def create_token(user, client, scope, id_token_dic=None):
"""
2015-01-08 20:55:24 +00:00
Create and populate a Token object.
Return a Token object.
"""
2015-01-08 20:55:24 +00:00
token = Token()
token.user = user
token.client = client
token.access_token = uuid.uuid4().hex
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
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
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):
"""
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
code.code = uuid.uuid4().hex
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
code.expires_at = timezone.now() + timedelta(
seconds=settings.get('OIDC_CODE_EXPIRE'))
code.scope = scope
code.nonce = nonce
2016-02-16 20:33:12 +00:00
code.is_authentication = is_authentication
return code
2017-08-08 22:41:42 +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