ea340993b1
The token endpoint handled the scope parameter incorrectly for all of the three handled grant types: 1. For "authorization_code" grant type the scope parameter in the token request should not be respected but the scope should be taken from the authorization code. It was not totally ignored, but rather the scope parameter of the token request was used for the generated ID token. This had two consequences: * Spec conforming implementations of authorization code flow didn't get correct ID tokens, since they usually don't pass scope parameter with the token request. * It's possible to get a broader scope for the ID token than what is authorized by the user in the original authorization code request. 2. For "refresh_token" grant type the scope parameter in the token request should only allow narrowing down the scope. It wasn't narrowed, but rather the original auth code scope was used for the access token and the passed in scope parameter was used for the ID token (again allowing unauthorized scopes in the ID token). 3. For "password" grant type the scope parameter in the token request should be respected. The problem with this was that it wasn't properly splitted when passed to ID token creation. Fixes #186
125 lines
3.3 KiB
Python
125 lines
3.3 KiB
Python
import random
|
|
import string
|
|
try:
|
|
from urlparse import parse_qs, urlsplit
|
|
except ImportError:
|
|
from urllib.parse import parse_qs, urlsplit
|
|
|
|
from django.utils import timezone
|
|
from django.contrib.auth.models import User
|
|
|
|
from oidc_provider.models import (
|
|
Client,
|
|
Code,
|
|
Token)
|
|
|
|
|
|
FAKE_NONCE = 'cb584e44c43ed6bd0bc2d9c7e242837d'
|
|
FAKE_RANDOM_STRING = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(32))
|
|
FAKE_CODE_CHALLENGE = 'YlYXEqXuRm-Xgi2BOUiK50JW1KsGTX6F1TDnZSC8VTg'
|
|
FAKE_CODE_VERIFIER = 'SmxGa0XueyNh5bDgTcSrqzAh2_FmXEqU8kDT6CuXicw'
|
|
|
|
|
|
def create_fake_user():
|
|
"""
|
|
Create a test user.
|
|
|
|
Return a User object.
|
|
"""
|
|
user = User()
|
|
user.username = 'johndoe'
|
|
user.email = 'johndoe@example.com'
|
|
user.first_name = 'John'
|
|
user.last_name = 'Doe'
|
|
user.set_password('1234')
|
|
|
|
user.save()
|
|
|
|
return user
|
|
|
|
|
|
def create_fake_client(response_type, is_public=False, require_consent=True):
|
|
"""
|
|
Create a test client, response_type argument MUST be:
|
|
'code', 'id_token' or 'id_token token'.
|
|
|
|
Return a Client object.
|
|
"""
|
|
client = Client()
|
|
client.name = 'Some Client'
|
|
client.client_id = str(random.randint(1, 999999)).zfill(6)
|
|
if is_public:
|
|
client.client_type = 'public'
|
|
client.client_secret = ''
|
|
else:
|
|
client.client_secret = str(random.randint(1, 999999)).zfill(6)
|
|
client.response_type = response_type
|
|
client.redirect_uris = ['http://example.com/']
|
|
client.require_consent = require_consent
|
|
|
|
client.save()
|
|
|
|
return client
|
|
|
|
|
|
def create_fake_token(user, scopes, client):
|
|
expires_at = timezone.now() + timezone.timedelta(seconds=60)
|
|
token = Token(user=user, client=client, expires_at=expires_at)
|
|
token.scope = scopes
|
|
|
|
token.save()
|
|
|
|
return token
|
|
|
|
|
|
def is_code_valid(url, user, client):
|
|
"""
|
|
Check if the code inside the url is valid. Supporting both query string and fragment.
|
|
"""
|
|
try:
|
|
parsed = urlsplit(url)
|
|
params = parse_qs(parsed.query or parsed.fragment)
|
|
code = params['code'][0]
|
|
code = Code.objects.get(code=code)
|
|
is_code_ok = (code.client == client) and (code.user == user)
|
|
except:
|
|
is_code_ok = False
|
|
|
|
return is_code_ok
|
|
|
|
|
|
def userinfo(claims, user):
|
|
"""
|
|
Fake function for setting OIDC_USERINFO.
|
|
"""
|
|
claims['given_name'] = 'John'
|
|
claims['family_name'] = 'Doe'
|
|
claims['name'] = '{0} {1}'.format(claims['given_name'], claims['family_name'])
|
|
claims['email'] = user.email
|
|
claims['address']['country'] = 'Argentina'
|
|
return claims
|
|
|
|
|
|
def fake_sub_generator(user):
|
|
"""
|
|
Fake function for setting OIDC_IDTOKEN_SUB_GENERATOR.
|
|
"""
|
|
return user.email
|
|
|
|
|
|
def fake_idtoken_processing_hook(id_token, user):
|
|
"""
|
|
Fake function for inserting some keys into token. Testing OIDC_IDTOKEN_PROCESSING_HOOK.
|
|
"""
|
|
id_token['test_idtoken_processing_hook'] = FAKE_RANDOM_STRING
|
|
id_token['test_idtoken_processing_hook_user_email'] = user.email
|
|
return id_token
|
|
|
|
|
|
def fake_idtoken_processing_hook2(id_token, user):
|
|
"""
|
|
Fake function for inserting some keys into token. Testing OIDC_IDTOKEN_PROCESSING_HOOK - tuple or list as param
|
|
"""
|
|
id_token['test_idtoken_processing_hook2'] = FAKE_RANDOM_STRING
|
|
id_token['test_idtoken_processing_hook_user_email2'] = user.email
|
|
return id_token
|