def retrieve_jwks(jwks_url, httpFactory=Http): """ Retrieves the potential keys used to sign issued tokens from the OP. """ http = httpFactory() _, content = http.request(jwks_url) return JWKSet.from_json(content)
def jwkset(self): from authentic2.crypto import base64url_encode if self.idtoken_algo == self.ALGO_RSA: if self.jwkset_json: return JWKSet.from_json(json.dumps(self.jwkset_json)) if self.idtoken_algo == self.ALGO_HMAC: return JWK(kty='oct', k=base64url_encode(self.client_secret.encode('utf-8'))) return None
def _fetch_certs(): """ Fetches and caches certs :return: Google's OAuth2 server certs """ global CACHED_CERTS if not CACHED_CERTS: resp = requests.get(JWKS_URI_ENDPOINT) key_set = JWKSet.from_json(resp.content) CACHED_CERTS = [k.export_to_pem() for k in key_set] return CACHED_CERTS
def get_jwkset(): try: jwkset = json.dumps(app_settings.JWKSET) except Exception as e: raise ImproperlyConfigured('invalid setting A2_IDP_OIDC_JWKSET: %s' % e) try: jwkset = JWKSet.from_json(jwkset) except InvalidJWKValue as e: raise ImproperlyConfigured('invalid setting A2_IDP_OIDC_JWKSET: %s' % e) if len(jwkset['keys']) < 1: raise ImproperlyConfigured('empty A2_IDP_OIDC_JWKSET') return jwkset
def get_keys(self): if self.jwks: return JWKSet.from_json(self.jwks) return JWKSet()
def test_role_control_access(login_first, oidc_settings, oidc_client, simple_user, app): # authorized_role role_authorized = get_role_model().objects.create( name='Goth Kids', slug='goth-kids', ou=get_default_ou()) oidc_client.add_authorized_role(role_authorized) redirect_uri = oidc_client.redirect_uris.split()[0] params = { 'client_id': oidc_client.client_id, 'scope': 'openid profile email', 'redirect_uri': redirect_uri, 'state': 'xxx', 'nonce': 'yyy', } if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: params['response_type'] = 'code' elif oidc_client.authorization_flow == oidc_client.FLOW_IMPLICIT: params['response_type'] = 'token id_token' authorize_url = make_url('oidc-authorize', params=params) if login_first: utils.login(app, simple_user) # user not authorized response = app.get(authorize_url) assert 'https://example.com/southpark/' in response.content # user authorized simple_user.roles.add(role_authorized) simple_user.save() response = app.get(authorize_url) if not login_first: response = response.follow() response.form.set('username', simple_user.username) response.form.set('password', simple_user.username) response = response.form.submit(name='login-password-submit') response = response.follow() if oidc_client.authorization_mode != oidc_client.AUTHORIZATION_MODE_NONE: response = response.form.submit('accept') assert OIDCAuthorization.objects.get() if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: code = OIDCCode.objects.get() location = urlparse.urlparse(response['Location']) if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: query = urlparse.parse_qs(location.query) code = query['code'][0] token_url = make_url('oidc-token') response = app.post(token_url, params={ 'grant_type': 'authorization_code', 'code': code, 'redirect_uri': oidc_client.redirect_uris.split()[0], }, headers=client_authentication_headers(oidc_client)) id_token = response.json['id_token'] elif oidc_client.authorization_flow == oidc_client.FLOW_IMPLICIT: query = urlparse.parse_qs(location.fragment) id_token = query['id_token'][0] if oidc_client.idtoken_algo == oidc_client.ALGO_RSA: key = JWKSet.from_json(app.get(reverse('oidc-certs')).content) elif oidc_client.idtoken_algo == oidc_client.ALGO_HMAC: key = JWK(kty='oct', k=base64.b64encode(oidc_client.client_secret.encode('utf-8'))) else: raise NotImplementedError jwt = JWT(jwt=id_token, key=key) claims = json.loads(jwt.claims) if login_first: assert claims['acr'] == '0' else: assert claims['acr'] == '1'
def test_authorization_code_sso(login_first, oidc_settings, oidc_client, simple_user, app): redirect_uri = oidc_client.redirect_uris.split()[0] params = { 'client_id': oidc_client.client_id, 'scope': 'openid profile email', 'redirect_uri': redirect_uri, 'state': 'xxx', 'nonce': 'yyy', } if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: params['response_type'] = 'code' elif oidc_client.authorization_flow == oidc_client.FLOW_IMPLICIT: params['response_type'] = 'token id_token' authorize_url = make_url('oidc-authorize', params=params) if login_first: utils.login(app, simple_user) response = app.get(authorize_url) if not login_first: response = response.follow() assert response.request.path == reverse('auth_login') response.form.set('username', simple_user.username) response.form.set('password', simple_user.username) response = response.form.submit(name='login-password-submit') response = response.follow() assert response.request.path == reverse('oidc-authorize') if oidc_client.authorization_mode != OIDCClient.AUTHORIZATION_MODE_NONE: assert 'a2-oidc-authorization-form' in response.content assert OIDCAuthorization.objects.count() == 0 assert OIDCCode.objects.count() == 0 assert OIDCAccessToken.objects.count() == 0 response = response.form.submit('accept') assert OIDCAuthorization.objects.count() == 1 authz = OIDCAuthorization.objects.get() assert authz.client == oidc_client assert authz.user == simple_user assert authz.scope_set() == set('openid profile email'.split()) assert authz.expired >= now() if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: assert OIDCCode.objects.count() == 1 code = OIDCCode.objects.get() assert code.client == oidc_client assert code.user == simple_user assert code.scope_set() == set('openid profile email'.split()) assert code.state == 'xxx' assert code.nonce == 'yyy' assert code.redirect_uri == redirect_uri assert code.session_key == app.session.session_key assert code.auth_time <= now() assert code.expired >= now() assert response['Location'].startswith(redirect_uri) location = urlparse.urlparse(response['Location']) if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: query = urlparse.parse_qs(location.query) assert set(query.keys()) == set(['code', 'state']) assert query['code'] == [code.uuid] code = query['code'][0] assert query['state'] == ['xxx'] token_url = make_url('oidc-token') response = app.post(token_url, params={ 'grant_type': 'authorization_code', 'code': code, 'redirect_uri': oidc_client.redirect_uris.split()[0], }, headers=client_authentication_headers(oidc_client)) assert 'error' not in response.json assert 'access_token' in response.json assert 'expires_in' in response.json assert 'id_token' in response.json assert response.json['token_type'] == 'Bearer' access_token = response.json['access_token'] id_token = response.json['id_token'] elif oidc_client.authorization_flow == oidc_client.FLOW_IMPLICIT: assert location.fragment query = urlparse.parse_qs(location.fragment) assert OIDCAccessToken.objects.count() == 1 access_token = OIDCAccessToken.objects.get() assert set(query.keys()) == set(['access_token', 'token_type', 'expires_in', 'id_token', 'state']) assert query['access_token'] == [access_token.uuid] assert query['token_type'] == ['Bearer'] assert query['state'] == ['xxx'] access_token = query['access_token'][0] id_token = query['id_token'][0] if oidc_client.idtoken_algo == oidc_client.ALGO_RSA: key = JWKSet.from_json(app.get(reverse('oidc-certs')).content) elif oidc_client.idtoken_algo == oidc_client.ALGO_HMAC: key = JWK(kty='oct', k=base64.b64encode(oidc_client.client_secret.encode('utf-8'))) else: raise NotImplementedError jwt = JWT(jwt=id_token, key=key) claims = json.loads(jwt.claims) assert set(claims) >= set(['iss', 'sub', 'aud', 'exp', 'iat', 'nonce', 'auth_time', 'acr']) assert claims['nonce'] == 'yyy' assert response.request.url.startswith(claims['iss']) assert claims['aud'] == oidc_client.client_id assert parse_timestamp(claims['iat']) <= now() assert parse_timestamp(claims['auth_time']) <= now() exp_delta = (parse_timestamp(claims['exp']) - now()).total_seconds() assert exp_delta > 0 if oidc_client.idtoken_duration: assert abs(exp_delta - oidc_client.idtoken_duration.total_seconds()) < 2 else: assert abs(exp_delta - 30) < 2 if login_first: assert claims['acr'] == '0' else: assert claims['acr'] == '1' assert claims['sub'] == make_sub(oidc_client, simple_user) assert claims['preferred_username'] == simple_user.username assert claims['given_name'] == simple_user.first_name assert claims['family_name'] == simple_user.last_name assert claims['email'] == simple_user.email assert claims['email_verified'] is False user_info_url = make_url('oidc-user-info') response = app.get(user_info_url, headers=bearer_authentication_headers(access_token)) assert response.json['sub'] == make_sub(oidc_client, simple_user) assert response.json['preferred_username'] == simple_user.username assert response.json['given_name'] == simple_user.first_name assert response.json['family_name'] == simple_user.last_name assert response.json['email'] == simple_user.email assert response.json['email_verified'] is False # when adding extra attributes OIDCClaim.objects.create(client=oidc_client, name='ou', value='django_user_ou_name', scopes='profile') OIDCClaim.objects.create(client=oidc_client, name='roles', value='a2_role_names', scopes='profile, role') simple_user.roles.add(get_role_model().objects.create( name='Whatever', slug='whatever', ou=get_default_ou())) response = app.get(user_info_url, headers=bearer_authentication_headers(access_token)) assert response.json['ou'] == simple_user.ou.name assert response.json['roles'][0] == 'Whatever' # check against a user without username simple_user.username = None simple_user.save() response = app.get(user_info_url, headers=bearer_authentication_headers(access_token)) assert 'preferred_username' not in response.json # Now logout if oidc_client.post_logout_redirect_uris: params = { 'post_logout_redirect_uri': oidc_client.post_logout_redirect_uris, 'state': 'xyz', } logout_url = make_url('oidc-logout', params=params) response = app.get(logout_url) assert 'You have been logged out' in response.content assert 'https://example.com/?state=xyz' in response.content assert '_auth_user_id' not in app.session else: response = app.get(make_url('account_management')) response = response.click('Logout') if oidc_client.frontchannel_logout_uri: iframes = response.pyquery('iframe[src="https://example.com/southpark/logout/"]') assert iframes if oidc_client.frontchannel_timeout: assert iframes.attr('onload').endswith(', %d)' % oidc_client.frontchannel_timeout) else: assert iframes.attr('onload').endswith(', 10000)')
def validate_jwkset(data): data = json.dumps(data) try: JWKSet.from_json(data) except InvalidJWKValue as e: raise ValidationError(_('Invalid JWKSet: %s') % e)
def get_key(kid: str, jwks_json: str) -> Tuple[str, str]: jwk = None if jwks_json: jwk = JWKSet.from_json(jwks_json).get_key(kid).export_public() return jwk, jwks_json