def inject_provider(request): clients = { TEST_CLIENT_ID: { 'subject_type': 'pairwise', 'redirect_uris': [TEST_REDIRECT_URI], 'response_types': ['code'], 'client_secret': TEST_CLIENT_SECRET, 'token_endpoint_auth_method': 'client_secret_post', 'post_logout_redirect_uris': ['https://client.example.com/post_logout'] } } userinfo = Userinfo({ TEST_USER_ID: { 'name': 'The T. Tester', 'family_name': 'Tester', 'given_name': 'The', 'middle_name': 'Theodore', 'nickname': 'testster', 'email': '*****@*****.**', } }) request.instance.provider = Provider( rsa_key(), {'issuer': ISSUER}, AuthorizationState(HashBasedSubjectIdentifierFactory('salt')), clients, userinfo)
def test_rejects_mismatching_request(self, client_preference, provider_capability, client_value, provider_value): request = {'redirect_uris': ['https://client.example.com/redirect']} provider_capabilities = provide_configuration() if client_preference.startswith( ('request_object_encryption', 'id_token_encrypted', 'userinfo_encrypted')): # provide default value for the metadata params that come in pairs param = client_preference[:-4] alg_param = param + '_alg' request[alg_param] = 'RSA-OAEP' provider_capabilities[PREFERENCE2PROVIDER[alg_param]] = [ 'RSA-OAEP' ] enc_param = param + '_enc' request[enc_param] = 'A192CBC-HS256' provider_capabilities[PREFERENCE2PROVIDER[enc_param]] = [ 'A192CBC-HS256' ] request[client_preference] = client_value provider_capabilities[provider_capability] = provider_value provider = Provider( rsa_key(), provider_capabilities, AuthorizationState(HashBasedSubjectIdentifierFactory('salt')), {}, None) with pytest.raises(InvalidClientRegistrationRequest): provider.handle_client_registration_request(json.dumps(request))
def init_oidc_provider(app): with app.app_context(): issuer = url_for('oidc_provider.index')[:-1] authentication_endpoint = url_for('oidc_provider.authentication_endpoint') jwks_uri = url_for('oidc_provider.jwks_uri') token_endpoint = url_for('oidc_provider.token_endpoint') userinfo_endpoint = url_for('oidc_provider.userinfo_endpoint') registration_endpoint = url_for('oidc_provider.registration_endpoint') end_session_endpoint = url_for('oidc_provider.end_session_endpoint') userinfo_ldap = LdapUserInfo() configuration_information = { 'issuer': issuer, 'authorization_endpoint': authentication_endpoint, 'jwks_uri': jwks_uri, 'token_endpoint': token_endpoint, 'userinfo_endpoint': userinfo_endpoint, 'registration_endpoint': registration_endpoint, 'end_session_endpoint': end_session_endpoint, 'scopes_supported': ['openid', 'profile'], 'response_types_supported': ['code', 'code id_token', 'code token', 'code id_token token'], # code and hybrid 'response_modes_supported': ['query', 'fragment'], 'grant_types_supported': ['authorization_code', 'implicit'], 'subject_types_supported': ['pairwise'], 'token_endpoint_auth_methods_supported': ['client_secret_basic'], 'claims_parameter_supported': True } signing_key = RSAKey(key=rsa_load('signing_key.pem'), alg='RS256') provider = Provider(signing_key, configuration_information, AuthorizationState(HashBasedSubjectIdentifierFactory(app.config['SUBJECT_ID_HASH_SALT'])), {}, userinfo_ldap) return provider
def init_oidc_provider(app): with app.app_context(): issuer = url_for("oidc_provider.index")[:-1] authentication_endpoint = url_for("oidc_provider.authentication_endpoint") jwks_uri = url_for("oidc_provider.jwks_uri") token_endpoint = url_for("oidc_provider.token_endpoint") userinfo_endpoint = url_for("oidc_provider.userinfo_endpoint") registration_endpoint = url_for("oidc_provider.registration_endpoint") end_session_endpoint = url_for("oidc_provider.end_session_endpoint") configuration_information = { "issuer": issuer, "authorization_endpoint": authentication_endpoint, "jwks_uri": jwks_uri, "token_endpoint": token_endpoint, "userinfo_endpoint": userinfo_endpoint, "registration_endpoint": registration_endpoint, "end_session_endpoint": end_session_endpoint, "scopes_supported": ["openid", "profile"], "response_types_supported": [ "code", "code id_token", "code token", "code id_token token", ], # code and hybrid "response_modes_supported": ["query", "fragment"], "grant_types_supported": ["authorization_code", "implicit"], "subject_types_supported": ["pairwise"], "token_endpoint_auth_methods_supported": [ "client_secret_post", "client_secret_basic", ], "claims_parameter_supported": True, } clients = { "sample": { "client_secret": "sample", "redirect_uris": ["http://localhost:5000/test_auth_callback",], "response_types": ["code"], } } userinfo_db = Userinfo(app.users) signing_key = RSAKey(key=rsa_load("signing_key.pem"), alg="RS256") provider = Provider( signing_key, configuration_information, AuthorizationState( HashBasedSubjectIdentifierFactory(app.config["SUBJECT_ID_HASH_SALT"]) ), clients, userinfo_db, ) return provider
def test_code_exchange_request_without_offline_scope(self): self.provider.authz_state = AuthorizationState( HashBasedSubjectIdentifierFactory('salt'), refresh_token_lifetime=10, refresh_token_threshold=2) self.authorization_code_exchange_request_args[ 'code'] = self.create_authz_code() response = self.provider._do_code_exchange( self.authorization_code_exchange_request_args, None) assert 'refresh_token' not in response.keys()
def test_refresh_request(self): self.provider.authz_state = AuthorizationState( HashBasedSubjectIdentifierFactory('salt'), refresh_token_lifetime=600) self.refresh_token_request_args[ 'refresh_token'] = self.create_refresh_token() response = self.provider.handle_token_request( urlencode(self.refresh_token_request_args)) assert response[ 'access_token'] in self.provider.authz_state.access_tokens assert 'refresh_token' not in response
def test_code_exchange_request_with_offline_scope_has_refresh_token(self): self.provider.authz_state = AuthorizationState( HashBasedSubjectIdentifierFactory('salt'), refresh_token_lifetime=10, refresh_token_threshold=2) scope_req = {'scope': 'openid offline_access'} self.authorization_code_exchange_request_args[ 'code'] = self.create_authz_code(extra_auth_req_params=scope_req) response = self.provider._do_code_exchange( self.authorization_code_exchange_request_args, None) assert 'refresh_token' in response.keys() and response[ 'refresh_token'] in self.provider.authz_state.refresh_tokens
def test_refresh_request_without_scope_parameter_defaults_to_scope_from_authentication_request( self): self.provider.authz_state = AuthorizationState( HashBasedSubjectIdentifierFactory('salt'), refresh_token_lifetime=600) self.refresh_token_request_args[ 'refresh_token'] = self.create_refresh_token() del self.refresh_token_request_args['scope'] response = self.provider.handle_token_request( urlencode(self.refresh_token_request_args)) assert response[ 'access_token'] in self.provider.authz_state.access_tokens assert self.provider.authz_state.access_tokens[response[ 'access_token']]['scope'] == self.authn_request_args['scope']
def _init_authorization_state(provider_config, db_uri, sub_hash_salt): if db_uri: authz_code_db = StorageBase.from_uri( db_uri, db_name="satosa", collection="authz_codes", ttl=provider_config.get("authorization_code_lifetime", 600), ) access_token_db = StorageBase.from_uri( db_uri, db_name="satosa", collection="access_tokens", ttl=provider_config.get("access_token_lifetime", 3600), ) refresh_token_db = StorageBase.from_uri( db_uri, db_name="satosa", collection="refresh_tokens", ttl=provider_config.get("refresh_token_lifetime", None), ) sub_db = StorageBase.from_uri(db_uri, db_name="satosa", collection="subject_identifiers", ttl=None) else: authz_code_db = None access_token_db = None refresh_token_db = None sub_db = None token_lifetimes = { k: provider_config[k] for k in [ "authorization_code_lifetime", "access_token_lifetime", "refresh_token_lifetime", "refresh_token_threshold", ] if k in provider_config } return AuthorizationState( HashBasedSubjectIdentifierFactory(sub_hash_salt), authz_code_db, access_token_db, refresh_token_db, sub_db, **token_lifetimes, )
def test_refresh_request_with_refresh_token_close_to_expiry_issues_new_refresh_token( self): self.provider.authz_state = AuthorizationState( HashBasedSubjectIdentifierFactory('salt'), refresh_token_lifetime=10, refresh_token_threshold=2) self.refresh_token_request_args[ 'refresh_token'] = self.create_refresh_token() close_to_expiration = int( time.time()) + self.provider.authz_state.refresh_token_lifetime - 1 with patch('time.time', Mock(return_value=close_to_expiration)): response = self.provider.handle_token_request( urlencode(self.refresh_token_request_args)) assert response[ 'access_token'] in self.provider.authz_state.access_tokens assert response[ 'refresh_token'] in self.provider.authz_state.refresh_tokens
def test_matches_common_set_of_metadata_values(self, client_preference, provider_capability, client_value, provider_value): provider_capabilities = provide_configuration() provider_capabilities.update({provider_capability: provider_value}) provider = Provider( rsa_key(), provider_capabilities, AuthorizationState(HashBasedSubjectIdentifierFactory('salt')), {}, None) request = { 'redirect_uris': ['https://client.example.com/redirect'], client_preference: client_value } response = provider.handle_client_registration_request( json.dumps(request)) expected_values = set(client_value).intersection(provider_value) assert Counter(frozenset(v.split()) for v in response[client_preference]) == \ Counter(frozenset(v.split()) for v in expected_values)
def plugin_enable(self) -> None: # pylint: disable=attribute-defined-outside-init self.signing_key = RSAKey(key=rsa_load("signing_key.pem"), use="sig", alg="RS256") # pylint: disable=attribute-defined-outside-init self.oidc_configuration_information = { "issuer": self.app.config["URL_BASE"].replace("http://", "https://"), "authorization_endpoint": self.app.config["URL_BASE"] + "/oidc/authorization", "jwks_uri": self.app.config["URL_BASE"] + "/oidc/jwks", "token_endpoint": self.app.config["URL_BASE"] + "/oidc/token", "userinfo_endpoint": self.app.config["URL_BASE"] + "/oidc/userinfo", "response_types_supported": ["code"], "id_token_signing_alg_values_supported": [self.signing_key.alg], "response_modes_supported": ["fragment", "query"], "subject_types_supported": ["public", "pairwise"], "grant_types_supported": ["authorization_code", "implicit"], "claim_types_supported": ["normal"], "claims_parameter_supported": True, "claims_supported": ["sub", "name", "given_name", "family_name", "email", "profile"], "scopes_supported": ["openid", "email", "profile"] } subject_id_factory = HashBasedSubjectIdentifierFactory( self.app.config["SECRET_KEY"]) # pylint: disable=attribute-defined-outside-init self.provider = Provider( self.signing_key, self.oidc_configuration_information, AuthorizationState( subject_id_factory, SQLWrapper(self.storage, 'authz_codes'), SQLWrapper(self.storage, 'access_tokens'), SQLWrapper(self.storage, 'refresh_tokens'), SQLWrapper(self.storage, 'subject_identifiers', True), ), SQLWrapper(self.storage, 'clients'), Userinfo(PersonWrapper(self.storage)))
def _init_authorization_state(self): sub_hash_salt = self.config.get("sub_hash_salt", rndstr(16)) db_uri = self.config.get("db_uri") if db_uri: authz_code_db = MongoWrapper(db_uri, "satosa", "authz_codes") access_token_db = MongoWrapper(db_uri, "satosa", "access_tokens") refresh_token_db = MongoWrapper(db_uri, "satosa", "refresh_tokens") sub_db = MongoWrapper(db_uri, "satosa", "subject_identifiers") else: authz_code_db = None access_token_db = None refresh_token_db = None sub_db = None token_lifetimes = {k: self.config["provider"][k] for k in ["authorization_code_lifetime", "access_token_lifetime", "refresh_token_lifetime", "refresh_token_threshold"] if k in self.config["provider"]} return AuthorizationState(HashBasedSubjectIdentifierFactory(sub_hash_salt), authz_code_db, access_token_db, refresh_token_db, sub_db, **token_lifetimes)
def authorization_state_factory(self): return functools.partial(AuthorizationState, HashBasedSubjectIdentifierFactory('salt'))
def init_oidc_provider(app): with app.app_context(): config = { 'issuer': app.config['OIDC_PROVIDER']['issuer'], 'authorization_endpoint': url_for('oidc_provider.auth'), 'jwks_uri': url_for('oidc_provider.jwks'), 'token_endpoint': url_for('oidc_provider.token'), 'userinfo_endpoint': url_for('oidc_provider.userinfo'), 'registration_endpoint': url_for('oidc_provider.register'), 'end_session_endpoint': url_for('oidc_provider.logout'), 'scopes_supported': ['openid', 'profile'], 'response_types_supported': ['code', 'code id_token', 'code token', 'code id_token token'], 'response_modes_supported': ['query', 'fragment'], 'grant_types_supported': ['authorization_code', 'implicit'], 'subject_types_supported': ['pairwise'], 'token_endpoint_auth_methods_supported': ['client_secret_basic'], 'claims_parameter_supported': True } userinfo_db = Userinfo() signing_key = RSAKey(key=rsa_load('signing_key.pem'), alg='RS256') authz_state = AuthorizationState( HashBasedSubjectIdentifierFactory( app.config['OIDC_PROVIDER']['subject_id_hash_salt'])) clients = { 'notify-test': { 'client_name': 'GOV.UK Notify', 'client_secret': 'notify-secret', 'client_uri': 'https://admin-ags.notify.works/', 'post_logout_redirect_uris': [ 'https://admin-ags.notify.works/sign-out', 'http://localhost:6012/sign-out', ], 'redirect_uris': [ 'https://admin-ags.notify.works/oidc_callback', 'http://localhost:6012/oidc_callback', ], 'response_types': [ 'code', ], }, 'test-client': { 'client_name': 'Test', 'client_secret': 'test-secret', 'post_logout_redirect_uris': [ 'http://example.com/sign-out', 'https://sue-my-brother.cloudapps.digital/sign-out', ], 'redirect_uris': [ 'http://example.com/oidc_callback', 'https://sue-my-brother.cloudapps.digital/oidc_callback', ], 'response_types': [ 'code', ], }, } provider = Provider(signing_key, config, authz_state, clients, userinfo_db) return provider
def init_oidc_provider(app): with app.app_context(): issuer = url_for("oidc_provider.index")[:-1] authentication_endpoint = url_for( "oidc_provider.authentication_endpoint") jwks_uri = url_for("oidc_provider.jwks_uri") token_endpoint = url_for("oidc_provider.token_endpoint") userinfo_endpoint = url_for("oidc_provider.userinfo_endpoint") registration_endpoint = url_for("oidc_provider.registration_endpoint") end_session_endpoint = url_for("oidc_provider.end_session_endpoint") configuration_information = { "issuer": issuer, "authorization_endpoint": authentication_endpoint, "jwks_uri": jwks_uri, "token_endpoint": token_endpoint, "userinfo_endpoint": userinfo_endpoint, "registration_endpoint": registration_endpoint, "end_session_endpoint": end_session_endpoint, "scopes_supported": app.config["OIDC_SCOPES_SUPPORTED"], "response_types_supported": app.config["OIDC_RESPONSE_TYPES_SUPPORTED"], "response_modes_supported": app.config["OIDC_RESPONSE_MODES_SUPPORTED"], "grant_types_supported": app.config["OIDC_GRANT_TYPES_SUPPORTED"], "subject_types_supported": app.config["OIDC_SUBJECT_TYPE_SUPPORTED"], "token_endpoint_auth_methods_supported": app.config["OIDC_TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED"], "userinfo_signing_alg_values_supported": app.config["OIDC_USERINFO_SIGNING_ALG_VALUES_SUPPORTED"], "claims_parameter_supported": app.config["OIDC_CLAIMS_PARAMETER_SUPPORTED"], "claims_supported": app.config["OIDC_CLAIMS_SUPPORTED"], } userinfo_db = Userinfo(app.sql_backend) signing_key = RSAKey(key=rsa_load(app.config["SIGNING_KEY_FILE"]), alg=app.config["SIGNING_KEY_ALG"], kid=app.config["SIGNING_KEY_ID"]) provider = Provider( signing_key, configuration_information, AuthorizationState( HashBasedSubjectIdentifierFactory( app.config["SUBJECT_ID_HASH_SALT"])), ClientRPSQLWrapper(), userinfo_db, ) return provider